From fbe6258afea84888f7be31fff7c8e8b032fa87df Mon Sep 17 00:00:00 2001 From: Apricot-S Date: Wed, 10 Jun 2026 21:52:24 +0900 Subject: [PATCH] refactor: move num_nuki_dora to HandConfig --- mahjong/hand_calculating/hand.py | 10 ++++------ mahjong/hand_calculating/hand_config.py | 6 ++++++ tests/hand_calculating/tests_yaku_calculation.py | 14 ++++++++++---- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/mahjong/hand_calculating/hand.py b/mahjong/hand_calculating/hand.py index f57c29e..11e4f0d 100644 --- a/mahjong/hand_calculating/hand.py +++ b/mahjong/hand_calculating/hand.py @@ -99,7 +99,6 @@ def estimate_hand_value( config: HandConfig | None = None, scores_calculator_factory: type[ScoresCalculator] = ScoresCalculator, ura_dora_indicators: Collection[int] | None = None, - num_nuki_dora: int = 0, ) -> HandResponse: """ Estimate the point value of a winning hand. @@ -176,7 +175,6 @@ def estimate_hand_value( :class:`~mahjong.hand_calculating.scores.Aotenjou` for aotenjou (limitless) scoring :param ura_dora_indicators: ura dora indicator tile indices in 136-format (counted only when riichi or double riichi is declared) - :param num_nuki_dora: the number of nuki dora (north wind extraction) :return: :class:`~mahjong.hand_calculating.hand_response.HandResponse` with scoring details on success, or with :attr:`~mahjong.hand_calculating.hand_response.HandResponse.error` set on failure @@ -281,8 +279,8 @@ def estimate_hand_value( # precompute dora counts, invariant across all hand decompositions dora_count_map = build_dora_count_map(dora_indicators) precomputed_dora = count_dora_for_hand(tiles_34, dora_count_map) - if num_nuki_dora > 0: - precomputed_dora += dora_count_map.get(NORTH, 0) * num_nuki_dora + num_nuki_dora + if config.num_nuki_dora > 0: + precomputed_dora += dora_count_map.get(NORTH, 0) * config.num_nuki_dora + config.num_nuki_dora precomputed_aka_dora = 0 if config.options.has_aka_dora: @@ -292,8 +290,8 @@ def estimate_hand_value( if config.is_riichi or config.is_daburu_riichi: ura_count_map = build_dora_count_map(ura_dora_indicators) precomputed_ura_dora = count_dora_for_hand(tiles_34, ura_count_map) - if num_nuki_dora > 0: - precomputed_ura_dora += ura_count_map.get(NORTH, 0) * num_nuki_dora + if config.num_nuki_dora > 0: + precomputed_ura_dora += ura_count_map.get(NORTH, 0) * config.num_nuki_dora yakuhai_seat_wind_yaku = ( config.yaku.seat_wind_east, diff --git a/mahjong/hand_calculating/hand_config.py b/mahjong/hand_calculating/hand_config.py index b312e69..3b205ba 100644 --- a/mahjong/hand_calculating/hand_config.py +++ b/mahjong/hand_calculating/hand_config.py @@ -220,6 +220,8 @@ class HandConfig(HandConstants): :vartype kyoutaku_number: int :ivar tsumi_number: number of honba counters (100 points each per counter) :vartype tsumi_number: int + :ivar num_nuki_dora: the number of nuki dora (north wind extraction) + :vartype num_nuki_dora: int """ yaku: YakuConfig @@ -246,6 +248,7 @@ class HandConfig(HandConstants): kyoutaku_number: int tsumi_number: int + num_nuki_dora: int def __init__( self, @@ -268,6 +271,7 @@ def __init__( tsumi_number: int = 0, paarenchan: int = 0, options: OptionalRules | None = None, + num_nuki_dora: int = 0, ) -> None: """ Initialize hand configuration. @@ -306,6 +310,7 @@ def __init__( :param paarenchan: consecutive dealer wins count for paarenchan check :param options: optional rule settings; defaults to :class:`OptionalRules` with all defaults when None + :param num_nuki_dora: the number of nuki dora (north wind extraction) """ self.yaku = YakuConfig() self.options = options or OptionalRules() @@ -331,3 +336,4 @@ def __init__( self.kyoutaku_number = kyoutaku_number self.tsumi_number = tsumi_number + self.num_nuki_dora = num_nuki_dora diff --git a/tests/hand_calculating/tests_yaku_calculation.py b/tests/hand_calculating/tests_yaku_calculation.py index 007efe2..d660ec7 100644 --- a/tests/hand_calculating/tests_yaku_calculation.py +++ b/tests/hand_calculating/tests_yaku_calculation.py @@ -1398,7 +1398,11 @@ def test_nuki_dora_with_dora_indicators(num_nuki_dora: int, num_dora_indicators: tiles = TilesConverter.string_to_136_array(sou="345", pin="456", man="12355599", has_aka_dora=True) win_tile = _string_to_136_tile(man="9") - hand_config = HandConfig(is_tsumo=True, options=OptionalRules(has_aka_dora=True)) + hand_config = HandConfig( + is_tsumo=True, + options=OptionalRules(has_aka_dora=True), + num_nuki_dora=num_nuki_dora, + ) dora_indicators = TilesConverter.string_to_136_array(honors="3" * num_dora_indicators) result = HandCalculator.estimate_hand_value( @@ -1406,7 +1410,6 @@ def test_nuki_dora_with_dora_indicators(num_nuki_dora: int, num_dora_indicators: win_tile, config=hand_config, dora_indicators=dora_indicators, - num_nuki_dora=num_nuki_dora, ) assert result.error is None assert result.han == han @@ -1435,7 +1438,11 @@ def test_nuki_dora_with_ura_dora_indicators(num_nuki_dora: int, num_ura_dora_ind tiles = TilesConverter.string_to_136_array(sou="345", pin="456", man="12355599", has_aka_dora=True) win_tile = _string_to_136_tile(man="9") - hand_config = HandConfig(is_riichi=True, options=OptionalRules(has_aka_dora=True)) + hand_config = HandConfig( + is_riichi=True, + options=OptionalRules(has_aka_dora=True), + num_nuki_dora=num_nuki_dora, + ) ura_dora_indicators = TilesConverter.string_to_136_array(honors="3" * num_ura_dora_indicators) result = HandCalculator.estimate_hand_value( @@ -1443,7 +1450,6 @@ def test_nuki_dora_with_ura_dora_indicators(num_nuki_dora: int, num_ura_dora_ind win_tile, config=hand_config, ura_dora_indicators=ura_dora_indicators, - num_nuki_dora=num_nuki_dora, ) assert result.error is None assert result.han == han