tianwen / divination /interpret.py
liuyd-dev's picture
v0: zero is infinite
07c10da verified
Raw
History Blame Contribute Delete
3.66 kB
"""排盘到文字解读的胶水层。"""
from __future__ import annotations
from dataclasses import dataclass, asdict
from typing import Any
from .coin import Cast
from .hexagram import Hexagram, TRIGRAM_ELEMENT
from bazi.five_elements import describe_relation
RELATION_TO_LIUQIN = {
"同类": "兄弟",
"我生": "子孙",
"生我": "父母",
"我克": "妻财",
"克我": "官鬼",
}
LIU_SHEN_ORDER = ("青龙", "朱雀", "勾陈", "螣蛇", "白虎", "玄武")
GAN_TO_LIU_SHEN_START = {
"甲": 0, "乙": 0,
"丙": 1, "丁": 1,
"戊": 2, "己": 2,
"庚": 3, "辛": 3,
"壬": 4, "癸": 4,
}
LINE_TITLE = {1: "初", 2: "二", 3: "三", 4: "四", 5: "五", 6: "上"}
class LineReading:
index: int
title: str
kind: str
is_yang: bool
is_changing: bool
element: str
liu_qin: str
liu_shen: str
bian: str
def __init__(self, **kw):
for k, v in kw.items():
setattr(self, k, v)
def to_dict(self):
return self.__dict__
class CastReading:
cast: Cast
ben: Hexagram
bian: Hexagram
changing_indices: list
lines: list
element: str
day_gan: str
summary: str
def __init__(self, **kw):
for k, v in kw.items():
setattr(self, k, v)
def to_dict(self):
"""递归把 namedtuple / LineReading 全部转为 dict, 方便模板层使用。"""
ben = self.ben
bian = self.bian
return {
"cast": self.cast.to_dict() if hasattr(self.cast, "to_dict") else None,
"ben": ben._asdict() if isinstance(ben, tuple) and hasattr(ben, "_asdict") else ben,
"bian": bian._asdict() if isinstance(bian, tuple) and hasattr(bian, "_asdict") else bian,
"changing_indices": list(self.changing_indices or []),
"lines": [ln.to_dict() if hasattr(ln, "to_dict") else ln for ln in (self.lines or [])],
"element": self.element,
"day_gan": self.day_gan,
"question": getattr(self, "question", "") or "",
"summary": getattr(self, "summary", "") or "",
}
def _line_element(line_idx: int, hexagram: Hexagram) -> str:
if line_idx <= 3:
return TRIGRAM_ELEMENT[hexagram.lower]
return TRIGRAM_ELEMENT[hexagram.upper]
def _liu_qin(hex_el: str, line_el: str) -> str:
return RELATION_TO_LIUQIN.get(describe_relation(hex_el, line_el), "—")
def _liu_shen(day_gan: str, line_idx: int) -> str:
start = GAN_TO_LIU_SHEN_START.get(day_gan, 0)
return LIU_SHEN_ORDER[(start + line_idx - 1) % 6]
def build_reading(cast: Cast, day_gan: str = "甲") -> CastReading:
hex_el = cast.ben_hex.element
lines = []
for ln in cast.lines:
el = _line_element(ln.index, cast.ben_hex)
bian_kind = ""
if ln.is_changing:
bian_kind = "变为阳" if not ln.is_yang else "变为阴"
lines.append(LineReading(
index=ln.index,
title=LINE_TITLE[ln.index],
kind=ln.kind,
is_yang=ln.is_yang,
is_changing=ln.is_changing,
element=el,
liu_qin=_liu_qin(hex_el, el),
liu_shen=_liu_shen(day_gan, ln.index),
bian=bian_kind,
))
return CastReading(
cast=cast,
ben=cast.ben_hex,
bian=cast.bian_hex,
changing_indices=cast.changing_indices,
lines=lines,
element=hex_el,
day_gan=day_gan,
question=getattr(cast, "question", ""),
)