Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions mahjong/hand_calculating/hand_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand All @@ -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.
Expand All @@ -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
Expand All @@ -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):
Expand Down
5 changes: 3 additions & 2 deletions mahjong/hand_calculating/scores.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
48 changes: 41 additions & 7 deletions tests/hand_calculating/tests_scores_calculation.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
[
Expand All @@ -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