diff --git a/mahjong/hand_calculating/hand_config.py b/mahjong/hand_calculating/hand_config.py index b312e69..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 @@ -83,6 +85,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 +103,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. @@ -125,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 @@ -140,6 +145,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/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" diff --git a/tests/hand_calculating/tests_scores_calculation.py b/tests/hand_calculating/tests_scores_calculation.py index 666f2d0..80e8964 100644 --- a/tests/hand_calculating/tests_scores_calculation.py +++ b/tests/hand_calculating/tests_scores_calculation.py @@ -148,6 +148,47 @@ def test_calculate_scores_with_bonus() -> None: assert result["total"] == 13500 +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, options=options) + 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, options=options) + 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, options=options) + 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, options=options) + 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"), [ @@ -161,10 +202,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