ritchie/model/state

State of a round of game. Because of its central importance, this is simply known as “The State”.

Types

Furiten status of a player.

pub type FuritenFlags {
  FuritenFlags(
    by_discard: Bool,
    miss_temporary: Bool,
    miss_permanent: Bool,
  )
}

Constructors

  • FuritenFlags(
      by_discard: Bool,
      miss_temporary: Bool,
      miss_permanent: Bool,
    )

    Arguments

    by_discard

    A tile in the player’s waiting set has been discarded by the player.

    miss_temporary

    Another player discarded a tile in the waiting set and this player did not call ron. Cleared after this player discards.

    miss_permanent

    Same trigger while under riichi. Permanent for the round.

pub type State {
  State(
    core: StateCore,
    melds: List(List(meld.Meld)),
    closed_hands: List(tile_set37.TileSet37),
    discards: List(List(discard.Discard)),
    discard_sets: List(tile_set34.TileSet34),
  )
}

Constructors

  • State(
      core: StateCore,
      melds: List(List(meld.Meld)),
      closed_hands: List(tile_set37.TileSet37),
      discards: List(List(discard.Discard)),
      discard_sets: List(tile_set34.TileSet34),
    )

    Arguments

    core

    Core state variables, cheap to copy and enough to reconstruct the rest when replayed from the round beginning.

    melds

    Melds / open hands of each player.

    closed_hands

    Concealed hands. Does not include the current draw. A player can only observe their own hand! This is not enforced by this library, but should be enforced by anything using it to implement a riichi mahjong game!

    discards

    Discard stream of each player, a.k.a. the “river”. Tiles that are called by other players are explicitly marked (unlike in the physical representation on a real table, where the tile can’t be both in the discard stream and the player’s meld at the same time).

    discard_sets

    Set of discarded tiles for each player, with red fives collapsed. This makes it easier to answer queries of whether a player had discarded a certain tile, no matter how many times and when.

Essential state variables.

Why separate these out? Hands, discards and melds are big lists. If we store all of that, it takes up a lot of space. We can reconstruct all 3 by aggregating actions and “core” variables from the initial full state. This means we can instead only store #(action, next_state_core) for each turn, and “replay”/fold the game to obtain the current state.

pub type StateCore {
  StateCore(
    seq: Int,
    actor: player.Player,
    num_drawn_head: Int,
    num_drawn_tail: Int,
    num_dora_indicators: Int,
    draw: option.Option(tile.Tile),
    incoming_meld: option.Option(meld.Meld),
    furiten: List(FuritenFlags),
    riichi: List(option.Option(riichi.Riichi)),
  )
}

Constructors

  • StateCore(
      seq: Int,
      actor: player.Player,
      num_drawn_head: Int,
      num_drawn_tail: Int,
      num_dora_indicators: Int,
      draw: option.Option(tile.Tile),
      incoming_meld: option.Option(meld.Meld),
      furiten: List(FuritenFlags),
      riichi: List(option.Option(riichi.Riichi)),
    )

    Arguments

    seq

    Total number of (closed!) actions since the beginning of the round.

    actor

    The player in action.

    num_drawn_head

    Number of tiles drawn from the head of the wall, including the initial deal. The draw for a normal turn is at num_drawn_head - 1. For example, before the first action of any round, num_drawn_head == 53, and the tile drawn will be wall[52].

    num_drawn_tail

    Number of tiles drawn from the tail of the wall for kans. Same as the number of completed kans.

    num_dora_indicators

    Number of revealed dora indicators.

    draw

    The tile drawn this turn, if any. The current player will always gain one tile before an action. That means either:

    • draw=Some, meld=None: Normal draw, from the head of the wall
    • draw=Some, meld=Some: Kan draw, from the tail of the wall
    • draw=None, meld=Some: Chii/Pon from another player, combined into the meld list (not the closed hand!)
    incoming_meld

    The meld called during the last action-reaction cycle, if any. This is not mutually exclusive with draw, since an incoming kan means you get both draw and meld.

    furiten

    Furiten status for each player before action.

    riichi

    Riichi status for each player.

Values

pub fn any_furiten(flags: FuritenFlags) -> Bool

Returns whether any furiten penalty is currently active.

pub fn evolve(
  state: State,
  act: action.Action,
  next: StateCore,
) -> State

Applies one action and the next core state to the current state, mutating only the non-core variables. This is pure bookkeeping; it does not validate the action or interpret the rules.

pub fn new(begin: boundary.RoundBegin) -> State

Returns the initial state of a round: 13 tiles dealt to each player, plus the first draw for the dealer.

pub fn no_furiten() -> FuritenFlags
✨ Search Document