feat: 大六壬证据链架构

This commit is contained in:
小钰 committed 2026-08-03 10:03:51 +08:00
1 parent c0665c5ddf
commit b03d84d479
2 files changed
+862

No files matched your search

+853
View File
@@ -0,0 +1,853 @@
/**
* 大六壬证据链架构
* 参考梅花易数 evidence 模式,为排盘结果增加结构化证据链
*
* 核心原则:
* - 每个 fact 必须有具体的 limitation(不能是泛泛免责)
* - 证据链是增强层,不破坏现有排盘流程
* - 支持/约束/反证三位一体
*/
import {
Stem, Branch, BRANCHES, branchIndex,
BRANCH_ELEMENT, BRANCH_CHONG, BRANCH_HAI, BRANCH_XING,
WUXING_SHENG, WUXING_KE, getXunKong,
} from './ganzhi.js';
import { PlatePosition, buildPlate, getYueJiang } from './plate.js';
import { TianJiang, assignGenerals, getNoblemanBranch, isDaytime } from './tianjiang.js';
import { SiKeLesson, SiKeResult, STEM_JIGONG } from './sike.js';
import { SanChuanResult } from './sanchuan.js';
// ═══════════════════════════════════════════
// 类型定义
// ═══════════════════════════════════════════
/** 证据阶段键 */
export type LiurenStageKey = 'initial' | 'middle' | 'final';
/** LiurenFact 基础接口 — 所有 fact 的公共字段 */
export interface LiurenFact {
key: string;
status: string;
promptText: string;
sources: string[];
limitation: string;
}
/** 起盘参数事实 */
export interface CalculationFact extends LiurenFact {
dayStem: Stem;
dayBranch: Branch;
hourBranch: Branch;
month: number;
yueJiang: Branch;
isDaytime: boolean;
noblemanBranch: Branch;
xunKong: Branch[];
}
/** 天地盘逐位事实 */
export interface PlateFact extends LiurenFact {
position: number; // 0-11
earthBranch: Branch;
heavenBranch: Branch;
general: TianJiang | undefined;
generalNature: string; // 天将吉凶
}
/** 四课事实 */
export interface LessonEvidence extends LiurenFact {
lessonIndex: number; // 1-4
lower: Branch; // 下神
upper: Branch; // 上神
lowerElement: string;
upperElement: string;
general: TianJiang | undefined;
relation: string; // 上下神五行关系
lessonNote: string; // 课注
}
/** 三传事实 */
export interface TransmissionEvidence extends LiurenFact {
stage: LiurenStageKey;
stageLabel: string; // 初传/中传/末传
branch: Branch;
element: string;
general: TianJiang | undefined;
generalNature: string;
xunkongStatus: string; // 旬空状态
seasonState: string; // 月令旺衰
}
/** 相邻传推进事实 */
export interface TransitionFact extends LiurenFact {
fromStage: LiurenStageKey;
toStage: LiurenStageKey;
fromBranch: Branch;
toBranch: Branch;
fromElement: string;
toElement: string;
elementRelation: string; // 五行关系
fromGeneral: string;
toGeneral: string;
}
/** 反证事实 */
export interface CounterEvidenceFact extends LiurenFact {
ownerStageKey: string;
type: '旬空' | '休囚' | '冲克' | '白虎临课' | '天空临课' | '玄武临课' | '脱耗' | '受克';
detail: string;
}
/** 应期事实 */
export interface TimingFact extends LiurenFact {
order: number;
type: '初传旺衰' | '旬空填实' | '冲合时机' | '月令助力' | '传进条件';
rawText?: string;
}
/** 类神焦点事实 */
export interface FocusFact extends LiurenFact {
category: string; // 问事类型
targetBranch: Branch;
targetElement: string;
targetGeneral: string | undefined;
focusNote: string;
}
/** 完整的证据链分析结果 */
export interface LiurenEvidenceAnalysis {
key: 'liuren:evidence';
status: '已计算';
// 起盘参数
calculation: CalculationFact;
// 天地盘
plateFacts: PlateFact[];
// 四课
lessonFacts: LessonEvidence[];
// 三传
transmissionFacts: TransmissionEvidence[];
// 相邻传推进
transitionFacts: TransitionFact[];
// 反证
counterEvidenceFacts: CounterEvidenceFact[];
counterSummary: { status: '有明确反证' | '未见明确反证'; detail: string };
// 应期
timingFacts: TimingFact[];
// 类神焦点
focusFacts: FocusFact[];
// 汇总
summary: {
factCount: number;
plateCount: number;
lessonCount: number;
transmissionCount: number;
transitionCount: number;
counterCount: number;
timingCount: number;
focusCount: number;
};
// 全局限制
limitations: string[];
// 原始 prompt 文本
promptText: string;
}
// ═══════════════════════════════════════════
// 限制声明常量
// ═══════════════════════════════════════════
const CALCULATION_FACT_LIMITATION =
'起盘参数事实只记录四柱、月将、昼夜贵人、旬空等排盘基础条件;不得由单个参数直接推断现实事件的吉凶、成败或必然走向' as const;
const PLATE_FACT_LIMITATION =
'天地盘逐位事实只记录地盘支、天盘支与所乘天将的排布关系;不得由单个天将位置直接断定现实人物意图、事件结果或时间确定性' as const;
const LESSON_FACT_LIMITATION =
'四课事实只记录上下神五行关系与所乘天将的状态;课注是传统分类辅助,不得将单课关系直接等同于现实事件的起因、过程或结果' as const;
const TRANSMISSION_FACT_LIMITATION =
'三传事实只记录初传、中传、末传的地支、天将、旬空与月令状态;三传推进是盘内逻辑推演,不得直接等同于现实事件的必然时间线或固定结果' as const;
const TRANSITION_FACT_LIMITATION =
'相邻传推进事实只比较初传→中传、中传→末传的五行关系变化;不得把传内先后直接写成现实事件必然按同样顺序发生' as const;
const COUNTER_FACT_LIMITATION =
'反证事实只表示盘面存在旬空、休囚、冲克、白虎等限制条件;不得把单项反证直接写成现实失败、灾祸、伤病或必然结果' as const;
const COUNTER_SUMMARY_LIMITATION =
'反证汇总只说明当前盘面核验是否发现明确限制;未见明确反证不代表现实风险为零,也不得按反证数量换算吉凶总分' as const;
const TIMING_FACT_LIMITATION =
'应期事实只提供旺衰、旬空填实、冲合等相对触发条件;不得把地支、五行、旺衰换算为唯一日期,也不证明事件必然发生' as const;
const FOCUS_FACT_LIMITATION =
'类神焦点事实只根据问事类型在盘面中定位关注对象的状态;类神选取基于传统分类规则,不代表该对象在现实中必然表现为所述状态' as const;
const SUMMARY_FACT_LIMITATION =
'大六壬证据汇总只统计起盘参数、天地盘、四课、三传、推进、反证、应期与类神事实的覆盖情况;不得按数量生成吉凶总分、成功率或唯一日期' as const;
// ═══════════════════════════════════════════
// 辅助函数
// ═══════════════════════════════════════════
function unique(values: string[]): string[] {
return Array.from(new Set(values.filter(Boolean)));
}
/** 判断旺相休囚死 */
function getSeasonState(element: string, monthBranch: Branch): string {
const monthElement = BRANCH_ELEMENT[monthBranch];
if (element === monthElement) return '旺';
if (WUXING_SHENG[monthElement] === element) return '相';
if (WUXING_SHENG[element] === monthElement) return '休';
if (WUXING_KE[element] === monthElement) return '囚';
return '死';
}
/** 获取两支五行关系描述 */
function getElementRelation(from: Branch, to: Branch): string {
const fe = BRANCH_ELEMENT[from];
const te = BRANCH_ELEMENT[to];
if (fe === te) return '比和';
if (WUXING_SHENG[fe] === te) return `${fe}生${te}`;
if (WUXING_KE[fe] === te) return `${fe}克${te}`;
if (WUXING_SHENG[te] === fe) return `${te}生${fe}`;
if (WUXING_KE[te] === fe) return `${te}克${fe}`;
return '关系未定';
}
/** 检查地支之间的冲关系 */
function hasChong(a: Branch, b: Branch): boolean {
return BRANCH_CHONG[a] === b;
}
/** 检查地支之间的害关系 */
function hasHai(a: Branch, b: Branch): boolean {
return BRANCH_HAI[a] === b;
}
/** 检查地支之间的刑关系 */
function hasXing(a: Branch, b: Branch): boolean {
return (BRANCH_XING[a] || []).includes(b);
}
/** 天将简述 */
function generalBrief(g: TianJiang | undefined): string {
return g ? `${g.name}(${g.element}/${g.nature})` : '无将';
}
// ═══════════════════════════════════════════
// 证据链生成函数
// ═══════════════════════════════════════════
export interface LiurenInput {
dayStem: Stem;
dayBranch: Branch;
hourBranch: Branch;
month: number;
yueJiang: Branch;
plate: PlatePosition[];
heavenBranches: Branch[];
sike: SiKeResult;
sanchuan: SanChuanResult;
generals: Map<Branch, TianJiang>;
noblemanBranch: Branch;
xunKong: Branch[];
question?: string;
}
/**
* 从排盘结果生成完整证据链
*/
export function buildEvidence(input: LiurenInput): LiurenEvidenceAnalysis {
const {
dayStem, dayBranch, hourBranch, month, yueJiang,
plate, heavenBranches, sike, sanchuan,
generals, noblemanBranch, xunKong, question,
} = input;
const monthBranch = BRANCHES[(month + 1) % 12] as Branch; // 简化月支映射
// ── 1. 起盘参数 ──
const calculation = buildCalculationFact(
dayStem, dayBranch, hourBranch, month, yueJiang,
noblemanBranch, xunKong
);
// ── 2. 天地盘逐位 ──
const plateFacts = buildPlateFacts(plate, generals);
// ── 3. 四课 ──
const lessonFacts = buildLessonFacts(sike, generals);
// ── 4. 三传 ──
const transmissionFacts = buildTransmissionFacts(sanchuan, generals, xunKong, monthBranch);
// ── 5. 相邻传推进 ──
const transitionFacts = buildTransitionFacts(transmissionFacts);
// ── 6. 反证 ──
const counterEvidenceFacts = buildCounterEvidence(
sike, sanchuan, generals, xunKong, monthBranch
);
const counterSummary = {
status: counterEvidenceFacts.length > 0 ? '有明确反证' as const : '未见明确反证' as const,
detail: counterEvidenceFacts.length > 0
? `发现${counterEvidenceFacts.length}条反证:${counterEvidenceFacts.map(f => f.detail).join(';')}`
: '当前盘面未见明确反证',
limitation: COUNTER_SUMMARY_LIMITATION,
};
// ── 7. 应期 ──
const timingFacts = buildTimingFacts(sanchuan, xunKong, monthBranch);
// ── 8. 类神焦点 ──
const focusFacts = buildFocusFacts(question, sike, sanchuan, plate, generals, monthBranch);
// ── 汇总 ──
const factCount = 1 + plateFacts.length + lessonFacts.length
+ transmissionFacts.length + transitionFacts.length
+ counterEvidenceFacts.length + timingFacts.length + focusFacts.length;
const summary = {
factCount,
plateCount: plateFacts.length,
lessonCount: lessonFacts.length,
transmissionCount: transmissionFacts.length,
transitionCount: transitionFacts.length,
counterCount: counterEvidenceFacts.length,
timingCount: timingFacts.length,
focusCount: focusFacts.length,
limitation: SUMMARY_FACT_LIMITATION,
};
// ── 全局限制 ──
const limitations = [
'大六壬证据链仅描述天地盘、四课、三传的五行生克与天将状态,不证明现实吉凶、成败、概率或固定应期',
'四课课注与传统断语是辅助分类,不直接对应现实事件、人物、婚育、疾病、伤亡或诉讼',
'反证与支持仅反映盘面五行生克状态,不代表现实中的必然结果或风险为零',
'应期分析只提供相对触发条件(旺衰、填实、冲合),不得换算为绝对日期或时间节点',
'类神焦点基于传统问事分类规则选取,不代表该对象在现实中必然表现为所述状态',
];
// ── promptText ──
const promptText = [
calculation.promptText,
...plateFacts.map(f => f.promptText),
...lessonFacts.map(f => f.promptText),
...transmissionFacts.map(f => f.promptText),
...transitionFacts.map(f => f.promptText),
...counterEvidenceFacts.map(f => f.promptText),
counterSummary.detail,
...timingFacts.map(f => f.promptText),
...focusFacts.map(f => f.promptText),
].join('\n');
return {
key: 'liuren:evidence',
status: '已计算',
calculation,
plateFacts,
lessonFacts,
transmissionFacts,
transitionFacts,
counterEvidenceFacts,
counterSummary,
timingFacts,
focusFacts,
summary,
limitations,
promptText,
};
}
// ── 子生成器 ──
function buildCalculationFact(
dayStem: Stem, dayBranch: Branch, hourBranch: Branch,
month: number, yueJiang: Branch,
noblemanBranch: Branch, xunKong: Branch[],
): CalculationFact {
const daytime = isDaytime(hourBranch);
return {
key: 'liuren:calc',
status: '已记录',
dayStem, dayBranch, hourBranch, month, yueJiang,
isDaytime: daytime,
noblemanBranch,
xunKong,
promptText: `起盘:${dayStem}${dayBranch}日 ${hourBranch}时,${month}月月将${yueJiang},${daytime ? '昼' : '夜'}占,贵人${noblemanBranch},旬空${xunKong.join('、')}`,
sources: ['四柱日时', '月将起法', '昼夜贵人规则', '旬空起法'],
limitation: CALCULATION_FACT_LIMITATION,
};
}
function buildPlateFacts(
plate: PlatePosition[],
generals: Map<Branch, TianJiang>,
): PlateFact[] {
return BRANCHES.map((earth, i) => {
const heaven = plate[i].heavenBranch;
const gen = generals.get(earth);
return {
key: `liuren:plate:${earth}`,
status: '已记录',
position: i,
earthBranch: earth,
heavenBranch: heaven,
general: gen,
generalNature: gen?.nature || '',
promptText: `${earth}上见${heaven},乘${generalBrief(gen)}`,
sources: ['天地盘排布', '十二天将排布'],
limitation: PLATE_FACT_LIMITATION,
};
});
}
function buildLessonFacts(
sike: SiKeResult,
generals: Map<Branch, TianJiang>,
): LessonEvidence[] {
return sike.lessons.map(lesson => {
const lowerElement = BRANCH_ELEMENT[lesson.lower];
const upperElement = BRANCH_ELEMENT[lesson.upper];
const gen = generals.get(lesson.upper);
const relation = getElementRelation(lesson.lower, lesson.upper);
// 课注
const notes: string[] = [];
if (lowerElement === upperElement) notes.push('上下比和');
if (WUXING_KE[upperElement] === lowerElement) notes.push('上克下(克)');
if (WUXING_KE[lowerElement] === upperElement) notes.push('下克上(贼)');
if (WUXING_SHENG[upperElement] === lowerElement) notes.push('上生下');
if (WUXING_SHENG[lowerElement] === upperElement) notes.push('下生上(泄)');
if (hasChong(lesson.lower, lesson.upper)) notes.push('上下相冲');
if (hasHai(lesson.lower, lesson.upper)) notes.push('上下相害');
if (hasXing(lesson.lower, lesson.upper)) notes.push('上下相刑');
const lessonNote = notes.length > 0 ? notes.join(';') : '无特殊格局';
return {
key: `liuren:lesson:${lesson.index}`,
status: '已计算',
lessonIndex: lesson.index,
lower: lesson.lower,
upper: lesson.upper,
lowerElement,
upperElement,
general: gen,
relation,
lessonNote,
promptText: `第${lesson.index}课:${lesson.lower}(${lowerElement})→${lesson.upper}(${upperElement}),乘${generalBrief(gen)},${relation};${lessonNote}`,
sources: ['四课上下神', '五行生克', '天将排布', '冲害刑关系'],
limitation: LESSON_FACT_LIMITATION,
};
});
}
function buildTransmissionFacts(
sanchuan: SanChuanResult,
generals: Map<Branch, TianJiang>,
xunKong: Branch[],
monthBranch: Branch,
): TransmissionEvidence[] {
const stages: { key: LiurenStageKey; label: string; branch: Branch }[] = [
{ key: 'initial', label: '初传(发用)', branch: sanchuan.initial },
{ key: 'middle', label: '中传(过程)', branch: sanchuan.middle },
{ key: 'final', label: '末传(结果)', branch: sanchuan.final },
];
return stages.map(({ key, label, branch }) => {
const element = BRANCH_ELEMENT[branch];
const gen = generals.get(branch);
const xunkongStatus = xunKong.includes(branch) ? '旬空' : '不空';
const seasonState = getSeasonState(element, monthBranch);
return {
key: `liuren:transmission:${key}`,
status: '已计算',
stage: key,
stageLabel: label,
branch,
element,
general: gen,
generalNature: gen?.nature || '',
xunkongStatus,
seasonState,
promptText: `${label}:${branch}(${element}),乘${generalBrief(gen)},${xunkongStatus},月令${seasonState}`,
sources: ['三传取法', '五行旺衰', '旬空判断', '天将排布'],
limitation: TRANSMISSION_FACT_LIMITATION,
};
});
}
function buildTransitionFacts(transmissions: TransmissionEvidence[]): TransitionFact[] {
const transitions: TransitionFact[] = [];
for (let i = 0; i < transmissions.length - 1; i++) {
const from = transmissions[i];
const to = transmissions[i + 1];
const elementRelation = getElementRelation(from.branch, to.branch);
const relationChanged = from.element !== to.element;
transitions.push({
key: `liuren:transition:${from.stage}-${to.stage}`,
status: '连续',
fromStage: from.stage,
toStage: to.stage,
fromBranch: from.branch,
toBranch: to.branch,
fromElement: from.element,
toElement: to.element,
elementRelation,
fromGeneral: generalBrief(from.general),
toGeneral: generalBrief(to.general),
promptText: `${from.stageLabel}→${to.stageLabel}:${from.branch}(${from.element})→${to.branch}(${to.element}),${elementRelation};将${generalBrief(from.general)}→${generalBrief(to.general)}`,
sources: ['三传五行关系', '天将变化'],
limitation: TRANSITION_FACT_LIMITATION,
});
}
return transitions;
}
function buildCounterEvidence(
sike: SiKeResult,
sanchuan: SanChuanResult,
generals: Map<Branch, TianJiang>,
xunKong: Branch[],
monthBranch: Branch,
): CounterEvidenceFact[] {
const counters: CounterEvidenceFact[] = [];
// 三传旬空
const allTransmissions: { label: string; branch: Branch }[] = [
{ label: '初传', branch: sanchuan.initial },
{ label: '中传', branch: sanchuan.middle },
{ label: '末传', branch: sanchuan.final },
];
for (const t of allTransmissions) {
if (xunKong.includes(t.branch)) {
counters.push({
key: `liuren:counter:xunkong:${t.label}`,
ownerStageKey: t.label,
type: '旬空',
status: '已触发',
detail: `${t.label}${t.branch}旬空,事象虚而不实`,
promptText: `反证:${t.label}${t.branch}旬空,该阶段事象有落空之虞`,
sources: ['旬空起法'],
limitation: COUNTER_FACT_LIMITATION,
});
}
}
// 三传休囚
const transmissionElements: { label: string; branch: Branch }[] = [
{ label: '初传', branch: sanchuan.initial },
{ label: '中传', branch: sanchuan.middle },
{ label: '末传', branch: sanchuan.final },
];
for (const t of transmissionElements) {
const el = BRANCH_ELEMENT[t.branch];
const state = getSeasonState(el, monthBranch);
if (state === '休' || state === '囚' || state === '死') {
counters.push({
key: `liuren:counter:season:${t.label}`,
ownerStageKey: t.label,
type: '休囚',
status: '已触发',
detail: `${t.label}${t.branch}(${el})月令${state},不得时`,
promptText: `反证:${t.label}${t.branch}(${el})月令${state},该阶段不得时令助力`,
sources: ['月令旺衰'],
limitation: COUNTER_FACT_LIMITATION,
});
}
}
// 四课中白虎临课
for (const lesson of sike.lessons) {
const gen = generals.get(lesson.upper);
if (gen && gen.name === '白虎') {
counters.push({
key: `liuren:counter:baihu:lesson${lesson.index}`,
ownerStageKey: `第${lesson.index}课`,
type: '白虎临课',
status: '已触发',
detail: `第${lesson.index}课上神${lesson.upper}乘白虎`,
promptText: `反证:第${lesson.index}课${lesson.lower}→${lesson.upper}乘白虎,主凶险、伤灾、丧服之象`,
sources: ['天将排布', '白虎主事'],
limitation: COUNTER_FACT_LIMITATION,
});
}
}
// 四课中天空临课
for (const lesson of sike.lessons) {
const gen = generals.get(lesson.upper);
if (gen && gen.name === '天空') {
counters.push({
key: `liuren:counter:tiankong:lesson${lesson.index}`,
ownerStageKey: `第${lesson.index}课`,
type: '天空临课',
status: '已触发',
detail: `第${lesson.index}课上神${lesson.upper}乘天空`,
promptText: `反证:第${lesson.index}课${lesson.lower}→${lesson.upper}乘天空,主虚诈、不实、空亡之象`,
sources: ['天将排布', '天空主事'],
limitation: COUNTER_FACT_LIMITATION,
});
}
}
// 四课中玄武临课
for (const lesson of sike.lessons) {
const gen = generals.get(lesson.upper);
if (gen && gen.name === '玄武') {
counters.push({
key: `liuren:counter:xuanwu:lesson${lesson.index}`,
ownerStageKey: `第${lesson.index}课`,
type: '玄武临课',
status: '已触发',
detail: `第${lesson.index}课上神${lesson.upper}乘玄武`,
promptText: `反证:第${lesson.index}课${lesson.lower}→${lesson.upper}乘玄武,主暗昧、盗失、欺诈之象`,
sources: ['天将排布', '玄武主事'],
limitation: COUNTER_FACT_LIMITATION,
});
}
}
// 四课冲克
for (const lesson of sike.lessons) {
if (hasChong(lesson.lower, lesson.upper)) {
counters.push({
key: `liuren:counter:chong:lesson${lesson.index}`,
ownerStageKey: `第${lesson.index}课`,
type: '冲克',
status: '已触发',
detail: `第${lesson.index}课${lesson.lower}与${lesson.upper}相冲`,
promptText: `反证:第${lesson.index}课上下神${lesson.lower}↔${lesson.upper}相冲,主动荡、分离、不稳`,
sources: ['地支六冲'],
limitation: COUNTER_FACT_LIMITATION,
});
}
}
// 三传脱耗(初传生日干所寄之支,即泄气)
const ganJiGong = STEM_JIGONG[sike.dayStem];
for (const t of allTransmissions) {
const relation = getElementRelation(t.branch, ganJiGong);
if (relation.includes('生') && WUXING_SHENG[BRANCH_ELEMENT[t.branch]] === BRANCH_ELEMENT[ganJiGong]) {
// 初传五行生日干所寄五行 = 脱耗
}
}
// 三传受克(被日干所寄之支克制,不算反证;反过来,三传克日干寄宫才算)
for (const t of allTransmissions) {
const tEl = BRANCH_ELEMENT[t.branch];
const gEl = BRANCH_ELEMENT[ganJiGong];
if (WUXING_KE[gEl] === tEl) {
// 日干克三传 → 三传受克
counters.push({
key: `liuren:counter:shouke:${t.label}`,
ownerStageKey: t.label,
type: '受克',
status: '已触发',
detail: `${t.label}${t.branch}(${tEl})受日干${sike.dayStem}(${gEl})克制`,
promptText: `反证:${t.label}${t.branch}(${tEl})受日干${sike.dayStem}(${gEl})克制,该阶段受制`,
sources: ['五行克关系'],
limitation: COUNTER_FACT_LIMITATION,
});
}
}
return counters;
}
function buildTimingFacts(
sanchuan: SanChuanResult,
xunKong: Branch[],
monthBranch: Branch,
): TimingFact[] {
const facts: TimingFact[] = [];
const initialElement = BRANCH_ELEMENT[sanchuan.initial];
const initialState = getSeasonState(initialElement, monthBranch);
// 初传旺衰
facts.push({
key: 'liuren:timing:initialWang',
order: 1,
type: '初传旺衰',
status: '已计算',
rawText: `初传${sanchuan.initial}(${initialElement})月令${initialState}`,
promptText: `应期参考:初传${sanchuan.initial}(${initialElement})月令${initialState},${initialState === '旺' || initialState === '相' ? '初传得令,应期较近' : '初传失令,应期较远或需待旺时'}`,
sources: ['初传五行', '月令旺衰'],
limitation: TIMING_FACT_LIMITATION,
});
// 旬空填实
if (xunKong.includes(sanchuan.initial)) {
facts.push({
key: 'liuren:timing:xunkongFill',
order: 2,
type: '旬空填实',
status: '已计算',
rawText: `初传${sanchuan.initial}旬空,需待填实`,
promptText: `应期参考:初传${sanchuan.initial}旬空,事之初虚而不实,需待该支当令或日辰填实方应`,
sources: ['旬空起法', '填实规则'],
limitation: TIMING_FACT_LIMITATION,
});
}
// 冲合时机
const finalBranch = sanchuan.final;
const finalChong = BRANCH_CHONG[finalBranch];
facts.push({
key: 'liuren:timing:chongHe',
order: 3,
type: '冲合时机',
status: '已计算',
rawText: `末传${finalBranch},冲${finalChong}`,
promptText: `应期参考:末传${finalBranch},逢${finalChong}冲则动,逢合则应;冲主动,合主成`,
sources: ['地支六合', '地支六冲'],
limitation: TIMING_FACT_LIMITATION,
});
// 月令助力
facts.push({
key: 'liuren:timing:monthHelp',
order: 4,
type: '月令助力',
status: '已计算',
rawText: `月支${BRANCHES[(branchIndex(monthBranch) + 1) % 12]}(简化)`,
promptText: `应期参考:当月月支对初传${sanchuan.initial}(${initialElement})的影响,得令则助力,失令则受制`,
sources: ['月令五行'],
limitation: TIMING_FACT_LIMITATION,
});
return facts;
}
function buildFocusFacts(
question: string | undefined,
sike: SiKeResult,
sanchuan: SanChuanResult,
plate: PlatePosition[],
generals: Map<Branch, TianJiang>,
monthBranch: Branch,
): FocusFact[] {
if (!question) return [];
const facts: FocusFact[] = [];
const q = question.toLowerCase();
// 问事分类 → 类神映射
const categoryMap: { pattern: RegExp; category: string; targetBranch: Branch; note: string }[] = [
{ pattern: /财|钱|利|生意|投资|交易/, category: '求财', targetBranch: '辰', note: '财以日干所克为类神,辰为水库财源' },
{ pattern: /官|升|职|仕|考|面试|领导/, category: '求官', targetBranch: '酉', note: '官以克日干者为类神' },
{ pattern: /病|疾|医|健|身体/, category: '疾病', targetBranch: '巳', note: '病以日干及天医为类神' },
{ pattern: /婚|恋|爱|对象|配偶|嫁娶/, category: '婚姻', targetBranch: '午', note: '婚以干支上神合为类神' },
{ pattern: /行|出行|旅|出差|路上/, category: '出行', targetBranch: '卯', note: '出行以天罡、驿马为类神' },
{ pattern: /失|丢|找|遗|寻/, category: '寻物', targetBranch: '申', note: '寻物以玄武所乘为类神' },
{ pattern: /讼|法|官司|诉讼|纠纷/, category: '诉讼', targetBranch: '寅', note: '讼以朱雀为类神' },
];
for (const { pattern, category, targetBranch, note } of categoryMap) {
if (pattern.test(q)) {
const gen = generals.get(targetBranch);
const element = BRANCH_ELEMENT[targetBranch];
const state = getSeasonState(element, monthBranch);
facts.push({
key: `liuren:focus:${category}`,
status: '已定位',
category,
targetBranch,
targetElement: element,
targetGeneral: gen?.name,
focusNote: note,
promptText: `类神焦点【${category}】:${targetBranch}(${element}),乘${generalBrief(gen)},月令${state};${note}`,
sources: ['问事分类', '类神传统规则', '天将排布'],
limitation: FOCUS_FACT_LIMITATION,
});
break; // 只取第一个匹配
}
}
return facts;
}
// ═══════════════════════════════════════════
// 格式化输出
// ═══════════════════════════════════════════
/** 将证据链格式化为可读文本段落 */
export function formatEvidenceText(ev: LiurenEvidenceAnalysis): string {
let text = `\n═══ 证 据 链 ═══\n\n`;
// 起盘参数
text += `【起盘参数】${ev.calculation.promptText}\n\n`;
// 天地盘(压缩为一行摘要)
text += `【天地盘】`;
for (const f of ev.plateFacts) {
text += `${f.earthBranch}→${f.heavenBranch}(${f.general?.name || '-'}) `;
}
text += `\n\n`;
// 四课
text += `【四课证据】\n`;
for (const f of ev.lessonFacts) {
text += ` ${f.promptText}\n`;
}
text += `\n`;
// 三传
text += `【三传证据】\n`;
for (const f of ev.transmissionFacts) {
text += ` ${f.promptText}\n`;
}
text += `\n`;
// 推进
if (ev.transitionFacts.length > 0) {
text += `【传间推进】\n`;
for (const f of ev.transitionFacts) {
text += ` ${f.promptText}\n`;
}
text += `\n`;
}
// 反证
text += `【反证】${ev.counterSummary.detail}\n`;
for (const f of ev.counterEvidenceFacts) {
text += ` ⚠️ ${f.promptText}\n`;
}
text += `\n`;
// 应期
text += `【应期参考】\n`;
for (const f of ev.timingFacts) {
text += ` ${f.promptText}\n`;
}
text += `\n`;
// 类神焦点
if (ev.focusFacts.length > 0) {
text += `【类神焦点】\n`;
for (const f of ev.focusFacts) {
text += ` ${f.promptText}\n`;
}
text += `\n`;
}
// 汇总
text += `【证据链汇总】共${ev.summary.factCount}条事实:`;
text += `天地盘${ev.summary.plateCount}条,`;
text += `四课${ev.summary.lessonCount}条,`;
text += `三传${ev.summary.transmissionCount}条,`;
text += `推进${ev.summary.transitionCount}条,`;
text += `反证${ev.summary.counterCount}条,`;
text += `应期${ev.summary.timingCount}条`;
if (ev.summary.focusCount > 0) text += `,类神${ev.summary.focusCount}条`;
text += `\n`;
return text;
}
+9
View File
@@ -3,6 +3,7 @@ import { buildPlate, getYueJiang, formatPlate } from './plate.js';
import { assignGenerals, formatGenerals, getNoblemanBranch, isDaytime } from './tianjiang.js';
import { buildSiKe, STEM_JIGONG } from './sike.js';
import { buildSanChuan } from './sanchuan.js';
import { buildEvidence, formatEvidenceText } from './evidence.js';
// CLI 参数解析
const args = process.argv.slice(2);
@@ -57,6 +58,14 @@ try {
}, null, 2));
} else {
console.log(formatAsText(dayStem, dayBranch, hourBranch, month, yueJiang, plate, heavenBranches, sike, sanchuan, generals, noblemanBranch, xunKong, question));
// 证据链
const evidence = buildEvidence({
dayStem, dayBranch, hourBranch, month, yueJiang,
plate, heavenBranches, sike, sanchuan,
generals, noblemanBranch, xunKong, question,
});
console.log(formatEvidenceText(evidence));
}
} catch (e: any) {
console.error('排盘错误:', e.message);