Spaces:
Sleeping
Sleeping
| import { AstrolabeData, DynamicChainStep, MutagenKey, PalaceData, TimeLayerData, TimeLayerScope } from './types'; | |
| // 十干四化表 (事实字典) | |
| export const SIHUA_DICT: Record<string, { 禄: string; 权: string; 科: string; 忌: string }> = { | |
| '甲': { 禄: '廉贞', 权: '破军', 科: '武曲', 忌: '太阳' }, | |
| '乙': { 禄: '天机', 权: '天梁', 科: '紫微', 忌: '太阴' }, | |
| '丙': { 禄: '天同', 权: '天机', 科: '文昌', 忌: '廉贞' }, | |
| '丁': { 禄: '太阴', 权: '天同', 科: '天机', 忌: '巨门' }, | |
| '戊': { 禄: '贪狼', 权: '太阴', 科: '右弼', 忌: '天机' }, | |
| '己': { 禄: '武曲', 权: '贪狼', 科: '天梁', 忌: '文曲' }, | |
| '庚': { 禄: '太阳', 权: '武曲', 科: '太阴', 忌: '天同' }, | |
| '辛': { 禄: '巨门', 权: '太阳', 科: '文曲', 忌: '文昌' }, | |
| '壬': { 禄: '天梁', 权: '紫微', 科: '左辅', 忌: '武曲' }, | |
| '癸': { 禄: '破军', 权: '巨门', 科: '太阴', 忌: '贪狼' }, | |
| }; | |
| // 飞星事实结构 | |
| 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<string>([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]; | |
| } | |
| } | |