import { ELEMENT_LABEL, STEM_INFO } from './constants'; import { createChart } from './chart'; import { getElementRelation, isSupportingRelation } from './relations'; import type { BaziChart, Confidence, EvidenceCategory, EvidenceImpact, FiveElement, FourPillarsInput, Pillar, SpecialPatternReview, StrengthAnalysis, StrengthEvidence, StrengthState, } from './types'; const IMPACT_UNITS: Record = { major: 3, moderate: 2, minor: 1, }; const STRENGTH_LABEL: Record = { 'extremely-weak': '极弱', weak: '偏弱', balanced: '相对中和', strong: '偏强', 'extremely-strong': '极强', }; const RELATION_LABEL = { companion: '同类比劫', resource: '印星生扶', output: '食伤泄身', wealth: '财星耗身', officer: '官杀克身', } as const; function addEvidence( evidence: StrengthEvidence[], params: Omit, ): void { evidence.push({ ...params, id: `E${String(evidence.length + 1).padStart(2, '0')}`, decisionUnits: IMPACT_UNITS[params.impact], }); } function classifyStrength(delta: number): StrengthState { if (delta >= 6) return 'extremely-strong'; if (delta >= 2) return 'strong'; if (delta <= -6) return 'extremely-weak'; if (delta <= -2) return 'weak'; return 'balanced'; } function confidenceFrom(delta: number, evidenceCount: number): Confidence { if (evidenceCount < 5) return 'low'; if (Math.abs(delta) >= 5) return 'high'; return 'medium'; } function supportingAndOpposingElements(dayMaster: FiveElement): { supporting: FiveElement[]; opposing: FiveElement[]; } { const elements: FiveElement[] = ['wood', 'fire', 'earth', 'metal', 'water']; return { supporting: elements.filter((element) => isSupportingRelation(getElementRelation(dayMaster, element)), ), opposing: elements.filter((element) => !isSupportingRelation(getElementRelation(dayMaster, element)), ), }; } function collectMonthCommand(chart: BaziChart, evidence: StrengthEvidence[]): void { const monthMainQi = chart.pillars.month.branch.hiddenStems[0]; if (!monthMainQi) return; const relation = getElementRelation(chart.dayMaster.element, monthMainQi.element); const supporting = isSupportingRelation(relation); addEvidence(evidence, { category: 'season', relation, side: supporting ? 'supporting' : 'opposing', impact: 'major', source: 'month.branch.hiddenStems[0]', description: `月令${chart.pillars.month.branch.name}以${monthMainQi.name}${ELEMENT_LABEL[monthMainQi.element]}为本气,${RELATION_LABEL[relation]},日主${supporting ? '得令或得生' : '失令并受泄耗克'}。`, }); } function collectVisibleStems(chart: BaziChart, evidence: StrengthEvidence[]): void { const pillars: Pillar[] = [chart.pillars.year, chart.pillars.month, chart.pillars.hour]; for (const pillar of pillars) { const relation = getElementRelation(chart.dayMaster.element, pillar.stem.element); const supporting = isSupportingRelation(relation); addEvidence(evidence, { category: supporting ? 'support' : 'pressure', relation, side: supporting ? 'supporting' : 'opposing', impact: 'moderate', source: `${pillar.key}.stem`, description: `${pillar.key}干${pillar.stem.name}${ELEMENT_LABEL[pillar.stem.element]}透出,为${pillar.stem.tenGod},${RELATION_LABEL[relation]}。`, }); } } function hiddenImpact(order: 'main' | 'middle' | 'residual'): EvidenceImpact { return order === 'main' ? 'moderate' : 'minor'; } function hiddenCategory(relation: ReturnType): EvidenceCategory { if (relation === 'companion') return 'root'; if (relation === 'resource') return 'support'; return 'pressure'; } function collectHiddenStems(chart: BaziChart, evidence: StrengthEvidence[]): void { const pillars: Pillar[] = [ chart.pillars.year, chart.pillars.month, chart.pillars.day, chart.pillars.hour, ]; for (const pillar of pillars) { pillar.branch.hiddenStems.forEach((hiddenStem, index) => { const relation = getElementRelation(chart.dayMaster.element, hiddenStem.element); const supporting = isSupportingRelation(relation); addEvidence(evidence, { category: hiddenCategory(relation), relation, side: supporting ? 'supporting' : 'opposing', impact: hiddenImpact(hiddenStem.order), source: `${pillar.key}.branch.hiddenStems[${index}]`, description: `${pillar.key}支${pillar.branch.name}藏${hiddenStem.name}${ELEMENT_LABEL[hiddenStem.element]}(${hiddenStem.order}),为${hiddenStem.tenGod},${RELATION_LABEL[relation]}。`, }); }); } } function detectSpecialPattern( evidence: StrengthEvidence[], supporting: number, opposing: number, ): SpecialPatternReview { const hasRoot = evidence.some((item) => item.category === 'root'); if (!hasRoot && supporting <= 4 && opposing >= 12) { return { status: 'review-required', candidate: 'following-weak', reason: '日主无同类根,生扶证据极少而泄耗克成势;可能涉及从弱或假从,v1 不自动定格。', }; } if (opposing <= 3 && supporting >= 12) { return { status: 'review-required', candidate: 'following-strong', reason: '生扶成势且泄耗克证据极少;可能涉及从强、专旺或假从,v1 不自动定格。', }; } return { status: 'not-indicated', candidate: null, reason: '常规旺衰证据中未触发从强或从弱候选条件。', }; } export function analyzeChart(chart: BaziChart): StrengthAnalysis { const evidence: StrengthEvidence[] = []; collectMonthCommand(chart, evidence); collectVisibleStems(chart, evidence); collectHiddenStems(chart, evidence); const supporting = evidence .filter((item) => item.side === 'supporting') .reduce((sum, item) => sum + item.decisionUnits, 0); const opposing = evidence .filter((item) => item.side === 'opposing') .reduce((sum, item) => sum + item.decisionUnits, 0); const delta = supporting - opposing; const strength = classifyStrength(delta); const specialPattern = detectSpecialPattern(evidence, supporting, opposing); const elements = supportingAndOpposingElements(chart.dayMaster.element); return { status: specialPattern.status === 'review-required' ? 'needs-special-pattern-review' : 'completed', strength, strengthLabel: STRENGTH_LABEL[strength], confidence: specialPattern.status === 'review-required' ? 'low' : confidenceFrom(delta, evidence.length), dayMaster: chart.dayMaster, supportingElements: elements.supporting, opposingElements: elements.opposing, evidence, decisionBalance: { supporting, opposing, delta, thresholds: { extremelyStrong: 6, strong: 2, weak: -2, extremelyWeak: -6, }, }, specialPattern, }; } export function analyzePillars(pillars: FourPillarsInput): StrengthAnalysis { return analyzeChart(createChart(pillars)); }