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 bewall[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 walldraw=Some, meld=Some: Kan draw, from the tail of the walldraw=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