- 交互契约改对话式询问(禁点选工具、一次1~2问、先铺垫再问),规则库 3.6 - 盘面图默认生成+chart_shot 截图;品牌区、大运/流年小卡信息补全、交流群二维码卡 - day_boundary 子正换日;memory_save --update-chart 换盘留痕;share_export 事件简介+城市级地域 - share_push 失败诊断+代理引导;_runtime_guard;修 STAR_ORDER 三奇贵人 KeyError - README 规则计数 541→542;manifest 重建(1275 文件);测试 142 项全绿
314 lines
16 KiB
Python
314 lines
16 KiB
Python
"""子平通行神煞求值(文档:rules/94_神煞细节.md)。
|
||
|
||
纯查表,零命理判断:输入四柱干支、性别、年柱纳音,输出按柱分组的神煞清单。
|
||
查法口径与 rules/94_神煞细节.md 各 R-煞 条目一一对应;条目注明出处为
|
||
《三命通会》者,查法表见本模块顶部各常量。
|
||
|
||
本命盘与岁运查询共用同一求值器:
|
||
- 本命:evaluate_natal() → {year/month/day/hour/combo: [条目文字]}
|
||
- 岁运:evaluate_target() → 目标干支(流年/流月/大运/流日)相对本命锚点所逢之神煞
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
from .ganzhi import GAN, NAYIN, ZHI
|
||
|
||
YANG_GAN = set("甲丙戊庚壬")
|
||
|
||
# —— 日干查地支 ——
|
||
TIANYI = {"甲": "丑未", "戊": "丑未", "庚": "丑未",
|
||
"乙": "子申", "己": "子申", "丙": "亥酉", "丁": "亥酉",
|
||
"壬": "卯巳", "癸": "卯巳", "辛": "午寅"} # 天乙贵人
|
||
WENCHANG = {"甲": "巳", "乙": "午", "丙": "申", "戊": "申", "丁": "酉",
|
||
"己": "酉", "庚": "亥", "辛": "子", "壬": "寅", "癸": "卯"} # 文昌
|
||
LU = {"甲": "寅", "乙": "卯", "丙": "巳", "丁": "午", "戊": "巳",
|
||
"己": "午", "庚": "申", "辛": "酉", "壬": "亥", "癸": "子"} # 禄神
|
||
YANGREN = {"甲": "卯", "丙": "午", "戊": "午", "庚": "酉", "壬": "子"} # 羊刃(五阳干)
|
||
JINYU = {"甲": "辰", "乙": "巳", "丙": "未", "丁": "申", "戊": "未",
|
||
"己": "申", "庚": "戌", "辛": "亥", "壬": "丑", "癸": "寅"} # 金舆(禄前二辰)
|
||
TAIJI = {"甲": "子午", "乙": "子午", "丙": "卯酉", "丁": "卯酉",
|
||
"戊": "辰戌丑未", "己": "辰戌丑未", "庚": "寅亥", "辛": "寅亥",
|
||
"壬": "巳申", "癸": "巳申"} # 太极贵人
|
||
FUXING = {"甲": "寅子", "乙": "丑卯", "丙": "寅子", "丁": "亥", "戊": "申",
|
||
"己": "未", "庚": "午", "辛": "巳", "壬": "辰", "癸": "丑卯"} # 福星贵人
|
||
HONGYAN = {"甲": "午", "乙": "午", "丙": "寅", "丁": "未", "戊": "子",
|
||
"己": "辰", "庚": "戌", "辛": "酉", "壬": "巳", "癸": "申"} # 红艳(三命通会)
|
||
|
||
# —— 月令查 ——
|
||
TIANDE = {"寅": "丁", "卯": "申", "辰": "壬", "巳": "辛", "午": "亥", "未": "甲",
|
||
"申": "癸", "酉": "寅", "戌": "丙", "亥": "乙", "子": "巳", "丑": "庚"} # 天德
|
||
YUEDE_GROUPS = {"寅": "丙", "午": "丙", "戌": "丙", "申": "壬", "子": "壬", "辰": "壬",
|
||
"亥": "甲", "卯": "甲", "未": "甲", "巳": "庚", "酉": "庚", "丑": "庚"} # 月德
|
||
STEM_HE = {"甲": "己", "己": "甲", "乙": "庚", "庚": "乙", "丙": "辛", "辛": "丙",
|
||
"丁": "壬", "壬": "丁", "戊": "癸", "癸": "戊"}
|
||
BRANCH_HE = {"子": "丑", "丑": "子", "寅": "亥", "亥": "寅", "卯": "戌", "戌": "卯",
|
||
"辰": "酉", "酉": "辰", "巳": "申", "申": "巳", "午": "未", "未": "午"}
|
||
|
||
# —— 三合局查(以年支或日支)——
|
||
def _sanhe(key: str, table: tuple[str, str, str, str]) -> str:
|
||
"""key 支所属三合局 → 对应目标支。table 序:申子辰、寅午戌、巳酉丑、亥卯未。"""
|
||
for group, target in zip(("申子辰", "寅午戌", "巳酉丑", "亥卯未"), table):
|
||
if key in group:
|
||
return target
|
||
raise ValueError(key)
|
||
|
||
YIMA = ("寅", "申", "亥", "巳") # 驿马
|
||
TAOHUA = ("酉", "卯", "午", "子") # 桃花(咸池)
|
||
HUAGAI = ("辰", "戌", "丑", "未") # 华盖
|
||
JIANGXING = ("子", "午", "酉", "卯") # 将星
|
||
JIESHA = ("巳", "亥", "寅", "申") # 劫煞
|
||
WANGSHEN = ("亥", "巳", "申", "寅") # 亡神
|
||
ZAISHA = ("午", "子", "卯", "酉") # 灾煞
|
||
|
||
# —— 年支查 ——
|
||
GUCHEN = {"亥": "寅", "子": "寅", "丑": "寅", "寅": "巳", "卯": "巳", "辰": "巳",
|
||
"巳": "申", "午": "申", "未": "申", "申": "亥", "酉": "亥", "戌": "亥"} # 孤辰
|
||
GUASU = {"亥": "戌", "子": "戌", "丑": "戌", "寅": "丑", "卯": "丑", "辰": "丑",
|
||
"巳": "辰", "午": "辰", "未": "辰", "申": "未", "酉": "未", "戌": "未"} # 寡宿
|
||
CHONG = {z: ZHI[(ZHI.index(z) + 6) % 12] for z in ZHI}
|
||
|
||
# —— 年柱纳音查 ——
|
||
# 天罗地网:火命见戌亥为天罗,水土命见辰巳为地网,金木命无(三命通会·卷三·论天罗地网)
|
||
# 童子煞:春秋寅子贵,冬夏卯未辰;金木马卯合,水火鸡犬多;土命逢辰巳(通行口诀,无书证)
|
||
SEASON_OF_MONTH = {**{m: "春" for m in "寅卯辰"}, **{m: "夏" for m in "巳午未"},
|
||
**{m: "秋" for m in "申酉戌"}, **{m: "冬" for m in "亥子丑"}}
|
||
TONGZI_SEASON = {"春": "寅子", "秋": "寅子", "冬": "卯未辰", "夏": "卯未辰"}
|
||
TONGZI_NAYIN = {"金": "午卯", "木": "午卯", "水": "酉戌", "火": "酉戌", "土": "辰巳"}
|
||
|
||
# —— 日柱干支固定组 ——
|
||
YINCHA_YANGCUO = {"丙子", "丁丑", "戊寅", "辛卯", "壬辰", "癸巳",
|
||
"丙午", "丁未", "戊申", "辛酉", "壬戌", "癸亥"} # 阴差阳错
|
||
SHILING = {"甲辰", "乙亥", "丙辰", "丁酉", "戊午",
|
||
"庚戌", "庚寅", "辛亥", "壬寅", "癸未"} # 十灵日
|
||
KUIGANG = {"庚辰", "庚戌", "壬辰", "戊戌"} # 魁罡
|
||
SHIE_DABAI = {"甲辰", "乙巳", "丙申", "丁亥", "戊戌",
|
||
"己丑", "庚辰", "辛巳", "壬申", "癸亥"} # 十恶大败
|
||
TIANSHE = {"春": "戊寅", "夏": "甲午", "秋": "戊申", "冬": "甲子"} # 天赦日
|
||
|
||
# —— 三奇(天干顺布相连;三组归属诸书异说,不冠名,只判顺逆)——
|
||
SANQI = ("乙丙丁", "甲戊庚", "辛壬癸")
|
||
|
||
# 输出排序(每柱内部按此序)
|
||
STAR_ORDER = [
|
||
"天德", "天德合", "月德", "月德合", "天乙贵人", "太极贵人", "福星贵人",
|
||
"三奇贵人",
|
||
"文昌", "禄神", "羊刃", "金舆", "驿马", "桃花", "红艳", "华盖", "将星",
|
||
"劫煞", "灾煞", "亡神", "元辰", "勾煞", "绞煞", "孤辰", "寡宿",
|
||
"天罗", "地网", "童子煞", "天赦", "魁罡", "阴差阳错", "十灵日", "十恶大败",
|
||
]
|
||
|
||
PILLAR_KEYS = ("year", "month", "day", "hour")
|
||
PILLAR_LABELS = {"year": "年柱", "month": "月柱", "day": "日柱", "hour": "时柱"}
|
||
_PILLAR_SHORT = {"year": "年", "month": "月", "day": "日", "hour": "时"}
|
||
|
||
|
||
def _shift(zhi: str, n: int) -> str:
|
||
return ZHI[(ZHI.index(zhi) + n) % 12]
|
||
|
||
|
||
def _entry(star: str, note: str) -> tuple[str, str]:
|
||
return (star, note)
|
||
|
||
|
||
class ShenShaContext:
|
||
"""本命锚点:日干、年支、日支、月令、年柱纳音、阴阳男女。"""
|
||
|
||
def __init__(self, pillars: dict[str, str], gender: str):
|
||
"""pillars: {year/month/day/hour: 干支两字符};gender: 「男」/「女」"""
|
||
self.pillars = pillars
|
||
self.gender = gender
|
||
self.day_gan = pillars["day"][0]
|
||
self.year_zhi = pillars["year"][1]
|
||
self.day_zhi = pillars["day"][1]
|
||
self.month_zhi = pillars["month"][1]
|
||
self.year_ganzhi = pillars["year"]
|
||
self.day_ganzhi = pillars["day"]
|
||
self.nayin_wuxing = NAYIN[self.year_ganzhi][-1]
|
||
year_gan = pillars["year"][0]
|
||
yang_year = year_gan in YANG_GAN
|
||
male = gender == "男"
|
||
# 阳男阴女为顺,阴男阳女为逆
|
||
self.shun = (yang_year and male) or (not yang_year and not male)
|
||
self.season = SEASON_OF_MONTH[self.month_zhi]
|
||
|
||
def anchor_hits(self, star: str, targets: str, anchor_desc: str,
|
||
out: dict[str, list]) -> None:
|
||
"""targets 为若干目标支;命中某柱地支则在该柱登记。"""
|
||
for key in PILLAR_KEYS:
|
||
zhi = self.pillars[key][1]
|
||
if zhi in targets:
|
||
out[key].append(_entry(
|
||
star, f"以{anchor_desc}查,见于{_PILLAR_SHORT[key]}支{zhi}"))
|
||
|
||
|
||
def evaluate_natal(pillars: dict[str, str], gender: str) -> dict[str, list]:
|
||
"""本命神煞:{year/month/day/hour: [(星名, 注)], combo: [...]}"""
|
||
ctx = ShenShaContext(pillars, gender)
|
||
out: dict[str, list] = {k: [] for k in PILLAR_KEYS}
|
||
out["combo"] = []
|
||
|
||
# —— 日干查 ——
|
||
ctx.anchor_hits("天乙贵人", TIANYI[ctx.day_gan], f"日干{ctx.day_gan}", out)
|
||
ctx.anchor_hits("文昌", WENCHANG[ctx.day_gan], f"日干{ctx.day_gan}", out)
|
||
ctx.anchor_hits("禄神", LU[ctx.day_gan], f"日干{ctx.day_gan}", out)
|
||
if ctx.day_gan in YANGREN:
|
||
ctx.anchor_hits("羊刃", YANGREN[ctx.day_gan], f"日干{ctx.day_gan}", out)
|
||
ctx.anchor_hits("金舆", JINYU[ctx.day_gan], f"日干{ctx.day_gan}", out)
|
||
ctx.anchor_hits("太极贵人", TAIJI[ctx.day_gan], f"日干{ctx.day_gan}", out)
|
||
ctx.anchor_hits("福星贵人", FUXING[ctx.day_gan], f"日干{ctx.day_gan}", out)
|
||
ctx.anchor_hits("红艳", HONGYAN[ctx.day_gan], f"日干{ctx.day_gan}", out)
|
||
|
||
# —— 月令查(天德、月德及其合,可应天干或地支)——
|
||
for star, target in (("天德", TIANDE[ctx.month_zhi]),
|
||
("月德", YUEDE_GROUPS[ctx.month_zhi])):
|
||
he = STEM_HE.get(target) or BRANCH_HE.get(target)
|
||
for key in PILLAR_KEYS:
|
||
gan, zhi = ctx.pillars[key]
|
||
if gan == target:
|
||
out[key].append(_entry(
|
||
star, f"以月令{ctx.month_zhi}查,见于{_PILLAR_SHORT[key]}干{gan}"))
|
||
elif zhi == target:
|
||
out[key].append(_entry(
|
||
star, f"以月令{ctx.month_zhi}查,见于{_PILLAR_SHORT[key]}支{zhi}"))
|
||
if gan == he:
|
||
out[key].append(_entry(
|
||
f"{star}合", f"以月令{ctx.month_zhi}查,见于{_PILLAR_SHORT[key]}干{gan}"))
|
||
elif zhi == he:
|
||
out[key].append(_entry(
|
||
f"{star}合", f"以月令{ctx.month_zhi}查,见于{_PILLAR_SHORT[key]}支{zhi}"))
|
||
|
||
# —— 年支、日支并查(三合局诸煞)——
|
||
for star, table in (("驿马", YIMA), ("桃花", TAOHUA), ("华盖", HUAGAI),
|
||
("将星", JIANGXING), ("劫煞", JIESHA),
|
||
("灾煞", ZAISHA), ("亡神", WANGSHEN)):
|
||
for anchor_zhi, anchor_desc in ((ctx.year_zhi, f"年支{ctx.year_zhi}"),
|
||
(ctx.day_zhi, f"日支{ctx.day_zhi}")):
|
||
ctx.anchor_hits(star, _sanhe(anchor_zhi, table), anchor_desc, out)
|
||
|
||
# —— 年支查(孤辰寡宿、元辰、勾绞)——
|
||
ctx.anchor_hits("孤辰", GUCHEN[ctx.year_zhi], f"年支{ctx.year_zhi}", out)
|
||
ctx.anchor_hits("寡宿", GUASU[ctx.year_zhi], f"年支{ctx.year_zhi}", out)
|
||
yz = ctx.year_zhi
|
||
if ctx.shun: # 阳男阴女
|
||
yuan_chen = _shift(CHONG[yz], 1) # 冲前一位
|
||
gou, jiao = _shift(yz, 3), _shift(yz, -3) # 命前三辰为勾,后三辰为绞
|
||
else: # 阴男阳女
|
||
yuan_chen = _shift(CHONG[yz], -1) # 冲后一位
|
||
jiao, gou = _shift(yz, 3), _shift(yz, -3)
|
||
ctx.anchor_hits("元辰", yuan_chen, f"年支{yz}", out)
|
||
ctx.anchor_hits("勾煞", gou, f"年支{yz}", out)
|
||
ctx.anchor_hits("绞煞", jiao, f"年支{yz}", out)
|
||
|
||
# —— 年柱纳音查 ——
|
||
wx = ctx.nayin_wuxing
|
||
if wx == "火":
|
||
ctx.anchor_hits("天罗", "戌亥", f"年柱纳音{wx}命", out)
|
||
elif wx in "水土":
|
||
ctx.anchor_hits("地网", "辰巳", f"年柱纳音{wx}命", out)
|
||
for key in ("day", "hour"): # 童子煞查日、时二支
|
||
zhi = ctx.pillars[key][1]
|
||
if zhi in TONGZI_SEASON[ctx.season]:
|
||
out[key].append(_entry(
|
||
"童子煞", f"以月令{ctx.month_zhi}季节查,见于{_PILLAR_SHORT[key]}支{zhi}"))
|
||
elif zhi in TONGZI_NAYIN[wx]:
|
||
out[key].append(_entry(
|
||
"童子煞", f"以年命纳音{wx}查,见于{_PILLAR_SHORT[key]}支{zhi}"))
|
||
|
||
# —— 日柱干支固定组 ——
|
||
fixed = (("阴差阳错", YINCHA_YANGCUO), ("十灵日", SHILING),
|
||
("魁罡", KUIGANG), ("十恶大败", SHIE_DABAI))
|
||
for star, group in fixed:
|
||
if ctx.day_ganzhi in group:
|
||
out["day"].append(_entry(star, "日柱干支直查"))
|
||
if ctx.day_ganzhi == TIANSHE[ctx.season]:
|
||
out["day"].append(_entry("天赦", f"{ctx.season}季生,日柱干支直查"))
|
||
|
||
# —— 三奇(天干顺布相连)——
|
||
stems = "".join(ctx.pillars[k][0] for k in PILLAR_KEYS)
|
||
for seq in SANQI:
|
||
if seq in stems:
|
||
i = stems.index(seq)
|
||
span = "年月日" if i == 0 else "月日时"
|
||
out["combo"].append(_entry("三奇贵人", f"{seq}顺布,见于{span}天干"))
|
||
elif seq[::-1] in stems:
|
||
i = stems.index(seq[::-1])
|
||
span = "年月日" if i == 0 else "月日时"
|
||
out["combo"].append(_entry("三奇贵人", f"{seq}逆布,见于{span}天干"))
|
||
|
||
return out
|
||
|
||
|
||
def evaluate_target(pillars: dict[str, str], gender: str,
|
||
target_ganzhi: str) -> dict[str, list]:
|
||
"""岁运查询:目标干支(流年/流月/大运/流日)相对本命锚点所逢神煞。
|
||
|
||
返回 {anchored: [(星名, 注)], own: [(星名, 注)]}:
|
||
anchored —— 以本命日干/年支/日支/月令/年命纳音查得,目标干支逢之;
|
||
own —— 目标干支自身所属之日柱固定组(阴差阳错等,供流日查询参看)。
|
||
"""
|
||
ctx = ShenShaContext(pillars, gender)
|
||
t_gan, t_zhi = target_ganzhi[0], target_ganzhi[1]
|
||
anchored: list[tuple[str, str]] = []
|
||
own: list[tuple[str, str]] = []
|
||
|
||
def hit(star: str, targets: str, anchor_desc: str) -> None:
|
||
if t_zhi in targets:
|
||
anchored.append(_entry(star, f"以{anchor_desc}查,见于{target_ganzhi}之支"))
|
||
|
||
hit("天乙贵人", TIANYI[ctx.day_gan], f"日干{ctx.day_gan}")
|
||
hit("文昌", WENCHANG[ctx.day_gan], f"日干{ctx.day_gan}")
|
||
hit("禄神", LU[ctx.day_gan], f"日干{ctx.day_gan}")
|
||
if ctx.day_gan in YANGREN:
|
||
hit("羊刃", YANGREN[ctx.day_gan], f"日干{ctx.day_gan}")
|
||
hit("金舆", JINYU[ctx.day_gan], f"日干{ctx.day_gan}")
|
||
hit("太极贵人", TAIJI[ctx.day_gan], f"日干{ctx.day_gan}")
|
||
hit("福星贵人", FUXING[ctx.day_gan], f"日干{ctx.day_gan}")
|
||
hit("红艳", HONGYAN[ctx.day_gan], f"日干{ctx.day_gan}")
|
||
|
||
for star, target in (("天德", TIANDE[ctx.month_zhi]),
|
||
("月德", YUEDE_GROUPS[ctx.month_zhi])):
|
||
he = STEM_HE.get(target) or BRANCH_HE.get(target)
|
||
if t_gan == target or t_zhi == target:
|
||
anchored.append(_entry(star, f"以月令{ctx.month_zhi}查,逢{target_ganzhi}"))
|
||
if t_gan == he or t_zhi == he:
|
||
anchored.append(_entry(f"{star}合", f"以月令{ctx.month_zhi}查,逢{target_ganzhi}"))
|
||
|
||
for star, table in (("驿马", YIMA), ("桃花", TAOHUA), ("华盖", HUAGAI),
|
||
("将星", JIANGXING), ("劫煞", JIESHA),
|
||
("灾煞", ZAISHA), ("亡神", WANGSHEN)):
|
||
for anchor_zhi, anchor_desc in ((ctx.year_zhi, f"年支{ctx.year_zhi}"),
|
||
(ctx.day_zhi, f"日支{ctx.day_zhi}")):
|
||
hit(star, _sanhe(anchor_zhi, table), anchor_desc)
|
||
|
||
hit("孤辰", GUCHEN[ctx.year_zhi], f"年支{ctx.year_zhi}")
|
||
hit("寡宿", GUASU[ctx.year_zhi], f"年支{ctx.year_zhi}")
|
||
yz = ctx.year_zhi
|
||
if ctx.shun:
|
||
yuan_chen, gou, jiao = _shift(CHONG[yz], 1), _shift(yz, 3), _shift(yz, -3)
|
||
else:
|
||
yuan_chen, jiao, gou = _shift(CHONG[yz], -1), _shift(yz, 3), _shift(yz, -3)
|
||
hit("元辰", yuan_chen, f"年支{yz}")
|
||
hit("勾煞", gou, f"年支{yz}")
|
||
hit("绞煞", jiao, f"年支{yz}")
|
||
|
||
wx = ctx.nayin_wuxing
|
||
if wx == "火":
|
||
hit("天罗", "戌亥", f"年柱纳音{wx}命")
|
||
elif wx in "水土":
|
||
hit("地网", "辰巳", f"年柱纳音{wx}命")
|
||
if t_zhi in TONGZI_SEASON[ctx.season]:
|
||
anchored.append(_entry("童子煞", f"以月令{ctx.month_zhi}季节查,逢{target_ganzhi}"))
|
||
elif t_zhi in TONGZI_NAYIN[wx]:
|
||
anchored.append(_entry("童子煞", f"以年命纳音{wx}查,逢{target_ganzhi}"))
|
||
|
||
for star, group in (("阴差阳错", YINCHA_YANGCUO), ("十灵日", SHILING),
|
||
("魁罡", KUIGANG), ("十恶大败", SHIE_DABAI)):
|
||
if target_ganzhi in group:
|
||
own.append(_entry(star, "目标干支自身所属"))
|
||
if target_ganzhi == TIANSHE[ctx.season]:
|
||
own.append(_entry("天赦", f"{ctx.season}季之目标干支自身所属"))
|
||
|
||
order = {name: i for i, name in enumerate(STAR_ORDER)}
|
||
anchored.sort(key=lambda e: order[e[0]])
|
||
own.sort(key=lambda e: order[e[0]])
|
||
return {"anchored": anchored, "own": own}
|