Skip to content

Implement game result analysis on the RiichiEnv #130

@smly

Description

@smly

Add a match result analysis API to the RiichiEnv object so users can easily access per-round and per-match aggregate statistics for each player's key actions.

Background

The RiichiEnv class (defined in riichienv-python/src/env.rs, exposed via PyO3) already tracks per-step game state including hands, discards, melds, riichi_declared, win_results, and score_deltas. The WinResult class provides detailed win-hand scoring (han, fu, yaku list, ron/tsumo). However, there is no built-in way to query aggregate statistics across rounds or across an entire match.

Currently, users must manually iterate through game logs or observations to compute basic statistics, which is tedious and error-prone.

Proposed API

Per-round (kyoku) statistics for each player:

Statistic Description
deal_in (houjuu) Whether the player dealt into another player's winning hand (ron)
riichi Whether the player declared riichi
furo (melds/calls) Whether the player made any open calls (chi/pon/kan)
win Whether the player won the round (and by tsumo or ron)
tenpai Whether the player was tenpai at exhaustive draw

Per-match aggregate statistics for each player:

Statistic Description
deal_in_count Total number of rounds where the player dealt in
riichi_count Total number of riichi declarations
furo_count Total number of rounds where the player made open calls
win_count Total number of round wins
tsumo_count Total number of tsumo wins
ron_count Total number of ron wins
final_score Final score
final_rank Final placement (1st, 2nd, 3rd, 4th)

Example Usage

env = RiichiEnv()
# ... run a full game ...

stats = env.match_stats()

# Per-player aggregates
for player in range(4):
    p = stats.player(player)
    print(f"Player {player}: deal-ins={p.deal_in_count}, riichi={p.riichi_count}, furo={p.furo_count}")

# Per-round detail
for kyoku in stats.kyokus():
    for player in range(4):
        r = kyoku.player(player)
        print(f"  Player {player}: deal_in={r.deal_in}, riichi={r.riichi}, furo={r.furo}")

Implementation Notes

  • Statistics should be computed lazily or accumulated during step() calls to avoid redundant iteration.
  • Consider whether this should live in the Rust core (for performance with large batches) or as a Python-level wrapper.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions