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 { assignGenerals, formatGenerals, getNoblemanBranch, isDaytime } from './tianjiang.js';
|
||||||
import { buildSiKe, STEM_JIGONG } from './sike.js';
|
import { buildSiKe, STEM_JIGONG } from './sike.js';
|
||||||
import { buildSanChuan } from './sanchuan.js';
|
import { buildSanChuan } from './sanchuan.js';
|
||||||
|
import { buildEvidence, formatEvidenceText } from './evidence.js';
|
||||||
|
|
||||||
// CLI 参数解析
|
// CLI 参数解析
|
||||||
const args = process.argv.slice(2);
|
const args = process.argv.slice(2);
|
||||||
@@ -57,6 +58,14 @@ try {
|
|||||||
}, null, 2));
|
}, null, 2));
|
||||||
} else {
|
} else {
|
||||||
console.log(formatAsText(dayStem, dayBranch, hourBranch, month, yueJiang, plate, heavenBranches, sike, sanchuan, generals, noblemanBranch, xunKong, question));
|
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) {
|
} catch (e: any) {
|
||||||
console.error('排盘错误:', e.message);
|
console.error('排盘错误:', e.message);
|
||||||
|
|||||||
Reference in new issue
Block a user