"""麻将胡了 · 数据模型(frozen dataclass,母本 olympus/models.py 同风格)。""" from __future__ import annotations from dataclasses import dataclass, field from typing import Dict, List, Optional, Tuple @dataclass(frozen=True) class Win: """一笔奖 = 某符号的一条前缀链。""" symbol: str run: int # 链长 3/4/5(从第 1 轴起连续的轴数) counts: Tuple[int, ...] # 链内各轴的格数(百搭计入) ways: int # counts 的乘积 loc: Tuple[int, ...] # 本笔涉及的全部格位(1 基,含金色格与百搭) pay_base: float # 赔付表[符号][链长] × ways,单位 = 倍投注总额 @dataclass(frozen=True) class Rotation: """一波 = 一次消除结算。""" seq: int # 本旋内第几波(= 阶梯序号) rotation_mul: int # 本波阶梯倍率 wins: Tuple[Win, ...] base_mul: float # Σ pay_base win_mul: float # base_mul × rotation_mul converted: Tuple[Tuple[int, str], ...] # (格位1基, 原自然符号):金色就地转百搭 drops: Tuple[Tuple[int, int], ...] # (from1基, to1基) fills: Tuple[Tuple[int, str], ...] # (格位1基, 新符号) grid: Tuple[Optional[str], ...] # 本波结算+补位后的盘面(20 格短码) @dataclass(frozen=True) class Spin: """一旋。""" index: int # 0 = 基础旋(触发盘),>0 = 第 N 次免费旋转 is_fs: bool rotations: Tuple[Rotation, ...] mul: float # 本旋倍率 scatters: int # 本旋**落定盘面**上的胡数(补位产生的不算触发) fs_awarded: int = 0 # 本旋触发的赠送旋数(0 = 没触发) retrigger: bool = False @dataclass class Round: """一局 = 基础旋 + 免费旋转(可重触发),受 500x 封顶。""" box_id: int spins: List[Spin] = field(default_factory=list) raw_mul: float = 0.0 # 封顶前的总倍率 actual_mul: float = 0.0 # min(raw_mul, 500) —— 落桶/发奖用这个 capped: bool = False near_miss: bool = False # 基础旋恰好 2 个胡且第 5 轴无胡 free_spins: int = 0 # 本局发放的免费旋数 @property def has_fs(self) -> bool: return self.free_spins > 0