Skip to content

Add four-player Riichi Mahjong (single hand) - #1573

Open
jaisinha77777 wants to merge 1 commit into
google-deepmind:masterfrom
jaisinha77777:add-mahjong-riichi
Open

Add four-player Riichi Mahjong (single hand)#1573
jaisinha77777 wants to merge 1 commit into
google-deepmind:masterfrom
jaisinha77777:add-mahjong-riichi

Conversation

@jaisinha77777

Copy link
Copy Markdown

Per #843 (see comment there flagging this before opening the PR, per #1511's guidance for larger contributions).

Adds python_mahjong_riichi: one hand of four-player Japanese Riichi Mahjong, Python-only.

The calling problem. Pon/Chi/Kan/Ron is a real-time interrupt mechanic with no direct mapping onto OpenSpiel's simultaneous-move or turn-based dynamics — there's an old, unresolved thread on exactly this at #979. This implements it as sequential, priority-ordered polling after each discard: every Ron-eligible player is asked first, in turn order (multiple simultaneous ron is supported); only if nobody rons is the single Pon/Kan-eligible player (if any) asked; only if nobody calls that is the single Chi-eligible player (the discarder's next player) asked. This reproduces standard priority (Ron > Pon/Kan > Chi) without requiring true real-time simultaneity.

Scoring (mahjong_riichi_utils.py, kept separate from game flow so it's independently unit-testable): standard hand-shape decomposition (4 sets + pair, chiitoitsu, kokushi), the standard ~25 named yaku, fu calculation (wait type, open/closed triplet-vs-kan distinction, shanpon-on-ron vs shanpon-on-tsumo, double-wind pairs), dora/aka-dora/ura-dora, and the mangan-through-yakuman point/payment tables. The common yakuman are covered (kokushi, suuankou, daisangen, shou/dai-suushii, tsuuiisou, chinroutou, ryuuiisou, suukantsu, chuuren poutou, tenhou/chiihou); a handful of rare local-rule yaku (e.g. renhou) are intentionally not included.

Also implemented: riichi declaration with furiten tracking (both permanent, from a player's own discards, and temporary, from declining a legal ron), chankan (robbing a kan), and the abortive draws (kyuushu kyuuhai, four-kan, four-riichi, four-wind-discard).

Scope: one hand per episode (deal through win or exhaustive draw), not a full multi-hand match with dealer rotation and score carryover across hands — same pattern as e.g. leduc_poker modeling one hand rather than a session. returns() is the point swing for the hand.

Testing:

  • mahjong_riichi_utils_test.py: 27 tests on hand-shape decomposition, fu/yaku detection (including a regression test for a sanshoku-doujun bug caught along the way — it originally only matched runs starting at "1"), dora counting, and the payment tables, checked against hand-derived reference values.
  • mahjong_riichi_test.py: 9 tests covering the deal structure, action-encoding consistency, a directed Tenhou-tsumo scenario, a directed ron + furiten scenario (confirming furiten blocks ron across a player's entire wait-set, not just the declined tile), riichi discard-locking, pyspiel.random_sim_test with serialization, and a zero-sum-returns check across many full random games.
  • Playthrough generated and verified to round-trip via generate_playthrough.replay.
  • Registered in open_spiel/python/games/__init__.py, CMakeLists.txt, and pyspiel_test.py's expected-games list; added a row to games.md.

Happy to iterate on the calling-priority design in particular, since that's the part without existing precedent in the codebase.

Python-only implementation of one hand of four-player Japanese Riichi
Mahjong: full deal/draw/discard cycle, riichi declaration, and calling
(chi/pon/kan/ron) implemented as sequential priority-ordered polling
after each discard (Ron > Pon/Kan > Chi, multi-ron supported), since
the real-time interrupt semantics of calling have no direct mapping
onto OpenSpiel's simultaneous or turn-based dynamics (see google-deepmind#979).

Hand evaluation (mahjong_riichi_utils.py) is a self-contained module
covering the standard yaku set, fu/han computation, dora/aka-dora/
ura-dora, and the mangan-through-yakuman point table, so it can be
unit-tested independently of game flow.

Scope: one hand per episode (deal through win or draw), not a full
multi-hand match with dealer rotation/score carryover.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant