mahjong 2.0.0.dev2__tar.gz → 2.2.0.dev1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/MANIFEST.in +1 -0
  2. {mahjong-2.0.0.dev2/mahjong.egg-info → mahjong-2.2.0.dev1}/PKG-INFO +13 -34
  3. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/README.md +6 -32
  4. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/__init__.py +25 -6
  5. mahjong-2.2.0.dev1/mahjong/hand_calculating/__init__.py +23 -0
  6. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/divider.py +18 -17
  7. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/fu.py +1 -1
  8. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/hand.py +226 -45
  9. mahjong-2.2.0.dev1/mahjong/hand_calculating/hand_config.py +345 -0
  10. mahjong-2.2.0.dev1/mahjong/hand_calculating/hand_response.py +125 -0
  11. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/scores.py +133 -41
  12. mahjong-2.2.0.dev1/mahjong/hand_calculating/yaku.py +87 -0
  13. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_config.py +110 -55
  14. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/__init__.py +12 -0
  15. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/aka_dora.py +3 -3
  16. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/chankan.py +2 -4
  17. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/chantai.py +4 -2
  18. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/chiitoitsu.py +2 -3
  19. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/chinitsu.py +2 -3
  20. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/chun.py +2 -3
  21. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/daburu_open_riichi.py +2 -4
  22. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/daburu_riichi.py +2 -4
  23. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/dora.py +4 -0
  24. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/haitei.py +2 -4
  25. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/haku.py +2 -3
  26. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/hatsu.py +2 -3
  27. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/honitsu.py +2 -3
  28. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/honroto.py +2 -3
  29. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/houtei.py +2 -4
  30. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/iipeiko.py +2 -3
  31. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/ippatsu.py +2 -4
  32. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/ittsu.py +2 -3
  33. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/junchan.py +4 -3
  34. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/nagashi_mangan.py +2 -4
  35. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/open_riichi.py +3 -0
  36. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/pinfu.py +2 -3
  37. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/renhou.py +2 -4
  38. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/riichi.py +3 -0
  39. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/rinshan.py +2 -4
  40. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/round_wind_east.py +8 -3
  41. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/round_wind_north.py +8 -3
  42. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/round_wind_south.py +8 -3
  43. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/round_wind_west.py +8 -3
  44. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/ryanpeiko.py +2 -3
  45. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/sanankou.py +11 -4
  46. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/sankantsu.py +8 -3
  47. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/sanshoku.py +2 -3
  48. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/sanshoku_douko.py +2 -3
  49. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/seat_wind_east.py +8 -3
  50. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/seat_wind_north.py +8 -3
  51. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/seat_wind_south.py +8 -3
  52. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/seat_wind_west.py +8 -3
  53. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/shosangen.py +2 -3
  54. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/tanyao.py +2 -3
  55. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/toitoi.py +2 -3
  56. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/tsumo.py +2 -4
  57. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/ura_dora.py +4 -0
  58. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/__init__.py +9 -0
  59. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/chiihou.py +3 -0
  60. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/chinroto.py +3 -5
  61. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/chuuren_poutou.py +2 -3
  62. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/daburu_chuuren_poutou.py +3 -1
  63. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/daburu_kokushi.py +3 -1
  64. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/daichisei.py +2 -3
  65. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/daisangen.py +2 -3
  66. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/daisharin.py +18 -4
  67. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/daisuushi.py +2 -3
  68. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/kokushi.py +11 -3
  69. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/paarenchan.py +4 -4
  70. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/renhou_yakuman.py +2 -4
  71. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/ryuisou.py +2 -3
  72. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/sashikomi.py +2 -4
  73. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/shosuushi.py +2 -3
  74. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/suuankou.py +9 -3
  75. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/suuankou_tanki.py +3 -0
  76. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/suukantsu.py +8 -3
  77. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/tenhou.py +2 -4
  78. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/tsuisou.py +2 -8
  79. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/meld.py +52 -3
  80. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/shanten.py +57 -21
  81. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/tile.py +2 -0
  82. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/utils.py +28 -4
  83. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1/mahjong.egg-info}/PKG-INFO +13 -34
  84. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/pyproject.toml +35 -19
  85. mahjong-2.0.0.dev2/mahjong/hand_calculating/__init__.py +0 -0
  86. mahjong-2.0.0.dev2/mahjong/hand_calculating/hand_config.py +0 -146
  87. mahjong-2.0.0.dev2/mahjong/hand_calculating/hand_response.py +0 -54
  88. mahjong-2.0.0.dev2/mahjong/hand_calculating/yaku.py +0 -27
  89. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/LICENSE.txt +0 -0
  90. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/agari.py +0 -0
  91. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/constants.py +0 -0
  92. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/py.typed +0 -0
  93. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong.egg-info/SOURCES.txt +0 -0
  94. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong.egg-info/dependency_links.txt +0 -0
  95. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong.egg-info/top_level.txt +0 -0
  96. {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/setup.cfg +0 -0
@@ -1,2 +1,3 @@
1
1
  prune examples
2
+ prune scripts
2
3
  prune tests
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mahjong
3
- Version: 2.0.0.dev2
3
+ Version: 2.2.0.dev1
4
4
  Summary: Mahjong hands calculation
5
5
  Author-email: Alexey Lisikhin <alexey@nihisil.com>
6
6
  Maintainer: Apricot S.
@@ -9,14 +9,19 @@ Project-URL: Homepage, https://github.com/MahjongRepository/mahjong
9
9
  Project-URL: Documentation, https://mahjongrepository.github.io/mahjong
10
10
  Project-URL: Wiki, https://github.com/MahjongRepository/mahjong/wiki
11
11
  Classifier: Development Status :: 5 - Production/Stable
12
- Classifier: Environment :: Console
13
12
  Classifier: Intended Audience :: Developers
14
13
  Classifier: Programming Language :: Python
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3 :: Only
15
16
  Classifier: Programming Language :: Python :: 3.10
16
17
  Classifier: Programming Language :: Python :: 3.11
17
18
  Classifier: Programming Language :: Python :: 3.12
18
19
  Classifier: Programming Language :: Python :: 3.13
19
20
  Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Programming Language :: Python :: 3.15
22
+ Classifier: Topic :: Games/Entertainment :: Board Games
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Classifier: Typing :: Typed
20
25
  Requires-Python: >=3.10
21
26
  Description-Content-Type: text/markdown
22
27
  License-File: LICENSE.txt
@@ -31,11 +36,11 @@ Dynamic: license-file
31
36
  [![Linters and tests](https://github.com/MahjongRepository/mahjong/actions/workflows/lint_and_test.yml/badge.svg)](https://github.com/MahjongRepository/mahjong/actions/workflows/lint_and_test.yml)
32
37
  [![API Docs](https://img.shields.io/badge/api-master-yellow.svg)](https://mahjongrepository.github.io/mahjong)
33
38
 
34
- This library can calculate hand cost (han, fu with details, yaku, and scores) for riichi mahjong (Japanese version).
39
+ Japanese riichi mahjong hand calculator library.
35
40
 
36
- Also calculating of shanten is supported.
41
+ Compute hand cost (han, fu, yaku, and scores), validate winning hands, and calculate shanten (minimum tiles to tenpai).
37
42
 
38
- The code was validated on tenhou.net phoenix replays in total on **26,148,038 hands**.
43
+ The code was validated on tenhou.net phoenix replays: **26,148,050** four-player and **8,406,421** three-player winning hands.
39
44
 
40
45
  So, we can say that our hand calculator works the same way that tenhou.net hand calculation.
41
46
 
@@ -50,36 +55,10 @@ So, we can say that our hand calculator works the same way that tenhou.net hand
50
55
  pip install mahjong
51
56
  ```
52
57
 
53
- ## Local development setup
58
+ ## Contributing
54
59
 
55
- To set up the project locally for development:
56
-
57
- 1. Clone the repository:
58
-
59
- ```bash
60
- git clone https://github.com/MahjongRepository/mahjong.git
61
- cd mahjong
62
- ```
63
-
64
- 2. Setup env using [uv](https://github.com/astral-sh/uv):
65
-
66
- ```bash
67
- uv sync
68
- ```
69
-
70
- 3. Run tests to verify setup:
71
-
72
- ```bash
73
- make tests
74
- # Or directly:
75
- uv run pytest
76
- ```
77
-
78
- 4. Run full checks before committing:
79
-
80
- ```bash
81
- make check # Runs format, lint, and tests
82
- ```
60
+ For guidance on setting up a development environment and contributing to mahjong,
61
+ see [Contributing to mahjong](CONTRIBUTING.md).
83
62
 
84
63
  ## License
85
64
 
@@ -7,11 +7,11 @@
7
7
  [![Linters and tests](https://github.com/MahjongRepository/mahjong/actions/workflows/lint_and_test.yml/badge.svg)](https://github.com/MahjongRepository/mahjong/actions/workflows/lint_and_test.yml)
8
8
  [![API Docs](https://img.shields.io/badge/api-master-yellow.svg)](https://mahjongrepository.github.io/mahjong)
9
9
 
10
- This library can calculate hand cost (han, fu with details, yaku, and scores) for riichi mahjong (Japanese version).
10
+ Japanese riichi mahjong hand calculator library.
11
11
 
12
- Also calculating of shanten is supported.
12
+ Compute hand cost (han, fu, yaku, and scores), validate winning hands, and calculate shanten (minimum tiles to tenpai).
13
13
 
14
- The code was validated on tenhou.net phoenix replays in total on **26,148,038 hands**.
14
+ The code was validated on tenhou.net phoenix replays: **26,148,050** four-player and **8,406,421** three-player winning hands.
15
15
 
16
16
  So, we can say that our hand calculator works the same way that tenhou.net hand calculation.
17
17
 
@@ -26,36 +26,10 @@ So, we can say that our hand calculator works the same way that tenhou.net hand
26
26
  pip install mahjong
27
27
  ```
28
28
 
29
- ## Local development setup
29
+ ## Contributing
30
30
 
31
- To set up the project locally for development:
32
-
33
- 1. Clone the repository:
34
-
35
- ```bash
36
- git clone https://github.com/MahjongRepository/mahjong.git
37
- cd mahjong
38
- ```
39
-
40
- 2. Setup env using [uv](https://github.com/astral-sh/uv):
41
-
42
- ```bash
43
- uv sync
44
- ```
45
-
46
- 3. Run tests to verify setup:
47
-
48
- ```bash
49
- make tests
50
- # Or directly:
51
- uv run pytest
52
- ```
53
-
54
- 4. Run full checks before committing:
55
-
56
- ```bash
57
- make check # Runs format, lint, and tests
58
- ```
31
+ For guidance on setting up a development environment and contributing to mahjong,
32
+ see [Contributing to mahjong](CONTRIBUTING.md).
59
33
 
60
34
  ## License
61
35
 
@@ -1,5 +1,27 @@
1
1
  """
2
- _summary_.
2
+ Japanese riichi mahjong hand calculator library.
3
+
4
+ Compute hand cost (han, fu, yaku, and scores), validate winning hands, and calculate shanten (minimum tiles to tenpai).
5
+
6
+ .. rubric:: Main entry points
7
+
8
+ * :class:`~mahjong.hand_calculating.hand.HandCalculator` — estimate hand value (han, fu, cost,
9
+ and yaku list) given tiles, win tile, melds, and dora indicators.
10
+ * :class:`~mahjong.agari.Agari` — check whether tiles form a complete (winning) hand structure.
11
+ * :class:`~mahjong.shanten.Shanten` — calculate how many tiles away a hand is from winning.
12
+
13
+ .. rubric:: Tile and meld utilities
14
+
15
+ * :class:`~mahjong.tile.TilesConverter` — convert between mpsz-notation strings, 34-format count
16
+ arrays, and 136-format tile index lists.
17
+ * :class:`~mahjong.meld.Meld` — represent declared melds (chi, pon, kan).
18
+
19
+ .. rubric:: Configuration and results
20
+
21
+ * :class:`~mahjong.hand_calculating.hand_config.HandConfig` — configure win conditions (tsumo/ron),
22
+ wind context, and optional rule variants (open tanyao, aka dora, double yakuman, etc.).
23
+ * :class:`~mahjong.hand_calculating.hand_response.HandResponse` — structured result containing
24
+ han, fu, fu details, cost breakdown, and yaku list.
3
25
 
4
26
  Tile representation
5
27
  ###################
@@ -30,11 +52,8 @@ Honors (`z`) use ranks to distinguish individual tiles:
30
52
  * `6` - Green dragon (發, hatsu)
31
53
  * `7` - Red dragon (中, chun)
32
54
 
33
- Multiple tiles may be concatenated without separators to form a string that represents a collection. For example:
34
-
35
- ``123m406p789s11555z``
36
-
37
- is equivalent to ``1m2m3m4p0p6p7s8s9s1z1z5z5z5z``.
55
+ Multiple tiles may be concatenated without separators to form a string that represents a collection
56
+ (e.g., ``123m406p789s11555z`` is equivalent to ``1m2m3m4p0p6p7s8s9s1z1z5z5z5z``).
38
57
 
39
58
  34-format
40
59
  ---------
@@ -0,0 +1,23 @@
1
+ """
2
+ Hand value calculation pipeline.
3
+
4
+ This subpackage evaluates a winning hand and produces its han, fu, yaku list,
5
+ and payment amounts. The calculation flows through several stages:
6
+
7
+ 1. :class:`~mahjong.hand_calculating.divider.HandDivider` decomposes the hand into all
8
+ valid block combinations (4 melds + 1 pair, or 7 pairs for chiitoitsu).
9
+ 2. :class:`~mahjong.hand_calculating.yaku.Yaku` subclasses test each decomposition for
10
+ matching yaku patterns (e.g., tanyao, pinfu, honitsu).
11
+ 3. :class:`~mahjong.hand_calculating.fu.FuCalculator` computes minipoints from the meld
12
+ structure, wait type, pair, and winning method.
13
+ 4. :class:`~mahjong.hand_calculating.scores.ScoresCalculator` converts han and fu into
14
+ payment amounts, accounting for honba and kyoutaku bonuses.
15
+
16
+ :class:`~mahjong.hand_calculating.hand.HandCalculator` orchestrates the full pipeline and
17
+ returns a :class:`~mahjong.hand_calculating.hand_response.HandResponse` with the
18
+ highest-scoring result.
19
+
20
+ Behavior is controlled by :class:`~mahjong.hand_calculating.hand_config.HandConfig`, which
21
+ specifies win conditions (tsumo/ron), wind context, and optional rule variants such as
22
+ open tanyao, aka dora, and double yakuman.
23
+ """
@@ -2,6 +2,7 @@ from collections.abc import Collection, Sequence
2
2
  from dataclasses import dataclass
3
3
  from enum import IntEnum
4
4
  from functools import lru_cache
5
+ from itertools import product
5
6
  from typing import Literal
6
7
 
7
8
  from mahjong.meld import Meld
@@ -21,7 +22,7 @@ class _Block:
21
22
 
22
23
  @classmethod
23
24
  def from_meld(cls, meld: Meld) -> "_Block":
24
- tile_34 = meld.tiles_34[0]
25
+ tile_34 = meld._sorted_tiles_34[0] # noqa: SLF001
25
26
  match meld.type:
26
27
  case Meld.CHI:
27
28
  return cls(tile_34, _BlockType.SEQUENCE)
@@ -160,29 +161,27 @@ class HandDivider:
160
161
  man_combinations = HandDivider._decompose_single_color_hand(hand[0:9], 0)
161
162
  pin_combinations = HandDivider._decompose_single_color_hand(hand[9:18], 9)
162
163
  sou_combinations = HandDivider._decompose_single_color_hand(hand[18:27], 18)
163
- honors = HandDivider._decompose_honors_hand(hand[27:34])
164
+ honors_combinations = HandDivider._decompose_honors_hand(hand[27:34])
164
165
 
165
166
  combinations: list[list[_Block]] = []
166
167
 
167
168
  if not melds and (chiitoitsu := HandDivider._decompose_chiitoitsu(hand)):
168
169
  combinations.append(chiitoitsu)
169
170
 
170
- for man in man_combinations:
171
- for pin in pin_combinations:
172
- for sou in sou_combinations:
173
- # For invalid suits, *_combinations will be [] and the loop will not be executed.
174
- all_blocks = [*man, *pin, *sou, *honors]
171
+ # For invalid suits or honors, *_combinations will be [] and the loop will not be executed.
172
+ for man, pin, sou, honors in product(man_combinations, pin_combinations, sou_combinations, honors_combinations):
173
+ all_blocks = [*man, *pin, *sou, *honors]
175
174
 
176
- num_pair = sum(block.ty == _BlockType.PAIR for block in all_blocks)
177
- if num_pair != 1:
178
- continue
175
+ num_pair = sum(block.ty == _BlockType.PAIR for block in all_blocks)
176
+ if num_pair != 1:
177
+ continue
179
178
 
180
- all_blocks.extend(melds)
181
- if len(all_blocks) != 5:
182
- continue
179
+ all_blocks.extend(melds)
180
+ if len(all_blocks) != 5:
181
+ continue
183
182
 
184
- all_blocks.sort()
185
- combinations.append(all_blocks)
183
+ all_blocks.sort()
184
+ combinations.append(all_blocks)
186
185
 
187
186
  combinations.sort()
188
187
  return tuple(tuple(blocks) for blocks in combinations)
@@ -282,7 +281,9 @@ class HandDivider:
282
281
  return combinations
283
282
 
284
283
  @staticmethod
285
- def _decompose_honors_hand(honors_hand: list[int]) -> list[_Block]:
284
+ def _decompose_honors_hand(honors_hand: list[int]) -> list[list[_Block]]:
285
+ # If there is no honor tile, returns [[]].
286
+ # If the honors cannot be decomposed, returns [].
286
287
  has_pair = False
287
288
  blocks: list[_Block] = []
288
289
  for i, count in enumerate(honors_hand):
@@ -299,4 +300,4 @@ class HandDivider:
299
300
  case _:
300
301
  return []
301
302
 
302
- return blocks
303
+ return [blocks]
@@ -169,7 +169,7 @@ class FuCalculator:
169
169
  as tile indices in 34-format
170
170
  :param config: hand configuration with win method and optional rule settings
171
171
  :param valued_tiles: tile indices in 34-format for tiles that grant pair fu
172
- (dragons, player wind, round wind); pass the same index twice for double-valued
172
+ (dragons, seat wind, round wind); pass the same index twice for double-valued
173
173
  :param melds: declared melds (chi, pon, kan)
174
174
  :return: tuple of (fu component list, total fu rounded up to nearest 10)
175
175
  """