ritchie
A complete Riichi Mahjong representation in Gleam. Heavily inspired by summivox’s Rust riichi library. Notable differences include:
- The hand decomposition parser is entirely hand-rolled, it doesn’t use lookup tables (that would be weird and hard with Gleam’s restrictions)
- A separate
gamemodule is provided that tracks state across game rounds - Possible actions/reactions can be listed at any point during the game, which makes it significantly easier to build upon (the logic is already there, after all)
gleam add ritchie
import ritchie/game
import ritchie/elements/tile
import ritchie/elements/wall
import ritchie/model/action
import ritchie/model/discard
pub fn main() -> Nil {
// Initialize a new game with standard rules, starting at 25000 points per player.
let g = game.new_default()
// Start a new round with a given wall (a full sorted wall, in our case)
let assert Ok(g) = game.start_round(g, wall.make_sorted_wall(#(1, 1, 1)))
// Reflect on `game.current_state` (which has the current draw, hands, melds, discards, and so on),
// and then construct an `action` from the player whose turn it is.
let ac = action.ActionDiscard(discard.Discard(
tile: tile.t("3m"), // for example
called_by: 2,
declares_riichi: False,
is_tsumogiri: True,
))
// Register the action with the game. The game checks if the action is valid, and caches it to be
// applied the next time you call `game.step`.
let assert Ok(g) = game.register_action(g, ac)
// Now, you could also construct a reaction (see `ritchie/model/reaction`), or even multiple, and
// apply them in the same way using `game.register_reaction`. When you are done...
// Process the next game step (action + reaction(s)), to produce the next state.
case game.step(g) {
Ok(game.GameStepOutcome(g, step, None)) -> {
// Congrats, the next turn has started!
}
Ok(game.GameStepOutcome(g, step, Some(_))) -> {
// Oh, the round has ended! This could potentially also
// mean that the game is over.
}
}
}
Further documentation can be found at https://ritchie.hexdocs.pm/.
Todo
Major and minor things that need doing:
-
Shuffle wall.
game.start_roundcurrently expects a pre-shuffled wall - More types should be opaque, especially the ones that the player uses. There should instead be constructors for most things
- Player bankruptcy should be handled
- Renhou is not detected
- Probably JSON serialization
-
Properly implement
yaku_extraandyaku_blockin the ruleset
License
Licensed under the LGPL-3.0.