From 206ac4dd1789d1486720039f03e6f8a2fff5a570 Mon Sep 17 00:00:00 2001 From: Aleksei Lisikhin Date: Sat, 7 Feb 2026 14:20:09 +0700 Subject: [PATCH 1/2] chore: add parametrize for some pytest tests --- tests/hand_calculating/tests_aotenjou.py | 20 +- .../hand_calculating/tests_fu_calculation.py | 53 ++- tests/hand_calculating/tests_hand_dividing.py | 101 +++--- .../tests_scores_calculation.py | 342 ++++++------------ .../tests_yaku_calculation.py | 28 +- .../tests_yakuman_calculation.py | 47 ++- tests/tests_tiles_converter.py | 126 +++++-- 7 files changed, 321 insertions(+), 396 deletions(-) diff --git a/tests/hand_calculating/tests_aotenjou.py b/tests/hand_calculating/tests_aotenjou.py index 3a99d3d1..25464e89 100644 --- a/tests/hand_calculating/tests_aotenjou.py +++ b/tests/hand_calculating/tests_aotenjou.py @@ -6,7 +6,7 @@ from tests.utils_for_tests import _make_hand_config, _make_meld, _string_to_136_tile -def test_aotenjou_hands() -> None: +def test_aotenjou_kokushi_ron() -> None: hand = HandCalculator() tiles = TilesConverter.string_to_136_array(sou="119", man="19", pin="19", honors="1234567") @@ -24,6 +24,10 @@ def test_aotenjou_hands() -> None: assert len(result.yaku) == 1 assert result.cost["main"] == 7864400 + +def test_aotenjou_open_hand_with_honor_melds() -> None: + hand = HandCalculator() + tiles = TilesConverter.string_to_136_array(man="234", honors="11122233344") win_tile = _string_to_136_tile(man="2") melds = [ @@ -44,6 +48,10 @@ def test_aotenjou_hands() -> None: assert len(result.yaku) == 4 assert result.cost["main"] + result.cost["additional"] == 83886200 + +def test_aotenjou_all_honors_with_meld() -> None: + hand = HandCalculator() + tiles = TilesConverter.string_to_136_array(honors="11122233444777") win_tile = _string_to_136_tile(honors="2") melds = [ @@ -63,7 +71,9 @@ def test_aotenjou_hands() -> None: assert len(result.yaku) == 6 assert result.cost["main"] + result.cost["additional"] == 1717986918400 - # monster hand for fun + +def test_aotenjou_monster_hand() -> None: + hand = HandCalculator() tiles = TilesConverter.string_to_136_array(honors="111133555566667777") win_tile = _string_to_136_tile(honors="3") @@ -334,7 +344,7 @@ def test_aotenjou_kokushi_tsumo() -> None: hand = HandCalculator() tiles = TilesConverter.string_to_136_array(sou="19", pin="19", man="19", honors="12345677") - win_tile = TilesConverter.string_to_136_array(honors="7")[0] + win_tile = _string_to_136_tile(honors="7") result = hand.estimate_hand_value( tiles, @@ -353,7 +363,7 @@ def test_aotenjou_kokushi_with_dora() -> None: hand = HandCalculator() tiles = TilesConverter.string_to_136_array(sou="19", pin="19", man="19", honors="12345677") - win_tile = TilesConverter.string_to_136_array(honors="7")[0] + win_tile = _string_to_136_tile(honors="7") # 8m indicator makes 9m a dora; kokushi hand includes 9m dora_indicators = TilesConverter.string_to_136_array(man="8") @@ -377,7 +387,7 @@ def test_aotenjou_kokushi_with_ura_dora() -> None: # kokushi with riichi and ura dora in aotenjou scoring tiles = TilesConverter.string_to_136_array(sou="19", pin="19", man="19", honors="12345677") - win_tile = TilesConverter.string_to_136_array(honors="7")[0] + win_tile = _string_to_136_tile(honors="7") ura_dora_indicators = TilesConverter.string_to_136_array(man="8") result = hand.estimate_hand_value( diff --git a/tests/hand_calculating/tests_fu_calculation.py b/tests/hand_calculating/tests_fu_calculation.py index 60d2909b..e95ca0d9 100644 --- a/tests/hand_calculating/tests_fu_calculation.py +++ b/tests/hand_calculating/tests_fu_calculation.py @@ -1,3 +1,5 @@ +import pytest + from mahjong.constants import EAST from mahjong.hand_calculating.fu import FuCalculator from mahjong.hand_calculating.hand import HandCalculator @@ -151,24 +153,19 @@ def test_tsumo_hand_and_not_pinfu() -> None: assert fu == 30 -def test_penchan_fu() -> None: +@pytest.mark.parametrize( + ("tiles_kwargs", "win_tile_kwargs"), + [ + pytest.param({"sou": "12456", "man": "123456", "pin": "55"}, {"sou": "3"}, id="12_wait"), + pytest.param({"sou": "34589", "man": "123456", "pin": "55"}, {"sou": "7"}, id="89_wait"), + ], +) +def test_penchan_fu(tiles_kwargs: dict, win_tile_kwargs: dict) -> None: fu_calculator = FuCalculator() config = HandConfig() - # 1-2-... wait - tiles = TilesConverter.string_to_136_array(sou="12456", man="123456", pin="55") - win_tile = _string_to_136_tile(sou="3") - hand = _hand(TilesConverter.to_34_array([*tiles, win_tile])) - - fu_details, fu = fu_calculator.calculate_fu(hand, win_tile, _get_win_group(hand, win_tile), config) - assert len(fu_details) == 2 - assert {"fu": 30, "reason": FuCalculator.BASE} in fu_details - assert {"fu": 2, "reason": FuCalculator.PENCHAN} in fu_details - assert fu == 40 - - # ...-8-9 wait - tiles = TilesConverter.string_to_136_array(sou="34589", man="123456", pin="55") - win_tile = _string_to_136_tile(sou="7") + tiles = TilesConverter.string_to_136_array(**tiles_kwargs) + win_tile = _string_to_136_tile(**win_tile_kwargs) hand = _hand(TilesConverter.to_34_array([*tiles, win_tile])) fu_details, fu = fu_calculator.calculate_fu(hand, win_tile, _get_win_group(hand, win_tile), config) @@ -193,7 +190,14 @@ def test_kanchan_fu() -> None: assert fu == 40 -def test_valued_pair_fu() -> None: +@pytest.mark.parametrize( + ("valued_tiles", "expected_fu", "expected_reason"), + [ + pytest.param([EAST], 2, FuCalculator.VALUED_PAIR, id="single"), + pytest.param([EAST, EAST], 4, FuCalculator.DOUBLE_VALUED_PAIR, id="double"), + ], +) +def test_valued_pair_fu(valued_tiles: list[int], expected_fu: int, expected_reason: str) -> None: fu_calculator = FuCalculator() config = HandConfig() @@ -201,21 +205,6 @@ def test_valued_pair_fu() -> None: win_tile = _string_to_136_tile(sou="6") hand = _hand(TilesConverter.to_34_array([*tiles, win_tile])) - valued_tiles = [EAST] - fu_details, fu = fu_calculator.calculate_fu( - hand, - win_tile, - _get_win_group(hand, win_tile), - config, - valued_tiles=valued_tiles, - ) - assert len(fu_details) == 2 - assert {"fu": 30, "reason": FuCalculator.BASE} in fu_details - assert {"fu": 2, "reason": FuCalculator.VALUED_PAIR} in fu_details - assert fu == 40 - - # double valued pair - valued_tiles = [EAST, EAST] fu_details, fu = fu_calculator.calculate_fu( hand, win_tile, @@ -225,7 +214,7 @@ def test_valued_pair_fu() -> None: ) assert len(fu_details) == 2 assert {"fu": 30, "reason": FuCalculator.BASE} in fu_details - assert {"fu": 4, "reason": FuCalculator.DOUBLE_VALUED_PAIR} in fu_details + assert {"fu": expected_fu, "reason": expected_reason} in fu_details assert fu == 40 diff --git a/tests/hand_calculating/tests_hand_dividing.py b/tests/hand_calculating/tests_hand_dividing.py index a618c2bc..2c936944 100644 --- a/tests/hand_calculating/tests_hand_dividing.py +++ b/tests/hand_calculating/tests_hand_dividing.py @@ -11,50 +11,61 @@ def _string(hand: list[list[int]]) -> list[str]: return [TilesConverter.to_one_line_string([x * 4 for x in set_item]) for set_item in hand] -def test_simple_hand_dividing() -> None: - hand = HandDivider() - - tiles_34 = TilesConverter.string_to_34_array(man="234567", sou="23455", honors="777") - result = hand.divide_hand(tiles_34) - assert len(result) == 1 - assert _string(result[0]) == ["234m", "567m", "234s", "55s", "777z"] - - -def test_second_simple_hand_dividing() -> None: - hand = HandDivider() - - tiles_34 = TilesConverter.string_to_34_array(man="123", pin="123", sou="123", honors="11222") - result = hand.divide_hand(tiles_34) - assert len(result) == 1 - assert _string(result[0]) == ["123m", "123p", "123s", "11z", "222z"] - - -def test_hand_with_pairs_dividing() -> None: - hand = HandDivider() - - tiles_34 = TilesConverter.string_to_34_array(man="23444", pin="344556", sou="333") - result = hand.divide_hand(tiles_34) +@pytest.mark.parametrize( + ("tiles_kwargs", "expected"), + [ + pytest.param( + {"man": "234567", "sou": "23455", "honors": "777"}, + ["234m", "567m", "234s", "55s", "777z"], + id="simple", + ), + pytest.param( + {"man": "123", "pin": "123", "sou": "123", "honors": "11222"}, + ["123m", "123p", "123s", "11z", "222z"], + id="simple_all_suits", + ), + pytest.param( + {"man": "23444", "pin": "344556", "sou": "333"}, + ["234m", "44m", "345p", "456p", "333s"], + id="with_pairs", + ), + pytest.param( + {"sou": "111123666789", "honors": "11"}, + ["111s", "123s", "666s", "789s", "11z"], + id="one_suit", + ), + ], +) +def test_single_hand_dividing(tiles_kwargs: dict, expected: list[str]) -> None: + tiles_34 = TilesConverter.string_to_34_array(**tiles_kwargs) + result = HandDivider.divide_hand(tiles_34) assert len(result) == 1 - assert _string(result[0]) == ["234m", "44m", "345p", "456p", "333s"] + assert _string(result[0]) == expected -def test_one_suit_hand_dividing() -> None: - hand = HandDivider() - - tiles_34 = TilesConverter.string_to_34_array(man="11122233388899") - result = hand.divide_hand(tiles_34) +@pytest.mark.parametrize( + ("tiles_kwargs", "expected_first", "expected_second"), + [ + pytest.param( + {"man": "11122233388899"}, + ["111m", "222m", "333m", "888m", "99m"], + ["123m", "123m", "123m", "888m", "99m"], + id="one_suit", + ), + pytest.param( + {"man": "112233", "pin": "99", "sou": "445566"}, + ["11m", "22m", "33m", "99p", "44s", "55s", "66s"], + ["123m", "123m", "99p", "456s", "456s"], + id="chitoitsu_like", + ), + ], +) +def test_multiple_hand_dividing(tiles_kwargs: dict, expected_first: list[str], expected_second: list[str]) -> None: + tiles_34 = TilesConverter.string_to_34_array(**tiles_kwargs) + result = HandDivider.divide_hand(tiles_34) assert len(result) == 2 - assert _string(result[0]) == ["111m", "222m", "333m", "888m", "99m"] - assert _string(result[1]) == ["123m", "123m", "123m", "888m", "99m"] - - -def test_second_one_suit_hand_dividing() -> None: - hand = HandDivider() - - tiles_34 = TilesConverter.string_to_34_array(sou="111123666789", honors="11") - result = hand.divide_hand(tiles_34) - assert len(result) == 1 - assert _string(result[0]) == ["111s", "123s", "666s", "789s", "11z"] + assert _string(result[0]) == expected_first + assert _string(result[1]) == expected_second def test_third_one_suit_hand_dividing() -> None: @@ -70,16 +81,6 @@ def test_third_one_suit_hand_dividing() -> None: assert _string(result[0]) == ["234p", "789p", "789p", "789p", "22z"] -def test_chitoitsu_like_hand_dividing() -> None: - hand = HandDivider() - - tiles_34 = TilesConverter.string_to_34_array(man="112233", pin="99", sou="445566") - result = hand.divide_hand(tiles_34) - assert len(result) == 2 - assert _string(result[0]) == ["11m", "22m", "33m", "99p", "44s", "55s", "66s"] - assert _string(result[1]) == ["123m", "123m", "99p", "456s", "456s"] - - def test_fix_not_correct_kan_handling() -> None: # Hand calculator crashed because it wasn't able to split hand diff --git a/tests/hand_calculating/tests_scores_calculation.py b/tests/hand_calculating/tests_scores_calculation.py index bd993cc6..666f2d0d 100644 --- a/tests/hand_calculating/tests_scores_calculation.py +++ b/tests/hand_calculating/tests_scores_calculation.py @@ -1,224 +1,111 @@ +import pytest + from mahjong.constants import EAST, WEST from mahjong.hand_calculating.hand_config import HandConfig, OptionalRules from mahjong.hand_calculating.scores import ScoresCalculator -def test_calculate_scores_and_ron() -> None: - hand = ScoresCalculator() +@pytest.mark.parametrize( + ("han", "fu", "expected_main"), + [ + (1, 30, 1000), + (1, 110, 3600), + (2, 30, 2000), + (3, 30, 3900), + (4, 30, 7700), + (4, 40, 8000), + (5, 0, 8000), + (6, 0, 12000), + (8, 0, 16000), + (11, 0, 24000), + (13, 0, 32000), + (26, 0, 64000), + (39, 0, 96000), + (52, 0, 128000), + (65, 0, 160000), + (78, 0, 192000), + ], +) +def test_calculate_scores_and_ron(han: int, fu: int, expected_main: int) -> None: config = HandConfig(options=OptionalRules(kazoe_limit=HandConfig.KAZOE_NO_LIMIT)) - - result = hand.calculate_scores(han=1, fu=30, config=config) - assert result["main"] == 1000 - - result = hand.calculate_scores(han=1, fu=110, config=config) - assert result["main"] == 3600 - - result = hand.calculate_scores(han=2, fu=30, config=config) - assert result["main"] == 2000 - - result = hand.calculate_scores(han=3, fu=30, config=config) - assert result["main"] == 3900 - - result = hand.calculate_scores(han=4, fu=30, config=config) - assert result["main"] == 7700 - - result = hand.calculate_scores(han=4, fu=40, config=config) - assert result["main"] == 8000 - - result = hand.calculate_scores(han=5, fu=0, config=config) - assert result["main"] == 8000 - - result = hand.calculate_scores(han=6, fu=0, config=config) - assert result["main"] == 12000 - - result = hand.calculate_scores(han=8, fu=0, config=config) - assert result["main"] == 16000 - - result = hand.calculate_scores(han=11, fu=0, config=config) - assert result["main"] == 24000 - - result = hand.calculate_scores(han=13, fu=0, config=config) - assert result["main"] == 32000 - - result = hand.calculate_scores(han=26, fu=0, config=config) - assert result["main"] == 64000 - - result = hand.calculate_scores(han=39, fu=0, config=config) - assert result["main"] == 96000 - - result = hand.calculate_scores(han=52, fu=0, config=config) - assert result["main"] == 128000 - - result = hand.calculate_scores(han=65, fu=0, config=config) - assert result["main"] == 160000 - - result = hand.calculate_scores(han=78, fu=0, config=config) - assert result["main"] == 192000 - - -def test_calculate_scores_and_ron_by_dealer() -> None: - hand = ScoresCalculator() + result = ScoresCalculator.calculate_scores(han=han, fu=fu, config=config) + assert result["main"] == expected_main + + +@pytest.mark.parametrize( + ("han", "fu", "expected_main"), + [ + (1, 30, 1500), + (2, 30, 2900), + (3, 30, 5800), + (4, 30, 11600), + (5, 0, 12000), + (6, 0, 18000), + (8, 0, 24000), + (11, 0, 36000), + (13, 0, 48000), + (26, 0, 96000), + (39, 0, 144000), + (52, 0, 192000), + (65, 0, 240000), + (78, 0, 288000), + ], +) +def test_calculate_scores_and_ron_by_dealer(han: int, fu: int, expected_main: int) -> None: config = HandConfig(player_wind=EAST, options=OptionalRules(kazoe_limit=HandConfig.KAZOE_NO_LIMIT)) - - result = hand.calculate_scores(han=1, fu=30, config=config) - assert result["main"] == 1500 - - result = hand.calculate_scores(han=2, fu=30, config=config) - assert result["main"] == 2900 - - result = hand.calculate_scores(han=3, fu=30, config=config) - assert result["main"] == 5800 - - result = hand.calculate_scores(han=4, fu=30, config=config) - assert result["main"] == 11600 - - result = hand.calculate_scores(han=5, fu=0, config=config) - assert result["main"] == 12000 - - result = hand.calculate_scores(han=6, fu=0, config=config) - assert result["main"] == 18000 - - result = hand.calculate_scores(han=8, fu=0, config=config) - assert result["main"] == 24000 - - result = hand.calculate_scores(han=11, fu=0, config=config) - assert result["main"] == 36000 - - result = hand.calculate_scores(han=13, fu=0, config=config) - assert result["main"] == 48000 - - result = hand.calculate_scores(han=26, fu=0, config=config) - assert result["main"] == 96000 - - result = hand.calculate_scores(han=39, fu=0, config=config) - assert result["main"] == 144000 - - result = hand.calculate_scores(han=52, fu=0, config=config) - assert result["main"] == 192000 - - result = hand.calculate_scores(han=65, fu=0, config=config) - assert result["main"] == 240000 - - result = hand.calculate_scores(han=78, fu=0, config=config) - assert result["main"] == 288000 - - -def test_calculate_scores_and_tsumo() -> None: - hand = ScoresCalculator() + result = ScoresCalculator.calculate_scores(han=han, fu=fu, config=config) + assert result["main"] == expected_main + + +@pytest.mark.parametrize( + ("han", "fu", "expected_main", "expected_additional"), + [ + (1, 30, 500, 300), + (3, 30, 2000, 1000), + (3, 60, 3900, 2000), + (4, 30, 3900, 2000), + (5, 0, 4000, 2000), + (6, 0, 6000, 3000), + (8, 0, 8000, 4000), + (11, 0, 12000, 6000), + (13, 0, 16000, 8000), + (26, 0, 32000, 16000), + (39, 0, 48000, 24000), + (52, 0, 64000, 32000), + (65, 0, 80000, 40000), + (78, 0, 96000, 48000), + ], +) +def test_calculate_scores_and_tsumo(han: int, fu: int, expected_main: int, expected_additional: int) -> None: config = HandConfig(is_tsumo=True, options=OptionalRules(kazoe_limit=HandConfig.KAZOE_NO_LIMIT)) - - result = hand.calculate_scores(han=1, fu=30, config=config) - assert result["main"] == 500 - assert result["additional"] == 300 - - result = hand.calculate_scores(han=3, fu=30, config=config) - assert result["main"] == 2000 - assert result["additional"] == 1000 - - result = hand.calculate_scores(han=3, fu=60, config=config) - assert result["main"] == 3900 - assert result["additional"] == 2000 - - result = hand.calculate_scores(han=4, fu=30, config=config) - assert result["main"] == 3900 - assert result["additional"] == 2000 - - result = hand.calculate_scores(han=5, fu=0, config=config) - assert result["main"] == 4000 - assert result["additional"] == 2000 - - result = hand.calculate_scores(han=6, fu=0, config=config) - assert result["main"] == 6000 - assert result["additional"] == 3000 - - result = hand.calculate_scores(han=8, fu=0, config=config) - assert result["main"] == 8000 - assert result["additional"] == 4000 - - result = hand.calculate_scores(han=11, fu=0, config=config) - assert result["main"] == 12000 - assert result["additional"] == 6000 - - result = hand.calculate_scores(han=13, fu=0, config=config) - assert result["main"] == 16000 - assert result["additional"] == 8000 - - result = hand.calculate_scores(han=26, fu=0, config=config) - assert result["main"] == 32000 - assert result["additional"] == 16000 - - result = hand.calculate_scores(han=39, fu=0, config=config) - assert result["main"] == 48000 - assert result["additional"] == 24000 - - result = hand.calculate_scores(han=52, fu=0, config=config) - assert result["main"] == 64000 - assert result["additional"] == 32000 - - result = hand.calculate_scores(han=65, fu=0, config=config) - assert result["main"] == 80000 - assert result["additional"] == 40000 - - result = hand.calculate_scores(han=78, fu=0, config=config) - assert result["main"] == 96000 - assert result["additional"] == 48000 - - -def test_calculate_scores_and_tsumo_by_dealer() -> None: - hand = ScoresCalculator() + result = ScoresCalculator.calculate_scores(han=han, fu=fu, config=config) + assert result["main"] == expected_main + assert result["additional"] == expected_additional + + +@pytest.mark.parametrize( + ("han", "fu", "expected_main", "expected_additional"), + [ + (1, 30, 500, 500), + (3, 30, 2000, 2000), + (4, 30, 3900, 3900), + (5, 0, 4000, 4000), + (6, 0, 6000, 6000), + (8, 0, 8000, 8000), + (11, 0, 12000, 12000), + (13, 0, 16000, 16000), + (26, 0, 32000, 32000), + (39, 0, 48000, 48000), + (52, 0, 64000, 64000), + (65, 0, 80000, 80000), + (78, 0, 96000, 96000), + ], +) +def test_calculate_scores_and_tsumo_by_dealer(han: int, fu: int, expected_main: int, expected_additional: int) -> None: config = HandConfig(player_wind=EAST, is_tsumo=True, options=OptionalRules(kazoe_limit=HandConfig.KAZOE_NO_LIMIT)) - - result = hand.calculate_scores(han=1, fu=30, config=config) - assert result["main"] == 500 - assert result["additional"] == 500 - - result = hand.calculate_scores(han=3, fu=30, config=config) - assert result["main"] == 2000 - assert result["additional"] == 2000 - - result = hand.calculate_scores(han=4, fu=30, config=config) - assert result["main"] == 3900 - assert result["additional"] == 3900 - - result = hand.calculate_scores(han=5, fu=0, config=config) - assert result["main"] == 4000 - assert result["additional"] == 4000 - - result = hand.calculate_scores(han=6, fu=0, config=config) - assert result["main"] == 6000 - assert result["additional"] == 6000 - - result = hand.calculate_scores(han=8, fu=0, config=config) - assert result["main"] == 8000 - assert result["additional"] == 8000 - - result = hand.calculate_scores(han=11, fu=0, config=config) - assert result["main"] == 12000 - assert result["additional"] == 12000 - - result = hand.calculate_scores(han=13, fu=0, config=config) - assert result["main"] == 16000 - assert result["additional"] == 16000 - - result = hand.calculate_scores(han=26, fu=0, config=config) - assert result["main"] == 32000 - assert result["additional"] == 32000 - - result = hand.calculate_scores(han=39, fu=0, config=config) - assert result["main"] == 48000 - assert result["additional"] == 48000 - - result = hand.calculate_scores(han=52, fu=0, config=config) - assert result["main"] == 64000 - assert result["additional"] == 64000 - - result = hand.calculate_scores(han=65, fu=0, config=config) - assert result["main"] == 80000 - assert result["additional"] == 80000 - - result = hand.calculate_scores(han=78, fu=0, config=config) - assert result["main"] == 96000 - assert result["additional"] == 96000 + result = ScoresCalculator.calculate_scores(han=han, fu=fu, config=config) + assert result["main"] == expected_main + assert result["additional"] == expected_additional def test_calculate_scores_with_bonus() -> None: @@ -261,24 +148,19 @@ def test_calculate_scores_with_bonus() -> None: assert result["total"] == 13500 -def test_kiriage_mangan() -> None: - hand = ScoresCalculator() - - config = HandConfig(options=OptionalRules(kiriage=True)) - - result = hand.calculate_scores(han=4, fu=30, config=config) - assert result["main"] == 8000 - - result = hand.calculate_scores(han=3, fu=60, config=config) - assert result["main"] == 8000 - - config = HandConfig(player_wind=EAST, options=OptionalRules(kiriage=True)) - - result = hand.calculate_scores(han=4, fu=30, config=config) - assert result["main"] == 12000 - - result = hand.calculate_scores(han=3, fu=60, config=config) - assert result["main"] == 12000 +@pytest.mark.parametrize( + ("player_wind", "han", "fu", "expected_main"), + [ + (None, 4, 30, 8000), + (None, 3, 60, 8000), + (EAST, 4, 30, 12000), + (EAST, 3, 60, 12000), + ], +) +def test_kiriage_mangan(player_wind: int | None, han: int, fu: int, expected_main: int) -> None: + 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: diff --git a/tests/hand_calculating/tests_yaku_calculation.py b/tests/hand_calculating/tests_yaku_calculation.py index 6c84bc17..5402021f 100644 --- a/tests/hand_calculating/tests_yaku_calculation.py +++ b/tests/hand_calculating/tests_yaku_calculation.py @@ -1274,7 +1274,15 @@ def test_is_agari_and_closed_kan() -> None: assert result.error is not None -def test_kazoe_settings() -> None: +@pytest.mark.parametrize( + ("kazoe_limit", "expected_cost"), + [ + pytest.param(HandConfig.KAZOE_LIMITED, 32000, id="limited"), + pytest.param(HandConfig.KAZOE_SANBAIMAN, 24000, id="sanbaiman"), + pytest.param(HandConfig.KAZOE_NO_LIMIT, 64000, id="no_limit"), + ], +) +def test_kazoe_settings(kazoe_limit: int, expected_cost: int) -> None: hand = HandCalculator() tiles = TilesConverter.string_to_136_array(man="222244466677788") @@ -1290,20 +1298,10 @@ def test_kazoe_settings() -> None: _string_to_136_tile(man="1"), ] - config = HandConfig(is_riichi=True, options=OptionalRules(kazoe_limit=HandConfig.KAZOE_LIMITED)) + config = HandConfig(is_riichi=True, options=OptionalRules(kazoe_limit=kazoe_limit)) result = hand.estimate_hand_value(tiles, win_tile, melds, dora_indicators, config) assert result.han == 28 - assert result.cost["main"] == 32000 - - config = HandConfig(is_riichi=True, options=OptionalRules(kazoe_limit=HandConfig.KAZOE_SANBAIMAN)) - result = hand.estimate_hand_value(tiles, win_tile, melds, dora_indicators, config) - assert result.han == 28 - assert result.cost["main"] == 24000 - - config = HandConfig(is_riichi=True, options=OptionalRules(kazoe_limit=HandConfig.KAZOE_NO_LIMIT)) - result = hand.estimate_hand_value(tiles, win_tile, melds, dora_indicators, config) - assert result.han == 28 - assert result.cost["main"] == 64000 + assert result.cost["main"] == expected_cost def test_open_hand_without_additional_fu() -> None: @@ -1322,7 +1320,7 @@ def test_open_hand_without_additional_fu() -> None: def test_aka_dora() -> None: hand_calculator = HandCalculator() - win_tile = TilesConverter.string_to_136_array(man="9")[0] + win_tile = _string_to_136_tile(man="9") hand_config = HandConfig(is_tsumo=True, options=OptionalRules(has_aka_dora=True)) @@ -1334,7 +1332,7 @@ def test_aka_dora() -> None: # zero red tiles = TilesConverter.string_to_136_array(sou="345", pin="456", man="12355599", has_aka_dora=True) - win_tile = TilesConverter.string_to_136_array(man="9")[0] + win_tile = _string_to_136_tile(man="9") hand_config = HandConfig(is_tsumo=True, options=OptionalRules(has_aka_dora=True)) diff --git a/tests/hand_calculating/tests_yakuman_calculation.py b/tests/hand_calculating/tests_yakuman_calculation.py index 7a84f1b7..e9c34280 100644 --- a/tests/hand_calculating/tests_yakuman_calculation.py +++ b/tests/hand_calculating/tests_yakuman_calculation.py @@ -9,26 +9,20 @@ from tests.utils_for_tests import _hand, _make_hand_config, _make_meld, _string_to_136_tile -def test_is_tenhou() -> None: - hand = HandCalculator() - - tiles = TilesConverter.string_to_136_array(sou="123444", man="234456", pin="66") - win_tile = _string_to_136_tile(sou="4") - - result = hand.estimate_hand_value(tiles, win_tile, config=_make_hand_config(is_tsumo=True, is_tenhou=True)) - assert result.error is None - assert result.han == 13 - assert result.fu == 30 - assert len(result.yaku) == 1 - - -def test_is_chiihou() -> None: +@pytest.mark.parametrize( + "config_kwargs", + [ + pytest.param({"is_tsumo": True, "is_tenhou": True}, id="tenhou"), + pytest.param({"is_tsumo": True, "is_chiihou": True}, id="chiihou"), + ], +) +def test_is_tenhou_or_chiihou(config_kwargs: dict) -> None: hand = HandCalculator() tiles = TilesConverter.string_to_136_array(sou="123444", man="234456", pin="66") win_tile = _string_to_136_tile(sou="4") - result = hand.estimate_hand_value(tiles, win_tile, config=_make_hand_config(is_tsumo=True, is_chiihou=True)) + result = hand.estimate_hand_value(tiles, win_tile, config=_make_hand_config(**config_kwargs)) assert result.error is None assert result.han == 13 assert result.fu == 30 @@ -407,7 +401,7 @@ def test_kokushi_musou_multiple_yakuman() -> None: # kokushi test tiles = TilesConverter.string_to_136_array(sou="19", pin="19", man="19", honors="12345677") - win_tile = TilesConverter.string_to_136_array(honors="1")[0] + win_tile = _string_to_136_tile(honors="1") hand_config = HandConfig(is_tsumo=True, is_tenhou=False, is_chiihou=False) @@ -448,7 +442,7 @@ def test_kokushi_musou_multiple_yakuman() -> None: # double kokushi test tiles = TilesConverter.string_to_136_array(sou="19", pin="19", man="19", honors="12345677") - win_tile = TilesConverter.string_to_136_array(honors="7")[0] + win_tile = _string_to_136_tile(honors="7") hand_config = HandConfig(is_tsumo=True, is_tenhou=False, is_chiihou=False) @@ -700,20 +694,21 @@ def test_paarenchan_no_yaku_allowed() -> None: assert result.han == 13 -def test_paarenchan() -> None: +@pytest.mark.parametrize( + ("paarenchan", "expected_han"), + [ + (1, 26), + (4, 65), + ], +) +def test_paarenchan(paarenchan: int, expected_han: int) -> None: hand = HandCalculator() tiles = TilesConverter.string_to_136_array(pin="111222777", sou="44455") win_tile = _string_to_136_tile(pin="7") - result = hand.estimate_hand_value(tiles, win_tile, config=_make_hand_config(paarenchan=1, is_tsumo=True)) - assert result.error is None - assert result.han == 26 - - tiles = TilesConverter.string_to_136_array(pin="111222777", sou="44455") - win_tile = _string_to_136_tile(pin="7") - result = hand.estimate_hand_value(tiles, win_tile, config=_make_hand_config(paarenchan=4, is_tsumo=True)) + result = hand.estimate_hand_value(tiles, win_tile, config=_make_hand_config(paarenchan=paarenchan, is_tsumo=True)) assert result.error is None - assert result.han == 65 + assert result.han == expected_han def test_sextuple_yakuman_limit() -> None: diff --git a/tests/tests_tiles_converter.py b/tests/tests_tiles_converter.py index aef2be01..676fa3c5 100644 --- a/tests/tests_tiles_converter.py +++ b/tests/tests_tiles_converter.py @@ -1,37 +1,72 @@ +import pytest + from mahjong.constants import FIVE_RED_PIN from mahjong.tile import Tile, TilesConverter +from tests.utils_for_tests import _string_to_34_tile, _string_to_136_tile def test_convert_to_one_line_string() -> None: - tiles = [0, 1, 34, 35, 36, 37, 70, 71, 72, 73, 106, 107, 108, 109, 133, 134] + tiles = TilesConverter.string_to_136_array(man="1199", pin="1199", sou="1199", honors="1177") result = TilesConverter.to_one_line_string(tiles) assert result == "1199m1199p1199s1177z" -def test_convert_to_one_line_string_with_aka_dora() -> None: - tiles = [1, 16, 13, 46, 5, 13, 24, 34, 134, 124] - result = TilesConverter.to_one_line_string(tiles, print_aka_dora=False) - assert result == "1244579m3p57z" - result = TilesConverter.to_one_line_string(tiles, print_aka_dora=True) - assert result == "1244079m3p57z" +@pytest.mark.parametrize( + ("print_aka_dora", "expected"), + [ + (False, "1244579m3p57z"), + (True, "1244079m3p57z"), + ], +) +def test_convert_to_one_line_string_with_aka_dora(print_aka_dora: bool, expected: str) -> None: + tiles = TilesConverter.string_to_136_array(man="1244079", pin="3", honors="57", has_aka_dora=True) + result = TilesConverter.to_one_line_string(tiles, print_aka_dora=print_aka_dora) + assert result == expected def test_convert_to_34_array() -> None: - tiles = [0, 34, 35, 36, 37, 70, 71, 72, 73, 106, 107, 108, 109, 134] + tiles = TilesConverter.string_to_136_array(man="199", pin="1199", sou="1199", honors="117") result = TilesConverter.to_34_array(tiles) - assert result[0] == 1 - assert result[8] == 2 - assert result[9] == 2 - assert result[17] == 2 - assert result[18] == 2 - assert result[26] == 2 - assert result[27] == 2 - assert result[33] == 1 - assert sum(result) == 14 + assert result == [ + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2, # man: 1m=1, 9m=2 + 2, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2, # pin: 1p=2, 9p=2 + 2, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2, # sou: 1s=2, 9s=2 + 2, + 0, + 0, + 0, + 0, + 0, + 1, # honors: 1z=2, 7z=1 + ] def test_convert_to_136_array() -> None: - tiles = [0, 32, 33, 36, 37, 68, 69, 72, 73, 104, 105, 108, 109, 132] + tiles = TilesConverter.string_to_136_array(man="199", pin="1199", sou="1199", honors="117") result = TilesConverter.to_34_array(tiles) result = TilesConverter.to_136_array(result) assert result == tiles @@ -43,15 +78,38 @@ def test_convert_string_to_136_array() -> None: assert tiles == [0, 32, 36, 68, 72, 104, 108, 112, 116, 120, 124, 128, 132] -def test_find_34_tile_in_136_array() -> None: - result = TilesConverter.find_34_tile_in_136_array(0, [3, 4, 5, 6]) - assert result == 3 - - result = TilesConverter.find_34_tile_in_136_array(33, [3, 4, 134, 135]) - assert result == 134 - - result = TilesConverter.find_34_tile_in_136_array(20, [3, 4, 134, 135]) - assert result is None +@pytest.mark.parametrize( + ("tile_34", "tiles_136", "expected"), + [ + pytest.param( + _string_to_34_tile(man="1"), + TilesConverter.string_to_136_array(man="1222"), + _string_to_136_tile(man="1"), + id="finds_first_match", + ), + pytest.param( + _string_to_34_tile(honors="7"), + TilesConverter.string_to_136_array(man="1", honors="77"), + _string_to_136_tile(honors="7"), + id="finds_honor_tile", + ), + pytest.param( + _string_to_34_tile(sou="3"), + TilesConverter.string_to_136_array(man="1", honors="77"), + None, + id="returns_none_for_missing_tile", + ), + pytest.param( + 34, + TilesConverter.string_to_136_array(man="1111"), + None, + id="returns_none_for_out_of_range_tile", + ), + ], +) +def test_find_34_tile_in_136_array(tile_34: int, tiles_136: list[int], expected: int | None) -> None: + result = TilesConverter.find_34_tile_in_136_array(tile_34, tiles_136) + assert result == expected def test_convert_string_with_aka_dora_to_136_array() -> None: @@ -67,8 +125,6 @@ def test_convert_string_with_aka_dora_as_zero_to_136_array() -> None: def test_one_line_string_to_136_array() -> None: initial_string = "789m456p555s11222z" tiles = TilesConverter.one_line_string_to_136_array(initial_string) - assert len(tiles) == 14 - new_string = TilesConverter.to_one_line_string(tiles) assert initial_string == new_string @@ -76,19 +132,13 @@ def test_one_line_string_to_136_array() -> None: def test_one_line_string_to_34_array() -> None: initial_string = "789m456p555s11222z" tiles = TilesConverter.one_line_string_to_34_array(initial_string) - assert len(tiles) == 34 - tiles = TilesConverter.to_136_array(tiles) new_string = TilesConverter.to_one_line_string(tiles) assert initial_string == new_string def test_tile_instantiation() -> None: - tile = Tile(value=0, is_tsumogiri=False) - assert tile.value == 0 + tile_value = _string_to_136_tile(man="1") + tile = Tile(value=tile_value, is_tsumogiri=False) + assert tile.value == tile_value assert tile.is_tsumogiri is False - - -def test_find_34_tile_in_136_array_returns_none_for_out_of_range_tile() -> None: - result = TilesConverter.find_34_tile_in_136_array(34, [0, 1, 2, 3]) - assert result is None From 950a248077b33d3a1ad62de3180a0199f6582999 Mon Sep 17 00:00:00 2001 From: Aleksei Lisikhin Date: Sat, 7 Feb 2026 20:18:09 +0700 Subject: [PATCH 2/2] refactor tests --- .../hand_calculating/tests_fu_calculation.py | 12 +++--- tests/hand_calculating/tests_hand_dividing.py | 40 ++++++------------ tests/tests_tiles_converter.py | 41 +++---------------- 3 files changed, 24 insertions(+), 69 deletions(-) diff --git a/tests/hand_calculating/tests_fu_calculation.py b/tests/hand_calculating/tests_fu_calculation.py index e95ca0d9..d3745212 100644 --- a/tests/hand_calculating/tests_fu_calculation.py +++ b/tests/hand_calculating/tests_fu_calculation.py @@ -154,18 +154,18 @@ def test_tsumo_hand_and_not_pinfu() -> None: @pytest.mark.parametrize( - ("tiles_kwargs", "win_tile_kwargs"), + ("tiles_string", "win_tile_string"), [ - pytest.param({"sou": "12456", "man": "123456", "pin": "55"}, {"sou": "3"}, id="12_wait"), - pytest.param({"sou": "34589", "man": "123456", "pin": "55"}, {"sou": "7"}, id="89_wait"), + pytest.param("123456m12456s55p", "3s", id="12_wait"), + pytest.param("123456m34589s55p", "7s", id="89_wait"), ], ) -def test_penchan_fu(tiles_kwargs: dict, win_tile_kwargs: dict) -> None: +def test_penchan_fu(tiles_string: str, win_tile_string: str) -> None: fu_calculator = FuCalculator() config = HandConfig() - tiles = TilesConverter.string_to_136_array(**tiles_kwargs) - win_tile = _string_to_136_tile(**win_tile_kwargs) + tiles = TilesConverter.one_line_string_to_136_array(tiles_string) + win_tile = TilesConverter.one_line_string_to_136_array(win_tile_string)[0] hand = _hand(TilesConverter.to_34_array([*tiles, win_tile])) fu_details, fu = fu_calculator.calculate_fu(hand, win_tile, _get_win_group(hand, win_tile), config) diff --git a/tests/hand_calculating/tests_hand_dividing.py b/tests/hand_calculating/tests_hand_dividing.py index 2c936944..9277daab 100644 --- a/tests/hand_calculating/tests_hand_dividing.py +++ b/tests/hand_calculating/tests_hand_dividing.py @@ -12,56 +12,40 @@ def _string(hand: list[list[int]]) -> list[str]: @pytest.mark.parametrize( - ("tiles_kwargs", "expected"), + ("tiles_string", "expected"), [ - pytest.param( - {"man": "234567", "sou": "23455", "honors": "777"}, - ["234m", "567m", "234s", "55s", "777z"], - id="simple", - ), - pytest.param( - {"man": "123", "pin": "123", "sou": "123", "honors": "11222"}, - ["123m", "123p", "123s", "11z", "222z"], - id="simple_all_suits", - ), - pytest.param( - {"man": "23444", "pin": "344556", "sou": "333"}, - ["234m", "44m", "345p", "456p", "333s"], - id="with_pairs", - ), - pytest.param( - {"sou": "111123666789", "honors": "11"}, - ["111s", "123s", "666s", "789s", "11z"], - id="one_suit", - ), + pytest.param("234567m23455s777z", ["234m", "567m", "234s", "55s", "777z"], id="simple"), + pytest.param("123m123p123s11222z", ["123m", "123p", "123s", "11z", "222z"], id="simple_all_suits"), + pytest.param("23444m344556p333s", ["234m", "44m", "345p", "456p", "333s"], id="with_pairs"), + pytest.param("111123666789s11z", ["111s", "123s", "666s", "789s", "11z"], id="one_suit"), ], ) -def test_single_hand_dividing(tiles_kwargs: dict, expected: list[str]) -> None: - tiles_34 = TilesConverter.string_to_34_array(**tiles_kwargs) +def test_single_hand_dividing(tiles_string: str, expected: list[str]) -> None: + tiles_34 = TilesConverter.one_line_string_to_34_array(tiles_string) result = HandDivider.divide_hand(tiles_34) assert len(result) == 1 assert _string(result[0]) == expected @pytest.mark.parametrize( - ("tiles_kwargs", "expected_first", "expected_second"), + ("tiles_string", "expected_first", "expected_second"), [ pytest.param( - {"man": "11122233388899"}, + "11122233388899m", ["111m", "222m", "333m", "888m", "99m"], ["123m", "123m", "123m", "888m", "99m"], id="one_suit", ), pytest.param( - {"man": "112233", "pin": "99", "sou": "445566"}, + "112233m99p445566s", ["11m", "22m", "33m", "99p", "44s", "55s", "66s"], ["123m", "123m", "99p", "456s", "456s"], id="chitoitsu_like", ), ], ) -def test_multiple_hand_dividing(tiles_kwargs: dict, expected_first: list[str], expected_second: list[str]) -> None: - tiles_34 = TilesConverter.string_to_34_array(**tiles_kwargs) +def test_multiple_hand_dividing(tiles_string: str, expected_first: list[str], expected_second: list[str]) -> None: + tiles_34 = TilesConverter.one_line_string_to_34_array(tiles_string) result = HandDivider.divide_hand(tiles_34) assert len(result) == 2 assert _string(result[0]) == expected_first diff --git a/tests/tests_tiles_converter.py b/tests/tests_tiles_converter.py index 676fa3c5..3873a7ea 100644 --- a/tests/tests_tiles_converter.py +++ b/tests/tests_tiles_converter.py @@ -27,42 +27,13 @@ def test_convert_to_one_line_string_with_aka_dora(print_aka_dora: bool, expected def test_convert_to_34_array() -> None: tiles = TilesConverter.string_to_136_array(man="199", pin="1199", sou="1199", honors="117") result = TilesConverter.to_34_array(tiles) + # 199m1199p1199s117z assert result == [ - 1, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 2, # man: 1m=1, 9m=2 - 2, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 2, # pin: 1p=2, 9p=2 - 2, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 2, # sou: 1s=2, 9s=2 - 2, - 0, - 0, - 0, - 0, - 0, - 1, # honors: 1z=2, 7z=1 - ] + 1, 0, 0, 0, 0, 0, 0, 0, 2, # m + 2, 0, 0, 0, 0, 0, 0, 0, 2, # p + 2, 0, 0, 0, 0, 0, 0, 0, 2, # s + 2, 0, 0, 0, 0, 0, 1, # z + ] # fmt: skip def test_convert_to_136_array() -> None: