from __future__ import annotations from typing import Iterable, List, Tuple SUITS = ("wan", "tong", "suo") WINDS = ("east", "south", "west", "north") DRAGONS = ("red", "green", "white") FLOWERS = ( "plum", "orchid", "chrysanthemum", "bamboo", "spring", "summer", "autumn", "winter", ) # 34 suited + honors, plus 8 flowers = 42 TILE_TYPES: List[str] = [] for suit in SUITS: for rank in range(1, 10): TILE_TYPES.append(f"{suit}_{rank}") for wind in WINDS: TILE_TYPES.append(f"wind_{wind}") for dragon in DRAGONS: TILE_TYPES.append(f"dragon_{dragon}") for flower in FLOWERS: TILE_TYPES.append(f"flower_{flower}") SUITED_RANGE = range(0, 27) HONOR_RANGE = range(27, 34) FLOWER_RANGE = range(34, 42) def build_wall(rng) -> List[int]: wall: List[int] = [] for tile_id in range(34): wall.extend([tile_id] * 4) for tile_id in FLOWER_RANGE: wall.append(tile_id) rng.shuffle(wall) return wall def is_flower(tile_id: int) -> bool: return tile_id in FLOWER_RANGE def tile_name(tile_id: int) -> str: return TILE_TYPES[tile_id] def counts_from_tiles(tiles: Iterable[int], size: int = 42) -> List[int]: counts = [0] * size for tile_id in tiles: counts[tile_id] += 1 return counts def split_suit_index(tile_id: int) -> Tuple[int, int] | None: if tile_id not in SUITED_RANGE: return None suit = tile_id // 9 rank = tile_id % 9 return suit, rank