Skip to content

Commit 580bbe0

Browse files
authored
docs: add meld doc (#229)
* docs: add meld doc * address pr comments * address pr comments
1 parent 76cf6f1 commit 580bbe0

File tree

2 files changed

+97
-3
lines changed

2 files changed

+97
-3
lines changed

mahjong/meld.py

Lines changed: 97 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,68 @@
55

66

77
class Meld:
8+
"""
9+
Representation of a declared meld (called or concealed tile group).
10+
11+
A meld is a set of tiles that a player has declared, either by calling another
12+
player's discard (open meld) or by declaring a concealed kan. Meld types follow
13+
standard Japanese mahjong terminology:
14+
15+
- **Chi** — a sequence of three consecutive suited tiles, called from the player to the left.
16+
- **Pon** — a triplet of identical tiles, called from any player.
17+
- **Kan** — a quad of identical tiles (open, closed, or extended).
18+
- **Shouminkan** — an added kan (extending an existing pon with the fourth tile).
19+
- **Nuki** — a special declaration used in three-player mahjong (north wind extraction).
20+
21+
Create an open chi meld:
22+
23+
>>> from mahjong.meld import Meld
24+
>>> from mahjong.tile import TilesConverter
25+
>>> tiles = TilesConverter.string_to_136_array(man="123")
26+
>>> meld = Meld(meld_type=Meld.CHI, tiles=tiles)
27+
>>> meld.type
28+
'chi'
29+
>>> meld.tiles
30+
(0, 4, 8)
31+
32+
Create a closed kan:
33+
34+
>>> tiles = TilesConverter.string_to_136_array(man="1111")
35+
>>> meld = Meld(meld_type=Meld.KAN, tiles=tiles, opened=False)
36+
>>> meld.opened
37+
False
38+
39+
:ivar type: one of the meld type constants (CHI, PON, KAN, SHOUMINKAN, NUKI)
40+
:vartype type: str | None
41+
:ivar tiles: tile indices in 136 format, stored as an immutable tuple
42+
:vartype tiles: tuple[int, ...]
43+
:ivar opened: True for open melds (called from another player), False for closed kan
44+
:vartype opened: bool
45+
:ivar called_tile: the specific tile index (136 format) that was called to form this meld
46+
:vartype called_tile: int | None
47+
:ivar who: seat index (0-3) of the player who declared the meld
48+
:vartype who: int | None
49+
:ivar from_who: seat index (0-3) of the player who discarded the called tile
50+
:vartype from_who: int | None
51+
"""
52+
853
CHI = "chi"
54+
"""Chi (sequence) meld type — three consecutive suited tiles."""
55+
956
PON = "pon"
57+
"""Pon (triplet) meld type — three identical tiles."""
58+
1059
KAN = "kan"
60+
"""Kan (quad) meld type — four identical tiles."""
61+
1162
SHOUMINKAN = "shouminkan"
63+
"""Shouminkan (added kan) meld type — extending a pon with the fourth tile."""
64+
1265
NUKI = "nuki"
66+
"""Nuki (north wind extraction) meld type, at the moment is not used in the library."""
1367

1468
type: str | None
1569
tiles: tuple[int, ...]
16-
# we need it to distinguish opened and closed kan
1770
opened: bool
1871
called_tile: int | None
1972
who: int | None
@@ -28,6 +81,25 @@ def __init__(
2881
who: int | None = None,
2982
from_who: int | None = None,
3083
) -> None:
84+
"""
85+
Initialize a meld.
86+
87+
>>> from mahjong.meld import Meld
88+
>>> from mahjong.tile import TilesConverter
89+
>>> tiles = TilesConverter.string_to_136_array(man="111")
90+
>>> meld = Meld(meld_type=Meld.PON, tiles=tiles, who=0, from_who=2)
91+
>>> meld.type
92+
'pon'
93+
>>> meld.who
94+
0
95+
96+
:param meld_type: one of the meld type constants (CHI, PON, KAN, SHOUMINKAN, NUKI)
97+
:param tiles: tile indices in 136 format
98+
:param opened: True for open melds (called from another player), False for closed kan
99+
:param called_tile: the specific tile index (136 format) that was called to form this meld
100+
:param who: seat index (0-3) of the player who declared the meld
101+
:param from_who: seat index (0-3) of the player who discarded the called tile
102+
"""
31103
self.type = meld_type
32104
self.tiles = tuple(tiles) if tiles else ()
33105
self.opened = opened
@@ -36,17 +108,40 @@ def __init__(
36108
self.from_who = from_who
37109

38110
def __setattr__(self, name: str, value: object) -> None:
111+
"""Invalidate the tiles_34 cache when tiles are reassigned."""
39112
super().__setattr__(name, value)
40113
if name == "tiles" and "tiles_34" in self.__dict__:
41114
del self.__dict__["tiles_34"]
42115

43116
def __str__(self) -> str:
117+
"""
118+
Return a human-readable string with meld type and tiles.
119+
120+
>>> from mahjong.meld import Meld
121+
>>> from mahjong.tile import TilesConverter
122+
>>> tiles = TilesConverter.string_to_136_array(man="123")
123+
>>> meld = Meld(meld_type=Meld.CHI, tiles=tiles)
124+
>>> str(meld)
125+
'Type: chi, Tiles: 123m (0, 4, 8)'
126+
"""
44127
return f"Type: {self.type}, Tiles: {TilesConverter.to_one_line_string(self.tiles)} {self.tiles}"
45128

46-
# for calls in array
47129
def __repr__(self) -> str:
130+
"""Return the same representation as __str__."""
48131
return self.__str__()
49132

50133
@cached_property
51134
def tiles_34(self) -> list[int]:
135+
"""
136+
Convert the meld's 136-format tile indices to 34-format tile indices.
137+
138+
>>> from mahjong.meld import Meld
139+
>>> from mahjong.tile import TilesConverter
140+
>>> tiles = TilesConverter.string_to_136_array(man="111")
141+
>>> meld = Meld(meld_type=Meld.PON, tiles=tiles)
142+
>>> meld.tiles_34
143+
[0, 0, 0]
144+
145+
:return: list of tile indices in 34-format
146+
"""
52147
return [x // 4 for x in self.tiles]

pyproject.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,6 @@ convention = "pep257"
116116
# temporary
117117
"./mahjong/__init__.py" = ["D"]
118118
"./mahjong/constants.py" = ["D"]
119-
"./mahjong/meld.py" = ["D"]
120119
"./mahjong/tile.py" = ["D"]
121120
"./mahjong/utils.py" = ["D"]
122121
"./mahjong/hand_calculating/__init__.py" = ["D"]

0 commit comments

Comments
 (0)