mahjong.hand_calculating.hand
- class mahjong.hand_calculating.hand.HandCalculator[source]
Bases:
objectHand value estimator.
Evaluate a winning hand’s han, fu, yaku, and payment amounts. Accepts tiles in 136-format, an optional
HandConfigfor win conditions and rule variants, and optional melds and dora/ura-dora indicators. When multiple valid decompositions exist, the highest-scoring result is returned.- ERR_NO_WINNING_TILE = 'winning_tile_not_in_hand'
win_tileindex is not present in thetilesarray.
- ERR_HAND_NOT_WINNING = 'hand_not_winning'
No valid decomposition exists; the hand is not a winning hand.
- ERR_NO_YAKU = 'no_yaku'
Hand can be decomposed but has zero han (no yaku applies).
- ERR_OPEN_HAND_RIICHI = 'open_hand_riichi_not_allowed'
Riichi declared on an open hand (open melds present).
- ERR_OPEN_HAND_DABURI = 'open_hand_daburi_not_allowed'
Double riichi declared on an open hand.
- ERR_IPPATSU_WITHOUT_RIICHI = 'ippatsu_without_riichi_not_allowed'
Ippatsu claimed without riichi or double riichi.
- ERR_CHANKAN_WITH_TSUMO = 'chankan_with_tsumo_not_allowed'
Chankan (robbing a kan) requires a ron win.
- ERR_RINSHAN_WITHOUT_TSUMO = 'rinshan_without_tsumo_not_allowed'
Rinshan kaihou (win after kan) requires a tsumo win.
- ERR_HAITEI_WITHOUT_TSUMO = 'haitei_without_tsumo_not_allowed'
Haitei raoyue (last-tile draw) requires a tsumo win.
- ERR_HOUTEI_WITH_TSUMO = 'houtei_with_tsumo_not_allowed'
Houtei raoyui (last-tile discard) requires a ron win.
- ERR_HAITEI_WITH_RINSHAN = 'haitei_with_rinshan_not_allowed'
Haitei and rinshan are mutually exclusive (different last-tile sources).
- ERR_HOUTEI_WITH_CHANKAN = 'houtei_with_chankan_not_allowed'
Houtei and chankan are mutually exclusive.
- ERR_TENHOU_NOT_AS_DEALER = 'tenhou_not_as_dealer_not_allowed'
Tenhou is exclusive to the dealer (East player).
- ERR_TENHOU_WITHOUT_TSUMO = 'tenhou_without_tsumo_not_allowed'
Tenhou requires a tsumo win on the dealer’s first draw.
- ERR_TENHOU_WITH_MELD = 'tenhou_with_meld_not_allowed'
Tenhou requires a closed hand with no declared melds.
- ERR_CHIIHOU_AS_DEALER = 'chiihou_as_dealer_not_allowed'
Chiihou is exclusive to non-dealer players.
- ERR_CHIIHOU_WITHOUT_TSUMO = 'chiihou_without_tsumo_not_allowed'
Chiihou requires a tsumo win on the player’s first draw.
- ERR_CHIIHOU_WITH_MELD = 'chiihou_with_meld_not_allowed'
Chiihou requires a closed hand with no declared melds.
- ERR_RENHOU_AS_DEALER = 'renhou_as_dealer_not_allowed'
Renhou is exclusive to non-dealer players.
- ERR_RENHOU_WITH_TSUMO = 'renhou_with_tsumo_not_allowed'
Renhou requires a ron win before the player’s first draw.
- ERR_RENHOU_WITH_MELD = 'renhou_with_meld_not_allowed'
Renhou requires a closed hand with no declared melds.
- static estimate_hand_value(tiles, win_tile, melds=None, dora_indicators=None, config=None, scores_calculator_factory=<class 'mahjong.hand_calculating.scores.ScoresCalculator'>, ura_dora_indicators=None)[source]
Estimate the point value of a winning hand.
Validate the hand and win conditions, decompose the hand into all possible block combinations, evaluate yaku and fu for each decomposition, and return the highest-scoring result as a
HandResponse.Basic closed ron with tanyao:
>>> from mahjong.hand_calculating.hand import HandCalculator >>> from mahjong.tile import TilesConverter >>> tiles = TilesConverter.string_to_136_array(man="22444", pin="333567", sou="444") >>> win_tile = TilesConverter.string_to_136_array(sou="4")[0] >>> result = HandCalculator.estimate_hand_value(tiles, win_tile) >>> result.han 1 >>> result.fu 40 >>> result.cost["main"] 1300
Tsumo win with riichi and pinfu:
>>> from mahjong.hand_calculating.hand_config import HandConfig >>> tiles = TilesConverter.string_to_136_array(man="234789", pin="12345666") >>> win_tile = TilesConverter.string_to_136_array(pin="6")[0] >>> config = HandConfig(is_tsumo=True, is_riichi=True) >>> result = HandCalculator.estimate_hand_value(tiles, win_tile, config=config) >>> result.han 3 >>> result.fu 20 >>> result.cost["main"] 1300 >>> result.cost["additional"] 700
Dealer tsumo with riichi and ippatsu:
>>> from mahjong.constants import EAST >>> config = HandConfig(is_tsumo=True, is_riichi=True, is_ippatsu=True, player_wind=EAST) >>> result = HandCalculator.estimate_hand_value(tiles, win_tile, config=config) >>> result.error is None True
Open hand with melds and dora:
>>> from mahjong.meld import Meld >>> tiles = TilesConverter.string_to_136_array(man="234567", pin="22", sou="234", honors="555") >>> win_tile = TilesConverter.string_to_136_array(man="7")[0] >>> melds = [Meld(meld_type=Meld.PON, tiles=TilesConverter.string_to_136_array(honors="555"))] >>> dora_indicators = [TilesConverter.string_to_136_array(man="6")[0]] >>> result = HandCalculator.estimate_hand_value(tiles, win_tile, melds=melds, dora_indicators=dora_indicators) >>> result.han 2
Invalid hand returns an error:
>>> tiles = TilesConverter.string_to_136_array(man="12345") >>> win_tile = TilesConverter.string_to_136_array(man="1")[0] >>> result = HandCalculator.estimate_hand_value(tiles, win_tile) >>> result.error == HandCalculator.ERR_HAND_NOT_WINNING True
- Parameters:
tiles (Collection[int]) – hand tiles in 136-format (14 tiles including the winning tile; 15 with one kan, 16 with two, etc.)
win_tile (int) – the winning tile index in 136-format (must be present in
tiles)melds (Collection[Meld] | None) – declared melds (
Meldobjects for chi, pon, kan)dora_indicators (Collection[int] | None) – dora indicator tile indices in 136-format
config (HandConfig | None) – hand configuration with win conditions, wind context, and optional rules; defaults to a closed ron with no special conditions
scores_calculator_factory (type[ScoresCalculator]) – scoring calculator class; pass
Aotenjoufor aotenjou (limitless) scoringura_dora_indicators (Collection[int] | None) – ura dora indicator tile indices in 136-format (counted only when riichi or double riichi is declared)
- Returns:
HandResponsewith scoring details on success, or witherrorset on failure- Return type: