사주 풀이 고도화, NAS 배포 자동화
This commit is contained in:
@@ -1,316 +1,386 @@
|
||||
import { SajuData } from './saju-calculator';
|
||||
|
||||
/**
|
||||
* AI 기반 사주 해석
|
||||
* 사주 데이터를 분석하여 상세한 해석 제공
|
||||
*/
|
||||
import {
|
||||
SajuData, FIVE_ELEMENTS, HEAVENLY_STEMS,
|
||||
getHiddenStems, getAllHiddenStems,
|
||||
analyzeBranchInteractions, calculateShinsal, calculateGongmang,
|
||||
getYearGanzi, FIVE_ELEMENTS_KR, EARTHLY_BRANCHES_KR, EARTHLY_BRANCHES,
|
||||
BranchInteraction, Shinsal,
|
||||
} from './saju-calculator';
|
||||
import { DaeunPillar } from './daeun-calculator';
|
||||
|
||||
interface Interpretation {
|
||||
personality: string[];
|
||||
strengths: string[];
|
||||
weaknesses: string[];
|
||||
career: string[];
|
||||
relationships: string[];
|
||||
health: string[];
|
||||
wealth: string[];
|
||||
advice: string[];
|
||||
// ============================================================
|
||||
// 오행 밸런스 정밀 분석 (가중치 적용)
|
||||
// ============================================================
|
||||
|
||||
export interface ElementBalance {
|
||||
木: number;
|
||||
火: number;
|
||||
土: number;
|
||||
金: number;
|
||||
水: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 오행 균형 분석
|
||||
* 가중치 적용 오행 점수 계산
|
||||
* - 천간: 1.0
|
||||
* - 지지 본기(정기): 1.0
|
||||
* - 지장간 중기: 0.5
|
||||
* - 지장간 여기: 0.3
|
||||
*/
|
||||
function analyzeElementBalance(saju: SajuData): { [key: string]: number } {
|
||||
const elements = { 木: 0, 火: 0, 土: 0, 金: 0, 水: 0 };
|
||||
export function calculateDetailedElementBalance(saju: SajuData): ElementBalance {
|
||||
const balance: ElementBalance = { 木: 0, 火: 0, 土: 0, 金: 0, 水: 0 };
|
||||
|
||||
// 사주팔자의 각 기둥에서 오행 카운트
|
||||
elements[saju.year.element]++;
|
||||
elements[saju.month.element]++;
|
||||
elements[saju.day.element]++;
|
||||
if (saju.hour) elements[saju.hour.element]++;
|
||||
// 천간 오행 (각 1.0)
|
||||
const stems = [saju.year.stem, saju.month.stem, saju.day.stem];
|
||||
if (saju.hour) stems.push(saju.hour.stem);
|
||||
|
||||
return elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* 십성 분석
|
||||
*/
|
||||
function analyzeTenGods(saju: SajuData): { [key: string]: number } {
|
||||
const tenGods: { [key: string]: number } = {};
|
||||
|
||||
[saju.year.tenGod, saju.month.tenGod, saju.hour?.tenGod].forEach(god => {
|
||||
if (god && god !== '일간') {
|
||||
tenGods[god] = (tenGods[god] || 0) + 1;
|
||||
}
|
||||
});
|
||||
|
||||
return tenGods;
|
||||
}
|
||||
|
||||
/**
|
||||
* 일간 기반 성격 해석
|
||||
*/
|
||||
function interpretDayStem(stem: string, element: string): string[] {
|
||||
const interpretations: { [key: string]: string[] } = {
|
||||
'甲': [
|
||||
'큰 나무처럼 곧고 꿋꿋한 성격입니다.',
|
||||
'리더십이 강하고 개척 정신이 뛰어납니다.',
|
||||
'정의감이 강하고 원칙을 중요시합니다.',
|
||||
'때로는 융통성이 부족할 수 있습니다.'
|
||||
],
|
||||
'乙': [
|
||||
'부드러운 풀처럼 유연하고 적응력이 뛰어납니다.',
|
||||
'섬세하고 예술적 감각이 있습니다.',
|
||||
'주변 환경에 잘 적응하며 협력을 중시합니다.',
|
||||
'때로는 우유부단할 수 있습니다.'
|
||||
],
|
||||
'丙': [
|
||||
'태양처럼 밝고 활발한 성격입니다.',
|
||||
'사교성이 뛰어나고 열정적입니다.',
|
||||
'창의적이고 표현력이 풍부합니다.',
|
||||
'때로는 충동적일 수 있습니다.'
|
||||
],
|
||||
'丁': [
|
||||
'촛불처럼 따뜻하고 섬세한 성격입니다.',
|
||||
'예민하고 감수성이 풍부합니다.',
|
||||
'예의 바르고 배려심이 깊습니다.',
|
||||
'때로는 너무 예민할 수 있습니다.'
|
||||
],
|
||||
'戊': [
|
||||
'산처럼 묵직하고 안정적인 성격입니다.',
|
||||
'책임감이 강하고 신뢰할 수 있습니다.',
|
||||
'현실적이고 실용적입니다.',
|
||||
'때로는 고집이 셀 수 있습니다.'
|
||||
],
|
||||
'己': [
|
||||
'밭처럼 포용력 있고 온화한 성격입니다.',
|
||||
'배려심이 깊고 참을성이 강합니다.',
|
||||
'현실적이며 실속을 챙깁니다.',
|
||||
'때로는 소극적일 수 있습니다.'
|
||||
],
|
||||
'庚': [
|
||||
'금속처럼 단단하고 강인한 성격입니다.',
|
||||
'결단력이 있고 추진력이 강합니다.',
|
||||
'정직하고 의리를 중시합니다.',
|
||||
'때로는 융통성이 부족할 수 있습니다.'
|
||||
],
|
||||
'辛': [
|
||||
'보석처럼 고귀하고 섬세한 성격입니다.',
|
||||
'예리하고 통찰력이 뛰어납니다.',
|
||||
'품위 있고 우아함을 추구합니다.',
|
||||
'때로는 까다로울 수 있습니다.'
|
||||
],
|
||||
'壬': [
|
||||
'큰 바다처럼 넓고 깊은 성격입니다.',
|
||||
'지혜롭고 포용력이 있습니다.',
|
||||
'융통성이 있고 적응력이 뛰어납니다.',
|
||||
'때로는 변덕스러울 수 있습니다.'
|
||||
],
|
||||
'癸': [
|
||||
'이슬처럼 섬세하고 조용한 성격입니다.',
|
||||
'지적이고 사려 깊습니다.',
|
||||
'인내심이 강하고 끈기가 있습니다.',
|
||||
'때로는 소심할 수 있습니다.'
|
||||
]
|
||||
};
|
||||
|
||||
return interpretations[stem] || ['독특한 개성을 가진 사람입니다.'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 직업 운세 분석
|
||||
*/
|
||||
function interpretCareer(saju: SajuData, tenGods: { [key: string]: number }): string[] {
|
||||
const career: string[] = [];
|
||||
const element = saju.day.element;
|
||||
|
||||
// 오행 기반 직업 추천
|
||||
const careerByElement: { [key: string]: string[] } = {
|
||||
'木': ['교육', '출판', '디자인', '패션', '임업', '환경'],
|
||||
'火': ['예술', '광고', '방송', '요식업', 'IT', '전자'],
|
||||
'土': ['부동산', '건설', '농업', '유통', '중개', '컨설팅'],
|
||||
'金': ['금융', '법조', '의료', '기계', '자동차', '보석'],
|
||||
'水': ['무역', '물류', '여행', '수산', '음료', '화학']
|
||||
};
|
||||
|
||||
career.push(...careerByElement[element].slice(0, 3).map(c => `${c} 분야에 적성이 있습니다.`));
|
||||
|
||||
// 십성 기반 직업 성향
|
||||
if (tenGods['정관'] || tenGods['편관']) {
|
||||
career.push('조직 생활이나 공직에 적합합니다.');
|
||||
}
|
||||
if (tenGods['정재'] || tenGods['편재']) {
|
||||
career.push('재물 관리나 사업에 능력이 있습니다.');
|
||||
}
|
||||
if (tenGods['식신'] || tenGods['상관']) {
|
||||
career.push('창의적인 일이나 표현하는 직업이 좋습니다.');
|
||||
}
|
||||
if (tenGods['정인'] || tenGods['편인']) {
|
||||
career.push('학문, 연구, 교육 분야가 적합합니다.');
|
||||
for (const stem of stems) {
|
||||
const elem = FIVE_ELEMENTS[stem as keyof typeof FIVE_ELEMENTS] as keyof ElementBalance;
|
||||
if (elem) balance[elem] += 1.0;
|
||||
}
|
||||
|
||||
return career;
|
||||
}
|
||||
// 지지 지장간 (본기 1.0, 중기 0.5, 여기 0.3)
|
||||
const branches = [saju.year.branch, saju.month.branch, saju.day.branch];
|
||||
if (saju.hour) branches.push(saju.hour.branch);
|
||||
|
||||
/**
|
||||
* 대인 관계 분석
|
||||
*/
|
||||
function interpretRelationships(saju: SajuData, tenGods: { [key: string]: number }): string[] {
|
||||
const relationships: string[] = [];
|
||||
|
||||
if (tenGods['비견'] || tenGods['겁재']) {
|
||||
relationships.push('친구나 동료와의 관계가 중요합니다.');
|
||||
relationships.push('경쟁심이 있지만 협력도 잘합니다.');
|
||||
}
|
||||
|
||||
if (tenGods['정관'] || tenGods['편관']) {
|
||||
relationships.push('윗사람의 인정을 받기 쉽습니다.');
|
||||
relationships.push('사회적 명예를 중시합니다.');
|
||||
}
|
||||
|
||||
if (tenGods['정재'] || tenGods['편재']) {
|
||||
if (saju.gender === 'male') {
|
||||
relationships.push('이성과의 인연이 좋습니다.');
|
||||
} else {
|
||||
relationships.push('재물 운이 좋습니다.');
|
||||
const weights = [1.0, 0.5, 0.3];
|
||||
for (const branch of branches) {
|
||||
const hidden = getHiddenStems(branch);
|
||||
for (let i = 0; i < hidden.length; i++) {
|
||||
const elem = FIVE_ELEMENTS[hidden[i] as keyof typeof FIVE_ELEMENTS] as keyof ElementBalance;
|
||||
if (elem) balance[elem] += weights[i] || 0.3;
|
||||
}
|
||||
}
|
||||
|
||||
if (tenGods['정인'] || tenGods['편인']) {
|
||||
if (saju.gender === 'female') {
|
||||
relationships.push('가족과의 유대가 깊습니다.');
|
||||
} else {
|
||||
relationships.push('멘토를 만나기 쉽습니다.');
|
||||
}
|
||||
// 소수점 둘째 자리로 반올림
|
||||
for (const key of Object.keys(balance) as (keyof ElementBalance)[]) {
|
||||
balance[key] = Math.round(balance[key] * 100) / 100;
|
||||
}
|
||||
|
||||
return relationships;
|
||||
return balance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 건강 운세 분석
|
||||
*/
|
||||
function interpretHealth(saju: SajuData, elements: { [key: string]: number }): string[] {
|
||||
const health: string[] = [];
|
||||
const element = saju.day.element;
|
||||
|
||||
// 오행별 건강 주의사항
|
||||
const healthByElement: { [key: string]: string } = {
|
||||
'木': '간, 담낭, 눈 건강에 주의하세요.',
|
||||
'火': '심장, 혈압, 소장 건강에 주의하세요.',
|
||||
'土': '위장, 소화기, 비장 건강에 주의하세요.',
|
||||
'金': '폐, 대장, 피부 건강에 주의하세요.',
|
||||
'水': '신장, 방광, 생식기 건강에 주의하세요.'
|
||||
};
|
||||
|
||||
health.push(healthByElement[element]);
|
||||
|
||||
// 오행 불균형 체크
|
||||
const maxElement = Object.keys(elements).reduce((a, b) =>
|
||||
elements[a] > elements[b] ? a : b
|
||||
);
|
||||
const minElement = Object.keys(elements).reduce((a, b) =>
|
||||
elements[a] < elements[b] ? a : b
|
||||
);
|
||||
|
||||
if (elements[maxElement] - elements[minElement] >= 2) {
|
||||
health.push('오행 균형을 맞추기 위한 식습관 관리가 필요합니다.');
|
||||
}
|
||||
|
||||
health.push('규칙적인 생활과 적절한 운동이 중요합니다.');
|
||||
|
||||
return health;
|
||||
}
|
||||
|
||||
/**
|
||||
* 재물 운세 분석
|
||||
*/
|
||||
function interpretWealth(saju: SajuData, tenGods: { [key: string]: number }): string[] {
|
||||
const wealth: string[] = [];
|
||||
|
||||
if (tenGods['정재']) {
|
||||
wealth.push('정직한 노력으로 재물을 모을 수 있습니다.');
|
||||
wealth.push('월급이나 안정적인 수입이 좋습니다.');
|
||||
}
|
||||
|
||||
if (tenGods['편재']) {
|
||||
wealth.push('사업이나 투자로 재물을 얻을 수 있습니다.');
|
||||
wealth.push('재테크에 관심을 가지면 좋습니다.');
|
||||
}
|
||||
|
||||
if (tenGods['식신'] || tenGods['상관']) {
|
||||
wealth.push('재능을 활용한 수입원이 있습니다.');
|
||||
wealth.push('창의적인 일로 돈을 벌 수 있습니다.');
|
||||
}
|
||||
|
||||
if (!tenGods['정재'] && !tenGods['편재']) {
|
||||
wealth.push('재물보다는 명예나 학문을 추구합니다.');
|
||||
wealth.push('꾸준한 저축이 중요합니다.');
|
||||
}
|
||||
|
||||
return wealth;
|
||||
}
|
||||
|
||||
/**
|
||||
* 종합 조언
|
||||
*/
|
||||
function generateAdvice(saju: SajuData, elements: { [key: string]: number }): string[] {
|
||||
const advice: string[] = [];
|
||||
const element = saju.day.element;
|
||||
|
||||
// 오행별 조언
|
||||
const adviceByElement: { [key: string]: string[] } = {
|
||||
'木': ['아침 산책으로 하루를 시작하세요.', '녹색 식물을 가까이 하세요.', '독서로 마음을 충전하세요.'],
|
||||
'火': ['밝은 색상의 옷을 입으세요.', '사람들과 적극적으로 소통하세요.', '예술 활동을 즐기세요.'],
|
||||
'土': ['규칙적인 식사를 하세요.', '안정적인 계획을 세우세요.', '자연과 가까운 곳에 가세요.'],
|
||||
'金': ['명확한 목표를 설정하세요.', '금속 액세서리를 착용하세요.', '원칙을 지키되 융통성도 발휘하세요.'],
|
||||
'水': ['충분한 수분 섭취를 하세요.', '유연한 사고를 유지하세요.', '명상이나 요가로 마음을 다스리세요.']
|
||||
};
|
||||
|
||||
advice.push(...adviceByElement[element]);
|
||||
|
||||
// 일반적인 조언
|
||||
advice.push('자신의 장점을 살리고 단점을 보완하세요.');
|
||||
advice.push('긍정적인 마인드로 하루를 시작하세요.');
|
||||
|
||||
return advice;
|
||||
}
|
||||
|
||||
/**
|
||||
* 전체 사주 해석 생성
|
||||
*/
|
||||
export function generateInterpretation(saju: SajuData): Interpretation {
|
||||
const elements = analyzeElementBalance(saju);
|
||||
const tenGods = analyzeTenGods(saju);
|
||||
|
||||
const personality = interpretDayStem(saju.day.stem, saju.day.element);
|
||||
|
||||
// 장점과 단점 분리
|
||||
const strengths = personality.filter((_, i) => i < 3);
|
||||
const weaknesses = [personality[3] || '균형 잡힌 성격입니다.'];
|
||||
|
||||
return {
|
||||
personality,
|
||||
strengths,
|
||||
weaknesses,
|
||||
career: interpretCareer(saju, tenGods),
|
||||
relationships: interpretRelationships(saju, tenGods),
|
||||
health: interpretHealth(saju, elements),
|
||||
wealth: interpretWealth(saju, tenGods),
|
||||
advice: generateAdvice(saju, elements)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 오행 균형 점수 계산
|
||||
* 오행 비율(%) 계산
|
||||
*/
|
||||
export function calculateElementScore(saju: SajuData): { [key: string]: number } {
|
||||
const elements = analyzeElementBalance(saju);
|
||||
const total = Object.values(elements).reduce((a, b) => a + b, 0);
|
||||
const balance = calculateDetailedElementBalance(saju);
|
||||
const total = Object.values(balance).reduce((a, b) => a + b, 0);
|
||||
|
||||
const scores: { [key: string]: number } = {};
|
||||
for (const [element, count] of Object.entries(elements)) {
|
||||
scores[element] = Math.round((count / total) * 100);
|
||||
for (const [element, value] of Object.entries(balance)) {
|
||||
scores[element] = total > 0 ? Math.round((value / total) * 100) : 0;
|
||||
}
|
||||
|
||||
return scores;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 신강/신약 자동 판단
|
||||
// ============================================================
|
||||
|
||||
export interface DayMasterStrength {
|
||||
result: '신강' | '신약' | '중화';
|
||||
score: number;
|
||||
reasons: string[];
|
||||
}
|
||||
|
||||
const PRODUCE_MAP: { [key: string]: string } = {
|
||||
'木': '火', '火': '土', '土': '金', '金': '水', '水': '木',
|
||||
};
|
||||
|
||||
function getProducingElement(elem: string): string {
|
||||
for (const [k, v] of Object.entries(PRODUCE_MAP)) {
|
||||
if (v === elem) return k;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 신강/신약 판단
|
||||
*/
|
||||
export function analyzeDayMasterStrength(saju: SajuData): DayMasterStrength {
|
||||
const dayStem = saju.dayStem;
|
||||
const dayElement = FIVE_ELEMENTS[dayStem as keyof typeof FIVE_ELEMENTS];
|
||||
const producingElement = getProducingElement(dayElement);
|
||||
const reasons: string[] = [];
|
||||
let score = 0;
|
||||
|
||||
// 1. 월령 득령 확인
|
||||
const monthBranch = saju.month.branch;
|
||||
const monthHidden = getHiddenStems(monthBranch);
|
||||
const monthMainElement = FIVE_ELEMENTS[monthHidden[0] as keyof typeof FIVE_ELEMENTS];
|
||||
|
||||
if (monthMainElement === dayElement) {
|
||||
score += 3;
|
||||
reasons.push(`월령 득령: 월지 ${saju.month.branchKr}(${monthBranch})의 본기가 일간과 같은 ${FIVE_ELEMENTS_KR[dayElement as keyof typeof FIVE_ELEMENTS_KR]}으로 강한 힘을 받음`);
|
||||
} else if (monthMainElement === producingElement) {
|
||||
score += 2;
|
||||
reasons.push(`월령 득령: 월지 ${saju.month.branchKr}(${monthBranch})의 본기가 일간을 생하는 ${FIVE_ELEMENTS_KR[producingElement as keyof typeof FIVE_ELEMENTS_KR]}으로 힘을 받음`);
|
||||
} else {
|
||||
score -= 2;
|
||||
reasons.push(`월령 실령: 월지 ${saju.month.branchKr}(${monthBranch})의 본기가 일간을 돕지 않음`);
|
||||
}
|
||||
|
||||
// 2. 통근 확인
|
||||
const allBranches = [saju.year.branch, saju.month.branch, saju.day.branch];
|
||||
if (saju.hour) allBranches.push(saju.hour.branch);
|
||||
|
||||
let rootCount = 0;
|
||||
for (const branch of allBranches) {
|
||||
const hidden = getHiddenStems(branch);
|
||||
for (const h of hidden) {
|
||||
const hElem = FIVE_ELEMENTS[h as keyof typeof FIVE_ELEMENTS];
|
||||
if (hElem === dayElement || hElem === producingElement) {
|
||||
rootCount++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rootCount >= 3) {
|
||||
score += 2;
|
||||
reasons.push(`통근 강함: ${rootCount}개 지지에서 일간의 뿌리를 찾음`);
|
||||
} else if (rootCount >= 2) {
|
||||
score += 1;
|
||||
reasons.push(`통근 보통: ${rootCount}개 지지에서 일간의 뿌리를 찾음`);
|
||||
} else {
|
||||
score -= 1;
|
||||
reasons.push(`통근 약함: ${rootCount}개 지지에서만 일간의 뿌리를 찾음`);
|
||||
}
|
||||
|
||||
// 3. 투출 확인
|
||||
const allStems = [saju.year.stem, saju.month.stem];
|
||||
if (saju.hour) allStems.push(saju.hour.stem);
|
||||
|
||||
let helpingStemCount = 0;
|
||||
for (const stem of allStems) {
|
||||
const stemElem = FIVE_ELEMENTS[stem as keyof typeof FIVE_ELEMENTS];
|
||||
if (stemElem === dayElement || stemElem === producingElement) {
|
||||
helpingStemCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (helpingStemCount >= 2) {
|
||||
score += 2;
|
||||
reasons.push(`투출 강함: 천간에 비겁/인성이 ${helpingStemCount}개 있어 일간을 도움`);
|
||||
} else if (helpingStemCount === 1) {
|
||||
score += 1;
|
||||
reasons.push(`투출 보통: 천간에 비겁/인성이 1개 있음`);
|
||||
} else {
|
||||
score -= 1;
|
||||
reasons.push(`투출 없음: 천간에 일간을 돕는 비겁/인성이 없음`);
|
||||
}
|
||||
|
||||
// 4. 오행 비율 기반 조력 분석
|
||||
const balance = calculateDetailedElementBalance(saju);
|
||||
const helpingScore = balance[dayElement as keyof ElementBalance] + balance[producingElement as keyof ElementBalance];
|
||||
const drainingScore = Object.entries(balance)
|
||||
.filter(([k]) => k !== dayElement && k !== producingElement)
|
||||
.reduce((sum, [, v]) => sum + v, 0);
|
||||
|
||||
if (helpingScore > drainingScore * 1.3) {
|
||||
score += 1;
|
||||
reasons.push(`오행 비율: 비겁+인성(${helpingScore.toFixed(1)}) > 식상+재관(${drainingScore.toFixed(1)}) → 일간 세력 우세`);
|
||||
} else if (drainingScore > helpingScore * 1.3) {
|
||||
score -= 1;
|
||||
reasons.push(`오행 비율: 식상+재관(${drainingScore.toFixed(1)}) > 비겁+인성(${helpingScore.toFixed(1)}) → 일간 세력 열세`);
|
||||
}
|
||||
|
||||
let result: '신강' | '신약' | '중화';
|
||||
if (score >= 3) result = '신강';
|
||||
else if (score <= -2) result = '신약';
|
||||
else result = '중화';
|
||||
|
||||
return { result, score, reasons };
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 용신 (用神) 추정
|
||||
// ============================================================
|
||||
|
||||
export interface YongShinResult {
|
||||
yongShin: string;
|
||||
yongShinKr: string;
|
||||
heeShin: string;
|
||||
heeShinKr: string;
|
||||
giShin: string;
|
||||
giShinKr: string;
|
||||
explanation: string;
|
||||
}
|
||||
|
||||
const OVERCOME_MAP: { [key: string]: string } = {
|
||||
'木': '土', '火': '金', '土': '水', '金': '木', '水': '火',
|
||||
};
|
||||
|
||||
function getOvercomingMe(elem: string): string {
|
||||
for (const [k, v] of Object.entries(OVERCOME_MAP)) {
|
||||
if (v === elem) return k;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
export function estimateYongShin(saju: SajuData, strength: DayMasterStrength): YongShinResult {
|
||||
const dayElement = FIVE_ELEMENTS[saju.dayStem as keyof typeof FIVE_ELEMENTS];
|
||||
const balance = calculateDetailedElementBalance(saju);
|
||||
|
||||
const producingMe = getProducingElement(dayElement); // 인성
|
||||
const myProduct = PRODUCE_MAP[dayElement]; // 식상
|
||||
const myOvercome = OVERCOME_MAP[dayElement]; // 재성
|
||||
const overcomeMe = getOvercomingMe(dayElement); // 관살
|
||||
|
||||
const kr = (e: string) => FIVE_ELEMENTS_KR[e as keyof typeof FIVE_ELEMENTS_KR] || e;
|
||||
|
||||
if (strength.result === '신강') {
|
||||
const candidates = [
|
||||
{ elem: myProduct, score: balance[myProduct as keyof ElementBalance], name: '식상' },
|
||||
{ elem: myOvercome, score: balance[myOvercome as keyof ElementBalance], name: '재성' },
|
||||
{ elem: overcomeMe, score: balance[overcomeMe as keyof ElementBalance], name: '관살' },
|
||||
];
|
||||
candidates.sort((a, b) => a.score - b.score);
|
||||
const yong = candidates[0];
|
||||
const hee = candidates[1];
|
||||
|
||||
return {
|
||||
yongShin: yong.elem, yongShinKr: kr(yong.elem),
|
||||
heeShin: hee.elem, heeShinKr: kr(hee.elem),
|
||||
giShin: dayElement, giShinKr: kr(dayElement),
|
||||
explanation: `신강한 사주로 일간의 힘이 넘치므로 ${yong.name}(${kr(yong.elem)}) 기운을 용신으로 삼아 기운을 설기(泄氣)하거나 제어해야 합니다. ${hee.name}(${kr(hee.elem)})이 희신으로 보조합니다.`,
|
||||
};
|
||||
} else if (strength.result === '신약') {
|
||||
const candidates = [
|
||||
{ elem: producingMe, score: balance[producingMe as keyof ElementBalance], name: '인성' },
|
||||
{ elem: dayElement, score: balance[dayElement as keyof ElementBalance], name: '비겁' },
|
||||
];
|
||||
candidates.sort((a, b) => a.score - b.score);
|
||||
const yong = candidates[0];
|
||||
const hee = candidates[1];
|
||||
|
||||
return {
|
||||
yongShin: yong.elem, yongShinKr: kr(yong.elem),
|
||||
heeShin: hee.elem, heeShinKr: kr(hee.elem),
|
||||
giShin: overcomeMe, giShinKr: kr(overcomeMe),
|
||||
explanation: `신약한 사주로 일간의 힘이 부족하므로 ${yong.name}(${kr(yong.elem)}) 기운을 용신으로 삼아 일간을 돕고 힘을 보충해야 합니다. ${hee.name}(${kr(hee.elem)})이 희신으로 보조합니다.`,
|
||||
};
|
||||
} else {
|
||||
const entries = Object.entries(balance) as [string, number][];
|
||||
entries.sort((a, b) => a[1] - b[1]);
|
||||
const yong = entries[0];
|
||||
const hee = entries[1];
|
||||
const gi = entries[entries.length - 1];
|
||||
|
||||
return {
|
||||
yongShin: yong[0], yongShinKr: kr(yong[0]),
|
||||
heeShin: hee[0], heeShinKr: kr(hee[0]),
|
||||
giShin: gi[0], giShinKr: kr(gi[0]),
|
||||
explanation: `중화에 가까운 사주로 오행이 비교적 균형을 이루고 있습니다. 가장 부족한 ${kr(yong[0])}(${yong[0]}) 기운을 보충하면 더욱 좋아집니다.`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 세운 (歲運) 계산
|
||||
// ============================================================
|
||||
|
||||
export interface SeunInfo {
|
||||
stem: string;
|
||||
branch: string;
|
||||
stemKr: string;
|
||||
branchKr: string;
|
||||
element: string;
|
||||
elementKr: string;
|
||||
year: number;
|
||||
interactions: BranchInteraction[];
|
||||
}
|
||||
|
||||
export function calculateSeun(year: number, saju: SajuData): SeunInfo {
|
||||
const ganzi = getYearGanzi(year);
|
||||
const element = FIVE_ELEMENTS[ganzi.stem as keyof typeof FIVE_ELEMENTS];
|
||||
|
||||
const seunBranch = ganzi.branch;
|
||||
const seunBranchKr = ganzi.branchKr;
|
||||
|
||||
const interactions: BranchInteraction[] = [];
|
||||
const pillarBranches = [
|
||||
{ branch: saju.year.branch, branchKr: saju.year.branchKr, pillar: '년주' },
|
||||
{ branch: saju.month.branch, branchKr: saju.month.branchKr, pillar: '월주' },
|
||||
{ branch: saju.day.branch, branchKr: saju.day.branchKr, pillar: '일주' },
|
||||
];
|
||||
if (saju.hour) {
|
||||
pillarBranches.push({ branch: saju.hour.branch, branchKr: saju.hour.branchKr, pillar: '시주' });
|
||||
}
|
||||
|
||||
const CHUNG: [string, string][] = [
|
||||
['子', '午'], ['丑', '未'], ['寅', '申'], ['卯', '酉'], ['辰', '戌'], ['巳', '亥'],
|
||||
];
|
||||
for (const [a, b] of CHUNG) {
|
||||
for (const pb of pillarBranches) {
|
||||
if ((seunBranch === a && pb.branch === b) || (seunBranch === b && pb.branch === a)) {
|
||||
interactions.push({
|
||||
type: '충(沖)', branches: [seunBranch, pb.branch],
|
||||
branchesKr: [seunBranchKr, pb.branchKr],
|
||||
pillars: ['세운', pb.pillar],
|
||||
description: `세운 ${seunBranchKr}와 ${pb.pillar} ${pb.branchKr}가 충 → 해당 영역에 변동과 변화가 예상됨.`,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const YUKAP: [string, string, string][] = [
|
||||
['子', '丑', '土'], ['寅', '亥', '木'], ['卯', '戌', '火'],
|
||||
['辰', '酉', '金'], ['巳', '申', '水'], ['午', '未', '火'],
|
||||
];
|
||||
for (const [a, b, elem] of YUKAP) {
|
||||
for (const pb of pillarBranches) {
|
||||
if ((seunBranch === a && pb.branch === b) || (seunBranch === b && pb.branch === a)) {
|
||||
interactions.push({
|
||||
type: '합(合)', branches: [seunBranch, pb.branch],
|
||||
branchesKr: [seunBranchKr, pb.branchKr],
|
||||
pillars: ['세운', pb.pillar],
|
||||
description: `세운 ${seunBranchKr}와 ${pb.pillar} ${pb.branchKr}가 합 → 해당 영역에 조화와 좋은 인연이 기대됨.`,
|
||||
resultElement: elem,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
stem: ganzi.stem, branch: ganzi.branch,
|
||||
stemKr: ganzi.stemKr, branchKr: ganzi.branchKr,
|
||||
element, elementKr: FIVE_ELEMENTS_KR[element as keyof typeof FIVE_ELEMENTS_KR],
|
||||
year, interactions,
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 종합 분석 데이터 구조체
|
||||
// ============================================================
|
||||
|
||||
export interface SajuAnalysis {
|
||||
elementBalance: ElementBalance;
|
||||
elementScores: { [key: string]: number };
|
||||
dayMasterStrength: DayMasterStrength;
|
||||
yongShin: YongShinResult;
|
||||
branchInteractions: BranchInteraction[];
|
||||
shinsal: Shinsal[];
|
||||
gongmang: { branches: string[]; branchesKr: string[]; description: string };
|
||||
seun: SeunInfo;
|
||||
hiddenStems: { pillar: string; branch: string; branchKr: string; stems: { stem: string; stemKr: string; element: string; role: string }[] }[];
|
||||
}
|
||||
|
||||
export function performFullAnalysis(saju: SajuData, currentYear: number = new Date().getFullYear()): SajuAnalysis {
|
||||
const elementBalance = calculateDetailedElementBalance(saju);
|
||||
const elementScores = calculateElementScore(saju);
|
||||
const dayMasterStrength = analyzeDayMasterStrength(saju);
|
||||
const yongShin = estimateYongShin(saju, dayMasterStrength);
|
||||
const branchInteractions = analyzeBranchInteractions(saju);
|
||||
const shinsal = calculateShinsal(saju);
|
||||
const gongmang = calculateGongmang(saju.dayStem, saju.day.branch);
|
||||
const seun = calculateSeun(currentYear, saju);
|
||||
const hiddenStems = getAllHiddenStems(saju);
|
||||
|
||||
return {
|
||||
elementBalance, elementScores, dayMasterStrength, yongShin,
|
||||
branchInteractions, shinsal, gongmang, seun, hiddenStems,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user