import { AstrolabeData, DynamicChainStep, MutagenKey, PalaceData, TimeLayerData, TimeLayerScope } from './types'; // 十干四化表 (事实字典) export const SIHUA_DICT: Record = { '甲': { 禄: '廉贞', 权: '破军', 科: '武曲', 忌: '太阳' }, '乙': { 禄: '天机', 权: '天梁', 科: '紫微', 忌: '太阴' }, '丙': { 禄: '天同', 权: '天机', 科: '文昌', 忌: '廉贞' }, '丁': { 禄: '太阴', 权: '天同', 科: '天机', 忌: '巨门' }, '戊': { 禄: '贪狼', 权: '太阴', 科: '右弼', 忌: '天机' }, '己': { 禄: '武曲', 权: '贪狼', 科: '天梁', 忌: '文曲' }, '庚': { 禄: '太阳', 权: '武曲', 科: '太阴', 忌: '天同' }, '辛': { 禄: '巨门', 权: '太阳', 科: '文曲', 忌: '文昌' }, '壬': { 禄: '天梁', 权: '紫微', 科: '左辅', 忌: '武曲' }, '癸': { 禄: '破军', 权: '巨门', 科: '太阴', 忌: '贪狼' }, }; // 飞星事实结构 export interface FlyingFact { scope: TimeLayerScope; sourceStem: string; fromPalaceIndex: number; fromPalaceName: string; toPalaceIndex: number; toPalaceName: string; mutagen: MutagenKey; star: string; isSelfMutagen: boolean; // 是否为自化 depth: number; } /** * 动态核心层:只负责计算星曜飞布事实,不涉及命理解读 */ export class DynamicCore { private timeLayerData: TimeLayerData; private astrolabe: AstrolabeData; constructor(timeLayerData: TimeLayerData) { this.timeLayerData = timeLayerData; this.astrolabe = timeLayerData.original; } // 1. 获取生年四化落点事实 public getShengNianSihuaFacts(): Record<'禄'|'权'|'科'|'忌', PalaceData | null> { const facts: Record<'禄'|'权'|'科'|'忌', PalaceData | null> = { 禄: null, 权: null, 科: null, 忌: null }; if (!this.astrolabe || !this.astrolabe.palaces) return facts; this.astrolabe.palaces.forEach(palace => { [...(palace.majorStars || []), ...(palace.minorStars || [])].forEach(star => { if (star.mutagen) { facts[star.mutagen as '禄'|'权'|'科'|'忌'] = palace; } }); }); return facts; } public getScopeMutagenFacts(scope: TimeLayerScope): FlyingFact[] { if (scope === 'original') { return this.getOriginalMutagenFacts(); } const scopeData = this.timeLayerData[scope]; if (!scopeData) { return []; } const fromPalace = this.findPalaceByName(scopeData.targetPalaceName); if (!fromPalace) { return []; } return (['禄', '权', '科', '忌'] as const) .map((mutagen) => this.buildFact(scope, fromPalace, scopeData.heavenlyStem, mutagen, 0)) .filter((fact): fact is FlyingFact => !!fact); } public traceMutagenChain( scope: TimeLayerScope, mutagen: MutagenKey = '忌', maxDepth = 3 ): DynamicChainStep[] { const firstFact = this.getScopeMutagenFacts(scope).find((fact) => fact.mutagen === mutagen); if (!firstFact) { return []; } const chain: FlyingFact[] = [firstFact]; const visitedPalaces = new Set([firstFact.fromPalaceName, firstFact.toPalaceName]); let currentPalace = this.findPalaceByName(firstFact.toPalaceName); for (let depth = 1; depth < maxDepth; depth += 1) { if (!currentPalace) { break; } const nextFact = this.buildFact(scope, currentPalace, currentPalace.heavenlyStem, mutagen, depth); if (!nextFact) { break; } chain.push(nextFact); if (nextFact.isSelfMutagen || visitedPalaces.has(nextFact.toPalaceName)) { break; } visitedPalaces.add(nextFact.toPalaceName); currentPalace = this.findPalaceByName(nextFact.toPalaceName); } return chain.map((fact) => ({ scope: fact.scope, sourceStem: fact.sourceStem, fromPalaceName: fact.fromPalaceName, toPalaceName: fact.toPalaceName, mutagen: fact.mutagen, star: fact.star, isSelfMutagen: fact.isSelfMutagen, })); } public getZiHuaFacts(): FlyingFact[] { if (!this.astrolabe || !this.astrolabe.palaces) { return []; } return this.astrolabe.palaces.flatMap((palace) => this.getFlyingFactsFromPalace(palace.index).filter((fact) => fact.isSelfMutagen) ); } // 2. 计算指定宫位的飞出事实 (含自化) public getFlyingFactsFromPalace(fromIndex: number): FlyingFact[] { const facts: FlyingFact[] = []; if (!this.astrolabe || !this.astrolabe.palaces) return facts; const fromPalace = this.astrolabe.palaces.find(p => p.index === fromIndex); if (!fromPalace) return facts; (['禄', '权', '科', '忌'] as const).forEach(mutagen => { const fact = this.buildFact('original', fromPalace, fromPalace.heavenlyStem, mutagen, 0); if (fact) { facts.push(fact); } }); return facts; } private getOriginalMutagenFacts(): FlyingFact[] { const shengNianGan = this.astrolabe.chineseDate.split(' ')[0][0]; const fromPalace = this.astrolabe.palaces.find((palace) => palace.heavenlyStem === shengNianGan); if (!fromPalace) { return []; } return (['禄', '权', '科', '忌'] as const) .map((mutagen) => this.buildFact('original', fromPalace, shengNianGan, mutagen, 0)) .filter((fact): fact is FlyingFact => !!fact); } private buildFact( scope: TimeLayerScope, fromPalace: PalaceData, sourceStem: string, mutagen: MutagenKey, depth: number ): FlyingFact | undefined { const sihuaMap = SIHUA_DICT[sourceStem]; if (!sihuaMap) { return undefined; } const targetStar = sihuaMap[mutagen]; const toPalace = this.findPalaceByStar(targetStar); if (!toPalace) { return undefined; } return { scope, sourceStem, fromPalaceIndex: fromPalace.index, fromPalaceName: this.cleanPalaceName(fromPalace.name), toPalaceIndex: toPalace.index, toPalaceName: this.cleanPalaceName(toPalace.name), mutagen, star: targetStar, isSelfMutagen: fromPalace.index === toPalace.index, depth, }; } // 3. 寻找某颗星所在的宫位 public findPalaceByStar(starName: string): PalaceData | undefined { if (!this.astrolabe || !this.astrolabe.palaces) return undefined; return this.astrolabe.palaces.find(p => [...(p.majorStars || []), ...(p.minorStars || [])].some(s => s.name === starName) ); } public findPalaceByName(palaceName: string): PalaceData | undefined { if (!this.astrolabe || !this.astrolabe.palaces) { return undefined; } return this.astrolabe.palaces.find((palace) => this.cleanPalaceName(palace.name) === this.cleanPalaceName(palaceName)); } private cleanPalaceName(name: string) { return name.split('(')[0]; } }