| """周易六爻起卦算法。 |
| |
| 每爻由三枚铜钱决定: |
| 正面 (字, 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 |
| SHAO_YIN = 7 |
| 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] |
|
|
|
|
| |
| |
| |
| 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"] |
| |
| 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 "平" |
|
|