C++ Engine¶
pymahjong uses a C++ backend for high-performance game simulation. This section documents the C++ architecture.
Architecture Overview¶
┌─────────────────────────────────────────┐
│ Python Layer │
│ (pymahjong/env_pymahjong.py) │
└─────────────────┬───────────────────────┘
│ pybind11
┌─────────────────▼───────────────────────┐
│ Pybinder Layer │
│ (Pybinder/MahjongPy.cpp) │
└─────────────────┬───────────────────────┘
│
┌─────────────────▼───────────────────────┐
│ Mahjong Core │
│ (Mahjong/Table.cpp, Player.cpp, etc.) │
└─────────────────────────────────────────┘
Core Classes¶
Table¶
The central game state manager.
File: Mahjong/Table.h, Mahjong/Table.cpp
Key Methods:
game_init()- Initialize a new gameget_phase()- Get current game phaseget_self_actions()- Get available self-actionsget_response_actions()- Get available response actionsmake_selection(int)- Execute an action
Phases:
Phase |
Description |
|---|---|
|
Self-action phases |
|
Response phases |
|
Terminal state |
Player¶
Manages individual player state.
File: Mahjong/Player.h, Mahjong/Player.cpp
Key Attributes:
hand- Tiles in handriver- Discarded tilescall_groups- Melded tile groupsatari_tiles- Winning tiles (if tenpai)riichi- Riichi statusscore- Player score
Key Methods:
get_discard()- Generate discard actionsget_chi()- Generate chi actionsget_pon()- Generate pon actionsget_ron()- Generate ron actionsupdate_atari_tiles()- Update tenpai status
Rule¶
Implements tile splitting and hand evaluation.
File: Mahjong/Rule.h, Mahjong/Rule.cpp
Key Functions:
get_completed_tiles()- Find valid hand decompositionsget_atari_hai()- Calculate waiting tilesis_tenpai()- Check tenpai statusis_riichi_able()- Check if riichi is possible
ScoreCounter¶
Evaluates yaku and calculates scores.
File: Mahjong/ScoreCounter.h, Mahjong/ScoreCounter.cpp
Key Functions:
count_yaku()- Evaluate yaku for a winning handcalculate_score()- Calculate final score
Action¶
Defines action types and selection.
File: Mahjong/Action.h, Mahjong/Action.cpp
BaseAction Enum:
enum class BaseAction : uint8_t {
Pass, Chi, Pon, Kan, Ron, // Response
ChanAnKan, ChanKan, // Special response
AnKan, KaKan, Discard, Riichi, // Self action
Tsumo, Kyushukyuhai // Special self
};
Observation Encoding¶
File: Mahjong/Encoding/TrainingDataEncodingV1.cpp
The V1 encoding produces a 93x34 boolean matrix:
void encode_table(const Table& table, int player_id, bool oracle, dtype* output);
void encode_actions_vector(const Table& table, int player_id, dtype* output);
V2 encoding provides more detailed features:
// Mahjong/Encoding/TrainingDataEncodingV2.h
class TableEncoder {
void init();
void update();
// ...
};
Game Flow¶
game_init()
│
▼
from_beginning() ─────► _generate_self_actions()
│ │
│ ▼
│ self_actions (list)
│ │
▼ │
make_selection() ◄─────────────────┘
│
▼
_handle_self_action()
│
▼
_generate_response_actions()
│
▼
response_actions (for each player)
│
▼
make_selection() (x4)
│
▼
_handle_response_final_execution()
│
▼
from_beginning() (loop)
│
▼
GAME_OVER
Building from Source¶
Requirements¶
CMake 3.15+
C++14 compiler
Python 3.10+ with development headers
Build Steps¶
mkdir build && cd build
cmake .. -DCMAKE_CXX_COMPILER=clang++
make -j$(nproc)
./bin/test # Run C++ tests
CMake Structure¶
CMakeLists.txt (root)
├── ThirdParty/CMakeLists.txt
│ ├── fmt/CMakeLists.txt
│ └── tenhou/CMakeLists.txt
├── Mahjong/CMakeLists.txt
├── Pybinder/CMakeLists.txt
└── test/CMakeLists.txt
Performance¶
Memory: ~800KB per game instance
Speed: ~10ms per complete game (random play)
Optimization: Uses lookup tables for shanten calculation
Debugging¶
Enable debug mode to generate replay code:
table.set_debug_mode(1); // Buffer mode
table.set_debug_mode(2); // Stdout mode
// ... play game ...
table.print_debug_replay(); // Print replay code