From 5828c54bf5df6e711d341fb9cbeb79121a6c6862 Mon Sep 17 00:00:00 2001 From: Apricot-S Date: Fri, 29 May 2026 21:34:00 +0900 Subject: [PATCH 1/5] test: Remove an unnecessary unit test --- tests/hand_calculating/tests_scores_calculation.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tests/hand_calculating/tests_scores_calculation.py b/tests/hand_calculating/tests_scores_calculation.py index 666f2d0..b205d63 100644 --- a/tests/hand_calculating/tests_scores_calculation.py +++ b/tests/hand_calculating/tests_scores_calculation.py @@ -161,10 +161,3 @@ def test_kiriage_mangan(player_wind: int | None, han: int, fu: int, expected_mai config = HandConfig(player_wind=player_wind, options=OptionalRules(kiriage=True)) result = ScoresCalculator.calculate_scores(han=han, fu=fu, config=config) assert result["main"] == expected_main - - -def test_calculate_scores_can_call_as_static_method() -> None: - config = HandConfig(options=OptionalRules(kazoe_limit=HandConfig.KAZOE_NO_LIMIT)) - - result = ScoresCalculator.calculate_scores(han=1, fu=30, config=config) - assert result["main"] == 1000 From d744aacc763f2263ce63d1de2c5998f2612dc5bd Mon Sep 17 00:00:00 2001 From: Apricot-S Date: Fri, 29 May 2026 21:49:21 +0900 Subject: [PATCH 2/5] test: Add unit tests for three-player (WIP) --- .../tests_scores_calculation.py | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tests/hand_calculating/tests_scores_calculation.py b/tests/hand_calculating/tests_scores_calculation.py index b205d63..ab21135 100644 --- a/tests/hand_calculating/tests_scores_calculation.py +++ b/tests/hand_calculating/tests_scores_calculation.py @@ -148,6 +148,46 @@ def test_calculate_scores_with_bonus() -> None: assert result["total"] == 13500 +def test_calculate_scores_with_bonus_three_player() -> None: + hand = ScoresCalculator() + + config = HandConfig(player_wind=EAST, is_tsumo=True, tsumi_number=2, kyoutaku_number=3) + result = hand.calculate_scores(han=3, fu=30, config=config) + assert result["main"] == 2000 + assert result["additional"] == 2000 + assert result["main_bonus"] == 200 + assert result["additional_bonus"] == 200 + assert result["kyoutaku_bonus"] == 3000 + assert result["total"] == 7400 + + config = HandConfig(player_wind=WEST, is_tsumo=True, tsumi_number=4, kyoutaku_number=1) + result = hand.calculate_scores(han=4, fu=30, config=config) + assert result["main"] == 3900 + assert result["additional"] == 2000 + assert result["main_bonus"] == 400 + assert result["additional_bonus"] == 400 + assert result["kyoutaku_bonus"] == 1000 + assert result["total"] == 7700 + + config = HandConfig(player_wind=WEST, tsumi_number=5) + result = hand.calculate_scores(han=6, fu=30, config=config) + assert result["main"] == 12000 + assert result["additional"] == 0 + assert result["main_bonus"] == 1000 + assert result["additional_bonus"] == 0 + assert result["kyoutaku_bonus"] == 0 + assert result["total"] == 13000 + + config = HandConfig(player_wind=EAST, tsumi_number=5) + result = hand.calculate_scores(han=5, fu=30, config=config) + assert result["main"] == 12000 + assert result["additional"] == 0 + assert result["main_bonus"] == 1000 + assert result["additional_bonus"] == 0 + assert result["kyoutaku_bonus"] == 0 + assert result["total"] == 13000 + + @pytest.mark.parametrize( ("player_wind", "han", "fu", "expected_main"), [ From 3b8ce216c767a7d4c534b44f05d41c898fee6b3e Mon Sep 17 00:00:00 2001 From: Apricot-S Date: Wed, 3 Jun 2026 11:21:40 +0900 Subject: [PATCH 3/5] feat: WIP --- mahjong/hand_calculating/hand_config.py | 3 +++ tests/hand_calculating/tests_scores_calculation.py | 9 +++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/mahjong/hand_calculating/hand_config.py b/mahjong/hand_calculating/hand_config.py index b312e69..eadf407 100644 --- a/mahjong/hand_calculating/hand_config.py +++ b/mahjong/hand_calculating/hand_config.py @@ -83,6 +83,7 @@ class OptionalRules: has_sashikomi_yakuman: bool limit_to_sextuple_yakuman: bool paarenchan_needs_yaku: bool + is_three_player: bool def __init__( self, @@ -100,6 +101,7 @@ def __init__( limit_to_sextuple_yakuman: bool = True, paarenchan_needs_yaku: bool = True, has_daichisei: bool = False, + is_three_player: bool = False, ) -> None: """ Initialize optional rules. @@ -140,6 +142,7 @@ def __init__( self.limit_to_sextuple_yakuman = limit_to_sextuple_yakuman self.has_daichisei = has_daichisei self.paarenchan_needs_yaku = paarenchan_needs_yaku + self.is_three_player = is_three_player class HandConfig(HandConstants): diff --git a/tests/hand_calculating/tests_scores_calculation.py b/tests/hand_calculating/tests_scores_calculation.py index ab21135..80e8964 100644 --- a/tests/hand_calculating/tests_scores_calculation.py +++ b/tests/hand_calculating/tests_scores_calculation.py @@ -150,8 +150,9 @@ def test_calculate_scores_with_bonus() -> None: def test_calculate_scores_with_bonus_three_player() -> None: hand = ScoresCalculator() + options = OptionalRules(is_three_player=True) - config = HandConfig(player_wind=EAST, is_tsumo=True, tsumi_number=2, kyoutaku_number=3) + config = HandConfig(player_wind=EAST, is_tsumo=True, tsumi_number=2, kyoutaku_number=3, options=options) result = hand.calculate_scores(han=3, fu=30, config=config) assert result["main"] == 2000 assert result["additional"] == 2000 @@ -160,7 +161,7 @@ def test_calculate_scores_with_bonus_three_player() -> None: assert result["kyoutaku_bonus"] == 3000 assert result["total"] == 7400 - config = HandConfig(player_wind=WEST, is_tsumo=True, tsumi_number=4, kyoutaku_number=1) + config = HandConfig(player_wind=WEST, is_tsumo=True, tsumi_number=4, kyoutaku_number=1, options=options) result = hand.calculate_scores(han=4, fu=30, config=config) assert result["main"] == 3900 assert result["additional"] == 2000 @@ -169,7 +170,7 @@ def test_calculate_scores_with_bonus_three_player() -> None: assert result["kyoutaku_bonus"] == 1000 assert result["total"] == 7700 - config = HandConfig(player_wind=WEST, tsumi_number=5) + config = HandConfig(player_wind=WEST, tsumi_number=5, options=options) result = hand.calculate_scores(han=6, fu=30, config=config) assert result["main"] == 12000 assert result["additional"] == 0 @@ -178,7 +179,7 @@ def test_calculate_scores_with_bonus_three_player() -> None: assert result["kyoutaku_bonus"] == 0 assert result["total"] == 13000 - config = HandConfig(player_wind=EAST, tsumi_number=5) + config = HandConfig(player_wind=EAST, tsumi_number=5, options=options) result = hand.calculate_scores(han=5, fu=30, config=config) assert result["main"] == 12000 assert result["additional"] == 0 From 240b8263ec695fb331e1bd4d82c5f0d958c70075 Mon Sep 17 00:00:00 2001 From: Apricot-S Date: Wed, 3 Jun 2026 13:36:04 +0900 Subject: [PATCH 4/5] feat: Support the score calculation for three-player --- mahjong/hand_calculating/scores.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mahjong/hand_calculating/scores.py b/mahjong/hand_calculating/scores.py index cf2be9d..8e3d9a1 100644 --- a/mahjong/hand_calculating/scores.py +++ b/mahjong/hand_calculating/scores.py @@ -196,11 +196,12 @@ def calculate_scores(han: int, fu: int, config: HandConfig, is_yakuman: bool = F else: # ron additional = 0 additional_bonus = 0 - main_bonus = 300 * config.tsumi_number + main_bonus = (200 if config.options.is_three_player else 300) * config.tsumi_number main = six_rounded if config.is_dealer else four_rounded + num_additional = 1 if config.options.is_three_player else 2 kyoutaku_bonus = 1000 * config.kyoutaku_number - total = (main + main_bonus) + 2 * (additional + additional_bonus) + kyoutaku_bonus + total = (main + main_bonus) + num_additional * (additional + additional_bonus) + kyoutaku_bonus if config.is_nagashi_mangan: yaku_level = "nagashi mangan" From e0724c83a52124ddd2398be7be29109da3479173 Mon Sep 17 00:00:00 2001 From: Apricot-S Date: Wed, 3 Jun 2026 13:47:01 +0900 Subject: [PATCH 5/5] docs: Add docstrings --- mahjong/hand_calculating/hand_config.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mahjong/hand_calculating/hand_config.py b/mahjong/hand_calculating/hand_config.py index eadf407..dc93ef1 100644 --- a/mahjong/hand_calculating/hand_config.py +++ b/mahjong/hand_calculating/hand_config.py @@ -67,6 +67,8 @@ class OptionalRules: :vartype limit_to_sextuple_yakuman: bool :ivar paarenchan_needs_yaku: require at least one yaku for paarenchan to count :vartype paarenchan_needs_yaku: bool + :ivar is_three_player: enable three-player rules + :vartype is_three_player: bool """ has_open_tanyao: bool @@ -127,6 +129,7 @@ def __init__( :param limit_to_sextuple_yakuman: cap yakuman multiplier at 6x :param paarenchan_needs_yaku: require yaku for paarenchan :param has_daichisei: enable daichisei yakuman + :param is_three_player: enable three-player rules """ self.has_open_tanyao = has_open_tanyao self.has_aka_dora = has_aka_dora