import { BRANCH_ELEMENT, BRANCHES, HIDDEN_STEMS, STEM_INFO, STEMS, } from './constants'; import { getTenGod } from './relations'; import type { BaziChart, EarthlyBranchName, FourPillarsInput, HeavenlyStemName, HiddenStem, Pillar, PillarKey, } from './types'; function isStem(value: string): value is HeavenlyStemName { return (STEMS as readonly string[]).includes(value); } function isBranch(value: string): value is EarthlyBranchName { return (BRANCHES as readonly string[]).includes(value); } export function parseGanZhi( ganZhi: string, source: string, ): [HeavenlyStemName, EarthlyBranchName] { const characters = [...ganZhi.trim()]; const stem = characters[0]; const branch = characters[1]; if (characters.length !== 2 || !stem || !branch || !isStem(stem) || !isBranch(branch)) { throw new RangeError(`${source} 必须是有效的两字干支,例如“甲子”;收到:${ganZhi}`); } return [stem, branch]; } function hiddenOrder(index: number): HiddenStem['order'] { if (index === 0) return 'main'; if (index === 1) return 'middle'; return 'residual'; } function createPillar( key: PillarKey, ganZhi: string, dayStem: HeavenlyStemName, ): Pillar { const [stemName, branchName] = parseGanZhi(ganZhi, key); const stemInfo = STEM_INFO[stemName]; const hiddenStems = HIDDEN_STEMS[branchName].map((hiddenStem, index) => { const info = STEM_INFO[hiddenStem]; return { name: hiddenStem, element: info.element, yinYang: info.yinYang, tenGod: getTenGod(dayStem, hiddenStem), order: hiddenOrder(index), } satisfies HiddenStem; }); return { key, ganZhi: `${stemName}${branchName}`, stem: { name: stemName, element: stemInfo.element, yinYang: stemInfo.yinYang, tenGod: key === 'day' ? '日主' : getTenGod(dayStem, stemName), }, branch: { name: branchName, element: BRANCH_ELEMENT[branchName], hiddenStems, }, }; } export function createChart(pillars: FourPillarsInput): BaziChart { const [dayStem] = parseGanZhi(pillars.day, 'day'); const dayInfo = STEM_INFO[dayStem]; const year = createPillar('year', pillars.year, dayStem); const month = createPillar('month', pillars.month, dayStem); const day = createPillar('day', pillars.day, dayStem); const hour = createPillar('hour', pillars.hour, dayStem); return { pillars: { year, month, day, hour }, dayMaster: { stem: dayStem, element: dayInfo.element, yinYang: dayInfo.yinYang, }, }; }