mahjong.hand_calculating.scores

Score calculation for winning hands.

Classes

  • ScoresResult - typed dictionary holding the score breakdown

  • ScoresCalculator - standard scoring with han/fu, honba, and kyoutaku bonuses

  • Aotenjou - variant scoring with no mangan cap (aotenjou rule)

class mahjong.hand_calculating.scores.ScoresResult[source]

Bases: TypedDict

Score breakdown for a winning hand.

Each field represents a component of the final payment between players. The meaning of main and additional depends on the win method:

  • Ron: main is the full payment from the discarding player; additional is always 0.

  • Dealer tsumo: main and additional are equal — each non-dealer pays the same amount.

  • Non-dealer tsumo: main is the dealer’s payment; additional is the payment from each non-dealer.

Parameters:
  • main – base cost (before honba bonus)

  • additional – base cost for each non-dealer (before honba bonus); 0 for ron

  • main_bonus – honba bonus added to main

  • additional_bonus – honba bonus added to additional

  • kyoutaku_bonus – points from accumulated riichi deposits (1000 per deposit)

  • total – total points the winner earns

  • yaku_level – scoring tier label (e.g. "mangan", "yakuman", "" for below mangan)

class mahjong.hand_calculating.scores.ScoresCalculator[source]

Bases: object

Calculate scores for a winning hand using standard Japanese mahjong rules.

Scores are determined by han and fu values, then adjusted for honba (tsumi) counters and kyoutaku (riichi deposit) bonuses. Hands at or above 5 han receive fixed-tier payouts (mangan through yakuman).

static calculate_scores(han, fu, config, is_yakuman=False)[source]

Calculate score payment for a hand with the given han and fu.

Determine the base payment from han/fu, apply scoring tier caps (mangan, haneman, baiman, sanbaiman, yakuman), then add honba and kyoutaku bonuses.

A non-dealer ron at 3 han 30 fu:

>>> from mahjong.hand_calculating.scores import ScoresCalculator
>>> from mahjong.hand_calculating.hand_config import HandConfig
>>> result = ScoresCalculator.calculate_scores(han=3, fu=30, config=HandConfig())
>>> result["main"]
3900
>>> result["additional"]
0
>>> result["total"]
3900

A dealer tsumo mangan with 2 honba and 3 riichi deposits:

>>> from mahjong.constants import EAST
>>> config = HandConfig(is_tsumo=True, player_wind=EAST, tsumi_number=2, kyoutaku_number=3)
>>> result = ScoresCalculator.calculate_scores(han=5, fu=30, config=config)
>>> result["main"]
4000
>>> result["additional"]
4000
>>> result["total"]
15600

Mangan (5 han):

>>> result = ScoresCalculator.calculate_scores(han=5, fu=30, config=HandConfig())
>>> result["yaku_level"]
'mangan'
>>> result["main"]
8000
Parameters:
  • han (int) – number of han (doubles)

  • fu (int) – fu (minipoints), rounded to nearest 10

  • config (HandConfig) – hand configuration with win method, dealer status, and bonuses

  • is_yakuman (bool) – True if the hand contains yakuman yaku (bypasses kazoe limit)

Returns:

ScoresResult with the full score breakdown

Return type:

ScoresResult

class mahjong.hand_calculating.scores.Aotenjou[source]

Bases: ScoresCalculator

Variant scoring calculator for the aotenjou (blue ceiling) rule.

Under aotenjou, there is no mangan cap — the base-points formula fu * 2^(2+han) is applied directly regardless of han count. Honba and kyoutaku bonuses are not applied. Yakuman yaku are treated as normal yaku and contribute their han values rather than triggering fixed payouts.

static calculate_scores(han, fu, config, is_yakuman=False)[source]

Calculate score payment under aotenjou rules.

Apply the base-points formula without any mangan cap or scoring tiers. Honba and kyoutaku bonuses are not included.

A non-dealer ron at 13 han 40 fu under aotenjou:

>>> from mahjong.hand_calculating.scores import Aotenjou
>>> from mahjong.hand_calculating.hand_config import HandConfig
>>> result = Aotenjou.calculate_scores(han=13, fu=40, config=HandConfig())
>>> result["main"]
5242900
>>> result["yaku_level"]
''
Parameters:
  • han (int) – number of han (doubles)

  • fu (int) – fu (minipoints)

  • config (HandConfig) – hand configuration with win method and dealer status

  • is_yakuman (bool) – unused (aotenjou treats yakuman as normal yaku)

Returns:

ScoresResult with the score breakdown (no bonuses)

Return type:

ScoresResult

static aotenjou_filter_yaku(hand_yaku, config)[source]

Remove lower yaku that are precursors to yakuman yaku in aotenjou mode.

Under aotenjou, yakuman are scored as normal yaku with their han values. When a yakuman is present, its precursor yaku (e.g. shosangen for daisangen, toitoi for chinroto) must be removed to avoid double-counting.

Parameters:
  • hand_yaku (MutableSequence[Yaku] | MutableSet[Yaku]) – mutable collection of yaku in the hand; modified in place

  • config (HandConfig) – hand configuration providing yaku definitions

Return type:

None