Skip to content

Latest commit

 

History

History
110 lines (82 loc) · 3.6 KB

File metadata and controls

110 lines (82 loc) · 3.6 KB

Mahjong AI

Competitive Mahjong AI engine supporting both Japanese Riichi (4-player) and MCR (Chinese Official) rules, with rule-specific strategy profiles designed for high-level play.

Features

Dual Rule Set Support

  • Japanese Riichi Mahjong: Full yaku detection (30+ yaku including all yakuman), han/fu scoring, furiten tracking, riichi declaration
  • MCR (Chinese Official): 81 fan pattern detection with exclusion rules, minimum 8-fan validation

AI Strategy Engine

  • Riichi Strategy (Defensive): Tile efficiency optimization, push/fold EV framework, suji/kabe safety analysis, conservative calling policy, riichi timing
  • MCR Strategy (Aggressive): Fan-targeting hand building, liberal calling policy, fan preservation scoring

Core Engine

  • Shanten Calculator: Recursive suit decomposition with caching for fast tiles-to-tenpai calculation
  • Tile Efficiency (Ukeire): Acceptance width calculation for optimal discard selection
  • Defense Module: Suji inference, kabe (wall) analysis, composite danger evaluation, EV-based push/fold decisions
  • Monte Carlo Simulation: Hidden tile sampling for probability estimation
  • Game Engine: Complete game loop with draw, discard, chi/pon/kan, riichi, tsumo/ron
  • Arena: Agent-vs-agent evaluation framework

Installation

pip install -e ".[dev]"

Quick Start

from mahjong_ai.ai.agent import MahjongAgent
from mahjong_ai.rules.riichi import RiichiRuleSet
from mahjong_ai.training.arena import Arena

# Create 4 AI agents
players = [MahjongAgent.riichi() for _ in range(4)]

# Run a match
arena = Arena(RiichiRuleSet(), players)
stats = arena.run_match(num_rounds=8, num_games=100)
print(stats.summary())

Shanten Calculation

from mahjong_ai.core.tiles import TileSet
from mahjong_ai.shanten import shanten, ukeire

hand = TileSet.from_string("123m456p789s12z")
print(f"Shanten: {shanten(hand)}")

count, accepting = ukeire(hand)
print(f"Acceptance: {count} tiles, kinds: {accepting}")

Hand Scoring

from mahjong_ai.core.hand import Hand, WinContext, WinType
from mahjong_ai.rules.riichi.scoring import calculate_score

hand = Hand.from_string("123m456p789s11177z")
ctx = WinContext(win_tile=33, win_type=WinType.RON, is_riichi=True)
score = calculate_score(hand, ctx)
print(f"{score.han} han / {score.fu} fu = {score.total_points} points")
print(f"Yaku: {score.yaku}")

Architecture

mahjong_ai/
├── core/           # Tile representation (34-array), melds, hands, wall, game state
├── shanten/        # Shanten calculator + tile efficiency (ukeire)
├── rules/
│   ├── riichi/     # Yaku detection, han/fu scoring, furiten tracking
│   └── mcr/        # 81 fan patterns, exclusion rules, MCR scoring
├── engine/         # Game loop, actions, player interface
├── defense/        # Suji, kabe, danger evaluation, push/fold framework
├── ai/             # Strategy agents, hand evaluator, Monte Carlo simulation
└── training/       # Arena for agent evaluation

Tile Notation

Numbered suits: 123m (man/characters), 456p (pin/circles), 789s (sou/bamboo)
Honors (z):     1=East, 2=South, 3=West, 4=North, 5=White, 6=Green, 7=Red

Testing

pytest tests/ -v

Roadmap

  • Cython-accelerated shanten for 100x speedup
  • Neural network policy via self-play reinforcement learning
  • Opponent modeling from discard pattern analysis
  • Tenhou/Majsoul replay import for supervised pre-training
  • MCR knitted pattern support in shanten calculator
  • Web interface for human vs AI play

License

MIT