"""周易六爻起卦算法。 每爻由三枚铜钱决定: 正面 (字, face=3) — 两正一反为多,视为阳 反面 (背, face=2) — 两反一正为多,视为阴 三枚之和: 6(老阴·三背·动) | 7(少阴·两背一字) | 8(少阳·两字一背) | 9(老阳·三字·动) 爻的阴阳: 8(少阳), 9(老阳) → 阳爻 6(老阴), 7(少阴) → 阴爻 动爻 (变爻): 6(老阴) → 变阳, 9(老阳) → 变阴 """ from __future__ import annotations import random from typing import List, Tuple from .constants import HEXAGRAMS # --------------------------------------------------------------------------- # 基础枚举 # --------------------------------------------------------------------------- LAO_YIN = 6 # 老阴 ━ ━ 动 (三背, 全反面) SHAO_YANG = 8 # 少阳 ━━━ (两正一反 = 二字一背 = face3+face3+face2) SHAO_YIN = 7 # 少阴 ━ ━ (两反一正 = 二背一字 = face2+face2+face3) LAO_YANG = 9 # 老阳 ━━━ 动 (三字, 全正面) YANG_LINES = (SHAO_YANG, LAO_YANG) YIN_LINES = (LAO_YIN, SHAO_YIN) MOVING_LINES = (LAO_YIN, LAO_YANG) def cast_one_line() -> Tuple[int, Tuple[int, int, int]]: """起一爻: 随机三枚铜钱,返回 (爻值, (c1, c2, c3))。 钱币值: 字=2, 背=3 """ coins = (random.randint(2, 3), random.randint(2, 3), random.randint(2, 3)) return sum(coins), coins def cast_six_lines(seed: int | None = None) -> List[int]: """连起六爻,自下而上(返回顺序即 [初爻, 二爻, ..., 上爻])。""" rng = random.Random(seed) out: List[int] = [] for _ in range(6): coins = (rng.randint(2, 3), rng.randint(2, 3), rng.randint(2, 3)) out.append(sum(coins)) return out def is_yang(line: int) -> bool: return line in YANG_LINES def is_moving(line: int) -> bool: return line in MOVING_LINES # --------------------------------------------------------------------------- # 卦象查表 # --------------------------------------------------------------------------- def _to_key(lines: List[int]) -> str: """六爻 (自下而上) → '111000' 形式的二进制 key (1=阳 0=阴)。""" return "".join("1" if is_yang(v) else "0" for v in lines) def lines_to_hex(lines: List[int]) -> dict: """根据六爻查主卦信息。""" key = _to_key(lines) return HEXAGRAMS[key] def get_changed_hex(lines: List[int]) -> dict | None: """动爻变卦: 老阳 9→阴, 老阴 6→阳; 无动爻返回 None。""" has_moving = any(is_moving(v) for v in lines) if not has_moving: return None new_lines = [ (SHAO_YANG if v == LAO_YIN else SHAO_YIN if v == LAO_YANG else v) for v in lines ] return lines_to_hex(new_lines) def moving_indices(lines: List[int]) -> List[int]: """返回动爻位置 (1-indexed, 自下而上)。""" return [i + 1 for i, v in enumerate(lines) if is_moving(v)] # --------------------------------------------------------------------------- # 爻画 (前端直接渲染的字符串) # --------------------------------------------------------------------------- def line_glyph(line: int) -> dict: """返回单爻的可视化信息。""" yang = is_yang(line) moving = is_moving(line) return { "value": line, "yang": yang, "moving": moving, "mark": "○" if line == LAO_YANG else "×" if line == LAO_YIN else "", "label": ( "老阳动爻" if line == LAO_YANG else "老阴动爻" if line == LAO_YIN else "少阳" if line == SHAO_YANG else "少阴" ), } def all_lines_glyph(lines: List[int]) -> List[dict]: """自下而上,前端按数组顺序自上而下渲染。""" return [line_glyph(v) for v in lines] # --------------------------------------------------------------------------- # 凶吉 (7 档详细等级 → 收束为 3 档大分类) # --------------------------------------------------------------------------- LEVEL_TO_CATEGORY = { "大吉": "吉", "吉": "吉", "中平偏吉": "吉", "中平": "平", "中平偏凶": "凶", "凶": "凶", "大凶": "凶", } def hex_auspicious_level(hex_info: dict) -> str: """从卦名反查 7 档吉凶等级(来自 constants.HEX_LEVELS)。 找不到时,按卦象初判给一个 3 档粗结果再退回 `中平`。 """ from .constants import HEX_LEVELS name = hex_info["name"] info = HEX_LEVELS.get(name) if info: return info["level"] # 兜底:用经典 3 档集合粗分 return LEVEL_TO_CATEGORY.get(hex_auspicious(hex_info), "中平") def hex_auspicious(hex_info: dict) -> str: """根据卦名给出一个粗略的吉凶倾向(3 档:吉/平/凶)。""" name = hex_info["name"] bad = {"讼", "否", "剥", "坎", "困", "明夷", "蹇", "小过", "大过", "夬"} good = {"乾", "泰", "谦", "大有", "随", "晋", "复", "临", "升", "家人", "益", "鼎", "震", "渐", "中孚", "既济", "需", "同人"} if name in bad: return "凶" if name in good: return "吉" return "平"