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.
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/MANIFEST.in +1 -0
- {mahjong-2.0.0.dev2/mahjong.egg-info → mahjong-2.2.0.dev1}/PKG-INFO +13 -34
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/README.md +6 -32
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/__init__.py +25 -6
- mahjong-2.2.0.dev1/mahjong/hand_calculating/__init__.py +23 -0
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/divider.py +18 -17
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/fu.py +1 -1
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/hand.py +226 -45
- mahjong-2.2.0.dev1/mahjong/hand_calculating/hand_config.py +345 -0
- mahjong-2.2.0.dev1/mahjong/hand_calculating/hand_response.py +125 -0
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/scores.py +133 -41
- mahjong-2.2.0.dev1/mahjong/hand_calculating/yaku.py +87 -0
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_config.py +110 -55
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/__init__.py +12 -0
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/aka_dora.py +3 -3
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/chankan.py +2 -4
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/chantai.py +4 -2
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/chiitoitsu.py +2 -3
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/chinitsu.py +2 -3
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/chun.py +2 -3
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/daburu_open_riichi.py +2 -4
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/daburu_riichi.py +2 -4
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/dora.py +4 -0
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/haitei.py +2 -4
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/haku.py +2 -3
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/hatsu.py +2 -3
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/honitsu.py +2 -3
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/honroto.py +2 -3
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/houtei.py +2 -4
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/iipeiko.py +2 -3
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/ippatsu.py +2 -4
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/ittsu.py +2 -3
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/junchan.py +4 -3
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/nagashi_mangan.py +2 -4
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/open_riichi.py +3 -0
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/pinfu.py +2 -3
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/renhou.py +2 -4
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/riichi.py +3 -0
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/rinshan.py +2 -4
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/round_wind_east.py +8 -3
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/round_wind_north.py +8 -3
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/round_wind_south.py +8 -3
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/round_wind_west.py +8 -3
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/ryanpeiko.py +2 -3
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/sanankou.py +11 -4
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/sankantsu.py +8 -3
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/sanshoku.py +2 -3
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/sanshoku_douko.py +2 -3
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/seat_wind_east.py +8 -3
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/seat_wind_north.py +8 -3
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/seat_wind_south.py +8 -3
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/seat_wind_west.py +8 -3
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/shosangen.py +2 -3
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/tanyao.py +2 -3
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/toitoi.py +2 -3
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/tsumo.py +2 -4
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/ura_dora.py +4 -0
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/__init__.py +9 -0
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/chiihou.py +3 -0
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/chinroto.py +3 -5
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/chuuren_poutou.py +2 -3
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/daburu_chuuren_poutou.py +3 -1
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/daburu_kokushi.py +3 -1
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/daichisei.py +2 -3
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/daisangen.py +2 -3
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/daisharin.py +18 -4
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/daisuushi.py +2 -3
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/kokushi.py +11 -3
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/paarenchan.py +4 -4
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/renhou_yakuman.py +2 -4
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/ryuisou.py +2 -3
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/sashikomi.py +2 -4
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/shosuushi.py +2 -3
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/suuankou.py +9 -3
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/suuankou_tanki.py +3 -0
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/suukantsu.py +8 -3
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/tenhou.py +2 -4
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/hand_calculating/yaku_list/yakuman/tsuisou.py +2 -8
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/meld.py +52 -3
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/shanten.py +57 -21
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/tile.py +2 -0
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/utils.py +28 -4
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1/mahjong.egg-info}/PKG-INFO +13 -34
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/pyproject.toml +35 -19
- mahjong-2.0.0.dev2/mahjong/hand_calculating/__init__.py +0 -0
- mahjong-2.0.0.dev2/mahjong/hand_calculating/hand_config.py +0 -146
- mahjong-2.0.0.dev2/mahjong/hand_calculating/hand_response.py +0 -54
- mahjong-2.0.0.dev2/mahjong/hand_calculating/yaku.py +0 -27
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/LICENSE.txt +0 -0
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/agari.py +0 -0
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/constants.py +0 -0
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong/py.typed +0 -0
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong.egg-info/SOURCES.txt +0 -0
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong.egg-info/dependency_links.txt +0 -0
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/mahjong.egg-info/top_level.txt +0 -0
- {mahjong-2.0.0.dev2 → mahjong-2.2.0.dev1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mahjong
|
|
3
|
-
Version: 2.
|
|
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
|
[](https://github.com/MahjongRepository/mahjong/actions/workflows/lint_and_test.yml)
|
|
32
37
|
[](https://mahjongrepository.github.io/mahjong)
|
|
33
38
|
|
|
34
|
-
|
|
39
|
+
Japanese riichi mahjong hand calculator library.
|
|
35
40
|
|
|
36
|
-
|
|
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
|
|
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
|
-
##
|
|
58
|
+
## Contributing
|
|
54
59
|
|
|
55
|
-
|
|
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
|
[](https://github.com/MahjongRepository/mahjong/actions/workflows/lint_and_test.yml)
|
|
8
8
|
[](https://mahjongrepository.github.io/mahjong)
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
Japanese riichi mahjong hand calculator library.
|
|
11
11
|
|
|
12
|
-
|
|
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
|
|
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
|
-
##
|
|
29
|
+
## Contributing
|
|
30
30
|
|
|
31
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
171
|
-
|
|
172
|
-
|
|
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
|
-
|
|
177
|
-
|
|
178
|
-
|
|
175
|
+
num_pair = sum(block.ty == _BlockType.PAIR for block in all_blocks)
|
|
176
|
+
if num_pair != 1:
|
|
177
|
+
continue
|
|
179
178
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
179
|
+
all_blocks.extend(melds)
|
|
180
|
+
if len(all_blocks) != 5:
|
|
181
|
+
continue
|
|
183
182
|
|
|
184
|
-
|
|
185
|
-
|
|
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,
|
|
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
|
"""
|