mahjong.utils
Utility functions for tile classification, dora counting, and hand structure analysis.
Tile classification
Functions that identify suit membership and tile properties, all operating on the 34-format tile index:
is_man(),is_pin(),is_sou(),is_honor()- suit membership checksis_terminal()- whether a tile is a terminal (1 or 9 of any suit)is_dora_indicator_for_terminal()- whether a tile is a dora indicator that points to a terminalis_sangenpai()- whether a tile is a dragon (haku, hatsu, or chun)simplify()- reduce a tile index to its rank (0-8) within its suit
Dora counting
Functions for calculating dora (bonus tiles) in a hand:
is_aka_dora()- check if a specific 136-format tile is a red fiveplus_dora()- count dora for a single tile given dora indicatorsbuild_dora_count_map()- precompute a mapping from tile type to dora countcount_dora_for_hand()- total dora in a hand using a precomputed map
Hand structure analysis
Functions that inspect sets (melds) within a hand decomposition:
is_chi(),is_pon(),is_kan(),is_pair()- classify a set by typeis_pon_or_kan()- check for triplet or quadhas_pon_or_kan_of()- search for a triplet/quad of a specific tileclassify_hand_suits()- bitmask summary of suits present in the handcontains_terminals()- whether a set includes terminal tilesfind_isolated_tile_indices()- find tiles with no adjacent neighborsis_tile_strictly_isolated()- strict isolation check (no +-2 neighbors)count_tiles_by_suits()- count tiles per suit
- mahjong.utils.is_aka_dora(tile_136, aka_enabled)[source]
Check if a tile is an aka dora (red five).
>>> from mahjong.utils import is_aka_dora >>> is_aka_dora(16, aka_enabled=True) True
>>> is_aka_dora(16, aka_enabled=False) False
- Parameters:
tile_136 (int) – tile index in 136-format
aka_enabled (bool) – whether aka dora rules are active
- Returns:
True if the tile is a red five and aka dora is enabled
- Return type:
bool
- mahjong.utils.plus_dora(tile_136, dora_indicators_136, add_aka_dora=False)[source]
Calculate the number of dora for a single tile given dora indicators.
Each dora indicator reveals which tile type is dora (the next tile in sequence). If the tile matches, the count increments once per matching indicator. Optionally includes aka dora (red five) as an additional bonus.
Indicator 0 (1m) points to 2m as dora, so tile 4 (2m) scores one dora:
>>> from mahjong.utils import plus_dora >>> plus_dora(4, [0]) 1
Two indicators each pointing to 2m as dora:
>>> plus_dora(4, [0, 1]) 2
- Parameters:
tile_136 (int) – tile index in 136-format
dora_indicators_136 (Collection[int]) – collection of dora indicator tile indices in 136-format
add_aka_dora (bool) – include aka dora (red five) bonus in the count
- Returns:
total dora count for this tile
- Return type:
int
- mahjong.utils.build_dora_count_map(dora_indicators_136)[source]
Build a mapping from tile type (34-format) to dora count for the given indicators.
Indicator 0 (first copy of 1m) points to dora 2m (index 1):
>>> from mahjong.utils import build_dora_count_map >>> build_dora_count_map([0]) {1: 1}
Two indicators each pointing to the same dora:
>>> build_dora_count_map([0, 1]) {1: 2}
- Parameters:
dora_indicators_136 (Collection[int]) – collection of dora indicator tile indices in 136-format
- Returns:
dictionary mapping 34-format tile index to dora count
- Return type:
dict[int, int]
- mahjong.utils.count_dora_for_hand(tiles_34, dora_count_map)[source]
Count total dora in a hand using a precomputed dora count map.
Three copies of 2m with one dora indicator pointing to 2m:
>>> from mahjong.utils import count_dora_for_hand, build_dora_count_map >>> tiles_34 = [0] * 34 >>> tiles_34[1] = 3 >>> dora_map = build_dora_count_map([0]) >>> count_dora_for_hand(tiles_34, dora_map) 3
- Parameters:
tiles_34 (Sequence[int]) – hand in 34-format tile count array
dora_count_map (dict[int, int]) – mapping from 34-format tile index to dora count
- Returns:
total dora count for the hand
- Return type:
int
- mahjong.utils.is_chi(item)[source]
Check if a set of tiles forms a chi (sequence of three consecutive tiles).
The indices must be in ascending order.
>>> from mahjong.utils import is_chi >>> is_chi([0, 1, 2]) True
>>> is_chi([0, 0, 0]) False
>>> is_chi([2, 1, 0]) False
- Parameters:
item (Sequence[int]) – array of tile indices in 34-format
- Returns:
True if the tiles form a chi
- Return type:
bool
- mahjong.utils.is_pon(item)[source]
Check if a set of tiles forms a pon (triplet of identical tiles).
>>> from mahjong.utils import is_pon >>> is_pon([0, 0, 0]) True
>>> is_pon([0, 1, 2]) False
- Parameters:
item (Sequence[int]) – array of tile indices in 34-format
- Returns:
True if the tiles form a pon
- Return type:
bool
- mahjong.utils.is_kan(item)[source]
Check if a set of tiles forms a kan (quad of identical tiles).
>>> from mahjong.utils import is_kan >>> is_kan([0, 0, 0, 0]) True
>>> is_kan([0, 0, 0]) False
- Parameters:
item (Sequence[int]) – array of tile indices in 34-format
- Returns:
True if the tiles form a kan
- Return type:
bool
- mahjong.utils.is_pon_or_kan(item)[source]
Check if a set of tiles forms a pon (triplet) or a kan (quad).
>>> from mahjong.utils import is_pon_or_kan >>> is_pon_or_kan([0, 0, 0]) True
>>> is_pon_or_kan([0, 0, 0, 0]) True
>>> is_pon_or_kan([0, 1, 2]) False
- Parameters:
item (Sequence[int]) – array of tile indices in 34-format
- Returns:
True if the tiles form a pon or kan
- Return type:
bool
- mahjong.utils.is_pair(item)[source]
Check if a set of tiles forms a pair (two tiles).
>>> from mahjong.utils import is_pair >>> is_pair([0, 0]) True
>>> is_pair([0, 0, 0]) False
- Parameters:
item (Sequence[int]) – array of tile indices in 34-format
- Returns:
True if the tiles form a pair
- Return type:
bool
- mahjong.utils.has_pon_or_kan_of(hand, tile)[source]
Check if hand contains a pon or kan of the specified tile.
>>> from mahjong.utils import has_pon_or_kan_of >>> has_pon_or_kan_of([[0, 0, 0], [1, 2, 3]], 0) True
>>> has_pon_or_kan_of([[0, 1, 2], [3, 3, 3]], 0) False
- Parameters:
hand (Collection[Sequence[int]]) – collection of tile sets, each a sequence of tile indices in 34-format
tile (int) – tile index in 34-format to search for
- Returns:
True if the hand contains a pon or kan of the given tile
- Return type:
bool
- mahjong.utils.classify_hand_suits(hand)[source]
Classify the tile sets in a hand by suit, returning a bitmask and honor count.
The bitmask bits are:
1for sou,2for pin,4for man.>>> from mahjong.utils import classify_hand_suits >>> classify_hand_suits([[0, 1, 2], [27, 27, 27]]) (4, 1)
- Parameters:
hand (Collection[Sequence[int]]) – collection of tile sets, each a sequence of tile indices in 34-format
- Returns:
tuple of (suit_mask, honor_count)
- Return type:
tuple[int, int]
- mahjong.utils.is_man(tile)[source]
Check if a tile belongs to the man (characters) suit.
>>> from mahjong.utils import is_man >>> is_man(0) True
>>> is_man(9) False
- Parameters:
tile (int) – tile index in 34-format
- Returns:
True if the tile is a man tile (indices 0-8)
- Return type:
bool
- mahjong.utils.is_pin(tile)[source]
Check if a tile belongs to the pin (circles) suit.
>>> from mahjong.utils import is_pin >>> is_pin(9) True
>>> is_pin(0) False
- Parameters:
tile (int) – tile index in 34-format
- Returns:
True if the tile is a pin tile (indices 9-17)
- Return type:
bool
- mahjong.utils.is_sou(tile)[source]
Check if a tile belongs to the sou (bamboo) suit.
>>> from mahjong.utils import is_sou >>> is_sou(18) True
>>> is_sou(0) False
- Parameters:
tile (int) – tile index in 34-format
- Returns:
True if the tile is a sou tile (indices 18-26)
- Return type:
bool
- mahjong.utils.is_honor(tile)[source]
Check if a tile is an honor tile (wind or dragon).
>>> from mahjong.utils import is_honor >>> is_honor(27) True
>>> is_honor(26) False
- Parameters:
tile (int) – tile index in 34-format
- Returns:
True if the tile is an honor tile (indices 27-33)
- Return type:
bool
- mahjong.utils.is_sangenpai(tile_34)[source]
Check if a tile is a dragon (sangenpai: haku, hatsu, or chun).
>>> from mahjong.utils import is_sangenpai >>> is_sangenpai(31) True
>>> is_sangenpai(27) False
- Parameters:
tile_34 (int) – tile index in 34-format
- Returns:
True if the tile is a dragon (indices 31-33)
- Return type:
bool
- mahjong.utils.is_terminal(tile)[source]
Check if a tile is a terminal (1 or 9 of any suited tile).
>>> from mahjong.utils import is_terminal >>> is_terminal(0) True
>>> is_terminal(8) True
>>> is_terminal(1) False
- Parameters:
tile (int) – tile index in 34-format
- Returns:
True if the tile is a terminal
- Return type:
bool
- mahjong.utils.is_dora_indicator_for_terminal(tile)[source]
Check if a tile is a dora indicator that points to a terminal.
The tiles with ranks 8 and 9 in each suit (indices 7, 8, 16, 17, 25, 26) are dora indicators for terminals: a rank-9 indicator wraps around to make rank 1 the dora, and a rank-8 indicator points to rank 9 as the dora.
>>> from mahjong.utils import is_dora_indicator_for_terminal >>> is_dora_indicator_for_terminal(7) True
>>> is_dora_indicator_for_terminal(0) False
- Parameters:
tile (int) – tile index in 34-format
- Returns:
True if the tile is a dora indicator for a terminal
- Return type:
bool
- mahjong.utils.contains_terminals(hand_set)[source]
Check if a set of tiles contains any terminal tiles.
>>> from mahjong.utils import contains_terminals >>> contains_terminals([0, 1, 2]) True
>>> contains_terminals([1, 2, 3]) False
- Parameters:
hand_set (Collection[int]) – collection of tile indices in 34-format
- Returns:
True if any tile in the set is a terminal
- Return type:
bool
- mahjong.utils.simplify(tile)[source]
Reduce a tile index to its rank within its suit (0-8).
>>> from mahjong.utils import simplify >>> simplify(0) 0
>>> simplify(9) 0
>>> simplify(20) 2
- Parameters:
tile (int) – tile index in 34-format
- Returns:
rank within the suit (0-8)
- Return type:
int
- mahjong.utils.find_isolated_tile_indices(hand_34)[source]
Find tiles that are isolated (absent from the hand with no adjacent neighbors).
A suited tile is isolated if neither the tile itself, its left neighbor (-1), nor its right neighbor (+1) is present in the hand. Honor tiles are isolated if they are simply not present.
>>> from mahjong.utils import find_isolated_tile_indices >>> hand_34 = [4, 0, 0, 0, 0, 0, 0, 0, 0] + [0] * 25 >>> 1 not in find_isolated_tile_indices(hand_34) True
>>> 2 in find_isolated_tile_indices(hand_34) True
- Parameters:
hand_34 (Sequence[int]) – hand in 34-format tile count array
- Returns:
list of 34-format tile indices that are isolated
- Return type:
list[int]
- mahjong.utils.is_tile_strictly_isolated(hand_34, tile_34)[source]
Check if a tile is strictly isolated (no tiles within ±2 distance).
A tile is strictly isolated if no copies (beyond the one being checked) and no neighbors at distances -2, -1, +1, +2 exist in the hand. Honor tiles only need to check for duplicates.
>>> from mahjong.utils import is_tile_strictly_isolated >>> hand_34 = [1, 0, 0, 0, 0, 0, 0, 0, 0] + [0] * 25 >>> is_tile_strictly_isolated(hand_34, 0) True
>>> hand_34[2] = 1 >>> is_tile_strictly_isolated(hand_34, 0) False
- Parameters:
hand_34 (Sequence[int]) – hand in 34-format tile count array
tile_34 (int) – tile index in 34-format to check
- Returns:
True if the tile is strictly isolated
- Return type:
bool
- class mahjong.utils.SuitCount[source]
Bases:
TypedDictPer-suit tile count entry returned by
count_tiles_by_suits().- Parameters:
count – number of tiles in this suit
name – suit name (
"sou","man","pin", or"honor")function – predicate that tests whether a 34-format tile index belongs to this suit
- mahjong.utils.count_tiles_by_suits(tiles_34)[source]
Separate tiles by suit and count them.
>>> from mahjong.utils import count_tiles_by_suits >>> tiles_34 = [0] * 34 >>> tiles_34[0] = 3 >>> tiles_34[27] = 2 >>> result = count_tiles_by_suits(tiles_34) >>> [(s["name"], s["count"]) for s in result] [('sou', 0), ('man', 3), ('pin', 0), ('honor', 2)]