feat: 大六壬证据链架构
This commit is contained in:
1 parent
c0665c5ddf
commit
b03d84d479
2 files changed
+862
No files matched your search
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user