@gracefullight/saju 0.2.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/README.en.md +250 -32
  2. package/README.md +250 -32
  3. package/dist/__tests__/date-fns-adapter.test.js +1 -1
  4. package/dist/__tests__/four-pillars.test.js +10 -9
  5. package/dist/__tests__/luck.test.d.ts +2 -0
  6. package/dist/__tests__/luck.test.d.ts.map +1 -0
  7. package/dist/__tests__/luck.test.js +33 -0
  8. package/dist/__tests__/lunar.test.js +1 -1
  9. package/dist/__tests__/luxon-adapter.test.js +1 -1
  10. package/dist/__tests__/relations.test.d.ts +2 -0
  11. package/dist/__tests__/relations.test.d.ts.map +1 -0
  12. package/dist/__tests__/relations.test.js +90 -0
  13. package/dist/__tests__/saju.test.d.ts +2 -0
  14. package/dist/__tests__/saju.test.d.ts.map +1 -0
  15. package/dist/__tests__/saju.test.js +133 -0
  16. package/dist/__tests__/sinsals.test.d.ts +2 -0
  17. package/dist/__tests__/sinsals.test.d.ts.map +1 -0
  18. package/dist/__tests__/sinsals.test.js +64 -0
  19. package/dist/__tests__/solar-terms.test.d.ts +2 -0
  20. package/dist/__tests__/solar-terms.test.d.ts.map +1 -0
  21. package/dist/__tests__/solar-terms.test.js +121 -0
  22. package/dist/__tests__/strength.test.d.ts +2 -0
  23. package/dist/__tests__/strength.test.d.ts.map +1 -0
  24. package/dist/__tests__/strength.test.js +44 -0
  25. package/dist/__tests__/ten-gods.test.d.ts +2 -0
  26. package/dist/__tests__/ten-gods.test.d.ts.map +1 -0
  27. package/dist/__tests__/ten-gods.test.js +119 -0
  28. package/dist/__tests__/twelve-stages.test.d.ts +2 -0
  29. package/dist/__tests__/twelve-stages.test.d.ts.map +1 -0
  30. package/dist/__tests__/twelve-stages.test.js +86 -0
  31. package/dist/__tests__/utils.test.d.ts +2 -0
  32. package/dist/__tests__/utils.test.d.ts.map +1 -0
  33. package/dist/__tests__/utils.test.js +130 -0
  34. package/dist/__tests__/yongshen.test.d.ts +2 -0
  35. package/dist/__tests__/yongshen.test.d.ts.map +1 -0
  36. package/dist/__tests__/yongshen.test.js +62 -0
  37. package/dist/adapters/date-fns.d.ts +1 -1
  38. package/dist/adapters/luxon.d.ts +1 -1
  39. package/dist/core/four-pillars.d.ts +6 -6
  40. package/dist/core/four-pillars.d.ts.map +1 -1
  41. package/dist/core/four-pillars.js +10 -26
  42. package/dist/core/luck.d.ts +60 -0
  43. package/dist/core/luck.d.ts.map +1 -0
  44. package/dist/core/luck.js +137 -0
  45. package/dist/core/relations.d.ts +94 -0
  46. package/dist/core/relations.d.ts.map +1 -0
  47. package/dist/core/relations.js +309 -0
  48. package/dist/core/sinsals.d.ts +19 -0
  49. package/dist/core/sinsals.d.ts.map +1 -0
  50. package/dist/core/sinsals.js +339 -0
  51. package/dist/core/solar-terms.d.ts +155 -0
  52. package/dist/core/solar-terms.d.ts.map +1 -0
  53. package/dist/core/solar-terms.js +266 -0
  54. package/dist/core/strength.d.ts +18 -0
  55. package/dist/core/strength.d.ts.map +1 -0
  56. package/dist/core/strength.js +255 -0
  57. package/dist/core/ten-gods.d.ts +127 -0
  58. package/dist/core/ten-gods.d.ts.map +1 -0
  59. package/dist/core/ten-gods.js +331 -0
  60. package/dist/core/twelve-stages.d.ts +17 -0
  61. package/dist/core/twelve-stages.d.ts.map +1 -0
  62. package/dist/core/twelve-stages.js +77 -0
  63. package/dist/core/yongshen.d.ts +20 -0
  64. package/dist/core/yongshen.d.ts.map +1 -0
  65. package/dist/core/yongshen.js +216 -0
  66. package/dist/index.d.ts +63 -3
  67. package/dist/index.d.ts.map +1 -1
  68. package/dist/index.js +57 -2
  69. package/dist/types/common.d.ts +12 -0
  70. package/dist/types/common.d.ts.map +1 -0
  71. package/dist/types/common.js +1 -0
  72. package/dist/types/index.d.ts +2 -0
  73. package/dist/types/index.d.ts.map +1 -0
  74. package/dist/types/index.js +1 -0
  75. package/dist/utils/constants.d.ts +13 -0
  76. package/dist/utils/constants.d.ts.map +1 -0
  77. package/dist/utils/constants.js +59 -0
  78. package/dist/utils/index.d.ts +2 -0
  79. package/dist/utils/index.d.ts.map +1 -0
  80. package/dist/utils/index.js +1 -0
  81. package/package.json +13 -12
@@ -0,0 +1,331 @@
1
+ import { ELEMENTS } from "../utils";
2
+ export { ELEMENTS };
3
+ export const TEN_GODS = [
4
+ "비견",
5
+ "겁재",
6
+ "식신",
7
+ "상관",
8
+ "편재",
9
+ "정재",
10
+ "편관",
11
+ "정관",
12
+ "편인",
13
+ "정인",
14
+ ];
15
+ // 십신 한자 매핑
16
+ export const TEN_GOD_HANJA = {
17
+ 비견: "比肩",
18
+ 겁재: "劫財",
19
+ 식신: "食神",
20
+ 상관: "傷官",
21
+ 편재: "偏財",
22
+ 정재: "正財",
23
+ 편관: "偏官",
24
+ 정관: "正官",
25
+ 편인: "偏印",
26
+ 정인: "正印",
27
+ };
28
+ // 십신 영문 매핑
29
+ export const TEN_GOD_ENGLISH = {
30
+ 비견: "Companion",
31
+ 겁재: "Rob Wealth",
32
+ 식신: "Eating God",
33
+ 상관: "Hurting Officer",
34
+ 편재: "Indirect Wealth",
35
+ 정재: "Direct Wealth",
36
+ 편관: "Seven Killings",
37
+ 정관: "Direct Officer",
38
+ 편인: "Indirect Seal",
39
+ 정인: "Direct Seal",
40
+ };
41
+ // 천간별 오행
42
+ const STEM_ELEMENT = {
43
+ 甲: "wood",
44
+ 乙: "wood",
45
+ 丙: "fire",
46
+ 丁: "fire",
47
+ 戊: "earth",
48
+ 己: "earth",
49
+ 庚: "metal",
50
+ 辛: "metal",
51
+ 壬: "water",
52
+ 癸: "water",
53
+ };
54
+ // 천간별 음양
55
+ const STEM_POLARITY = {
56
+ 甲: "yang",
57
+ 乙: "yin",
58
+ 丙: "yang",
59
+ 丁: "yin",
60
+ 戊: "yang",
61
+ 己: "yin",
62
+ 庚: "yang",
63
+ 辛: "yin",
64
+ 壬: "yang",
65
+ 癸: "yin",
66
+ };
67
+ // 지지별 오행
68
+ const BRANCH_ELEMENT = {
69
+ 子: "water",
70
+ 丑: "earth",
71
+ 寅: "wood",
72
+ 卯: "wood",
73
+ 辰: "earth",
74
+ 巳: "fire",
75
+ 午: "fire",
76
+ 未: "earth",
77
+ 申: "metal",
78
+ 酉: "metal",
79
+ 戌: "earth",
80
+ 亥: "water",
81
+ };
82
+ // 지지별 음양
83
+ const BRANCH_POLARITY = {
84
+ 子: "yang",
85
+ 丑: "yin",
86
+ 寅: "yang",
87
+ 卯: "yin",
88
+ 辰: "yang",
89
+ 巳: "yin",
90
+ 午: "yang",
91
+ 未: "yin",
92
+ 申: "yang",
93
+ 酉: "yin",
94
+ 戌: "yang",
95
+ 亥: "yin",
96
+ };
97
+ // 장간(藏干) - 지지 속에 숨은 천간들
98
+ // 순서: [본기(本氣), 중기(中氣)?, 여기(餘氣)?]
99
+ export const HIDDEN_STEMS = {
100
+ 子: ["癸"],
101
+ 丑: ["己", "癸", "辛"],
102
+ 寅: ["甲", "丙", "戊"],
103
+ 卯: ["乙"],
104
+ 辰: ["戊", "乙", "癸"],
105
+ 巳: ["丙", "庚", "戊"],
106
+ 午: ["丁", "己"],
107
+ 未: ["己", "丁", "乙"],
108
+ 申: ["庚", "壬", "戊"],
109
+ 酉: ["辛"],
110
+ 戌: ["戊", "辛", "丁"],
111
+ 亥: ["壬", "甲"],
112
+ };
113
+ // 오행 상생 관계 (A가 B를 생함)
114
+ // 목생화, 화생토, 토생금, 금생수, 수생목
115
+ const GENERATES = {
116
+ wood: "fire",
117
+ fire: "earth",
118
+ earth: "metal",
119
+ metal: "water",
120
+ water: "wood",
121
+ };
122
+ // 오행 상극 관계 (A가 B를 극함)
123
+ // 목극토, 토극수, 수극화, 화극금, 금극목
124
+ const CONTROLS = {
125
+ wood: "earth",
126
+ earth: "water",
127
+ water: "fire",
128
+ fire: "metal",
129
+ metal: "wood",
130
+ };
131
+ /**
132
+ * 천간의 오행을 반환
133
+ */
134
+ export function getStemElement(stem) {
135
+ const element = STEM_ELEMENT[stem];
136
+ if (!element)
137
+ throw new Error(`Invalid stem: ${stem}`);
138
+ return element;
139
+ }
140
+ /**
141
+ * 천간의 음양을 반환
142
+ */
143
+ export function getStemPolarity(stem) {
144
+ const polarity = STEM_POLARITY[stem];
145
+ if (!polarity)
146
+ throw new Error(`Invalid stem: ${stem}`);
147
+ return polarity;
148
+ }
149
+ /**
150
+ * 지지의 오행을 반환
151
+ */
152
+ export function getBranchElement(branch) {
153
+ const element = BRANCH_ELEMENT[branch];
154
+ if (!element)
155
+ throw new Error(`Invalid branch: ${branch}`);
156
+ return element;
157
+ }
158
+ /**
159
+ * 지지의 음양을 반환
160
+ */
161
+ export function getBranchPolarity(branch) {
162
+ const polarity = BRANCH_POLARITY[branch];
163
+ if (!polarity)
164
+ throw new Error(`Invalid branch: ${branch}`);
165
+ return polarity;
166
+ }
167
+ /**
168
+ * 지지의 장간(숨은 천간들)을 반환
169
+ */
170
+ export function getHiddenStems(branch) {
171
+ const hidden = HIDDEN_STEMS[branch];
172
+ if (!hidden)
173
+ throw new Error(`Invalid branch: ${branch}`);
174
+ return [...hidden];
175
+ }
176
+ /**
177
+ * 일간(Day Master)과 다른 천간의 관계에서 십신을 판정
178
+ * @param dayMaster 일간 (예: "甲")
179
+ * @param targetStem 비교 대상 천간
180
+ * @returns 십신
181
+ */
182
+ export function getTenGod(dayMaster, targetStem) {
183
+ const dmElement = getStemElement(dayMaster);
184
+ const dmPolarity = getStemPolarity(dayMaster);
185
+ const targetElement = getStemElement(targetStem);
186
+ const targetPolarity = getStemPolarity(targetStem);
187
+ const samePolarity = dmPolarity === targetPolarity;
188
+ // 같은 오행
189
+ if (dmElement === targetElement) {
190
+ return samePolarity ? "비견" : "겁재";
191
+ }
192
+ // 내가 생하는 오행 (식상)
193
+ if (GENERATES[dmElement] === targetElement) {
194
+ return samePolarity ? "식신" : "상관";
195
+ }
196
+ // 내가 극하는 오행 (재성)
197
+ if (CONTROLS[dmElement] === targetElement) {
198
+ return samePolarity ? "편재" : "정재";
199
+ }
200
+ // 나를 극하는 오행 (관성)
201
+ if (CONTROLS[targetElement] === dmElement) {
202
+ return samePolarity ? "편관" : "정관";
203
+ }
204
+ // 나를 생하는 오행 (인성)
205
+ if (GENERATES[targetElement] === dmElement) {
206
+ return samePolarity ? "편인" : "정인";
207
+ }
208
+ throw new Error(`Unable to determine ten god relationship: ${dayMaster} -> ${targetStem}`);
209
+ }
210
+ /**
211
+ * 지지에 대한 십신 판정 (본기 기준)
212
+ */
213
+ export function getTenGodForBranch(dayMaster, branch) {
214
+ const hiddenStems = getHiddenStems(branch);
215
+ // 본기(첫 번째 장간)를 기준으로 판정
216
+ return getTenGod(dayMaster, hiddenStems[0]);
217
+ }
218
+ /**
219
+ * 지지의 모든 장간에 대한 십신 분석
220
+ */
221
+ export function getTenGodsForBranch(dayMaster, branch) {
222
+ const hiddenStems = getHiddenStems(branch);
223
+ const types = ["본기", "중기", "여기"];
224
+ return hiddenStems.map((stem, i) => ({
225
+ stem,
226
+ tenGod: getTenGod(dayMaster, stem),
227
+ type: types[i],
228
+ }));
229
+ }
230
+ /**
231
+ * 사주 팔자에서 십신 분석
232
+ * @param yearPillar 연주 (예: "甲子")
233
+ * @param monthPillar 월주
234
+ * @param dayPillar 일주
235
+ * @param hourPillar 시주
236
+ */
237
+ export function analyzeTenGods(yearPillar, monthPillar, dayPillar, hourPillar) {
238
+ const dayMaster = dayPillar[0];
239
+ const analyzePillar = (pillar, isDayPillar = false) => {
240
+ const stem = pillar[0];
241
+ const branch = pillar[1];
242
+ const hiddenStems = getHiddenStems(branch).map((hs) => ({
243
+ stem: hs,
244
+ tenGod: getTenGod(dayMaster, hs),
245
+ }));
246
+ return {
247
+ stem: {
248
+ char: stem,
249
+ tenGod: isDayPillar ? "일간" : getTenGod(dayMaster, stem),
250
+ },
251
+ branch: {
252
+ char: branch,
253
+ tenGod: getTenGod(dayMaster, hiddenStems[0].stem),
254
+ hiddenStems,
255
+ },
256
+ };
257
+ };
258
+ return {
259
+ year: analyzePillar(yearPillar),
260
+ month: analyzePillar(monthPillar),
261
+ day: analyzePillar(dayPillar, true),
262
+ hour: analyzePillar(hourPillar),
263
+ dayMaster,
264
+ };
265
+ }
266
+ /**
267
+ * 십신 통계 (각 십신이 몇 개 있는지)
268
+ */
269
+ export function countTenGods(analysis) {
270
+ const counts = {
271
+ 비견: 0,
272
+ 겁재: 0,
273
+ 식신: 0,
274
+ 상관: 0,
275
+ 편재: 0,
276
+ 정재: 0,
277
+ 편관: 0,
278
+ 정관: 0,
279
+ 편인: 0,
280
+ 정인: 0,
281
+ };
282
+ // 천간 십신 카운트 (일간 제외)
283
+ const stems = [analysis.year.stem, analysis.month.stem, analysis.hour.stem];
284
+ for (const s of stems) {
285
+ counts[s.tenGod]++;
286
+ }
287
+ // 지지 본기 십신 카운트
288
+ const branches = [
289
+ analysis.year.branch,
290
+ analysis.month.branch,
291
+ analysis.day.branch,
292
+ analysis.hour.branch,
293
+ ];
294
+ for (const b of branches) {
295
+ counts[b.tenGod]++;
296
+ }
297
+ return counts;
298
+ }
299
+ /**
300
+ * 오행별 개수 (천간 + 지지 본기)
301
+ */
302
+ export function countElements(analysis) {
303
+ const counts = {
304
+ wood: 0,
305
+ fire: 0,
306
+ earth: 0,
307
+ metal: 0,
308
+ water: 0,
309
+ };
310
+ // 모든 천간의 오행
311
+ const stems = [
312
+ analysis.year.stem.char,
313
+ analysis.month.stem.char,
314
+ analysis.day.stem.char,
315
+ analysis.hour.stem.char,
316
+ ];
317
+ for (const s of stems) {
318
+ counts[getStemElement(s)]++;
319
+ }
320
+ // 모든 지지의 오행
321
+ const branches = [
322
+ analysis.year.branch.char,
323
+ analysis.month.branch.char,
324
+ analysis.day.branch.char,
325
+ analysis.hour.branch.char,
326
+ ];
327
+ for (const b of branches) {
328
+ counts[getBranchElement(b)]++;
329
+ }
330
+ return counts;
331
+ }
@@ -0,0 +1,17 @@
1
+ export declare const TWELVE_STAGES: readonly ["longLife", "bathing", "crownBelt", "establishment", "imperial", "decline", "illness", "death", "tomb", "extinction", "conception", "nurturing"];
2
+ export type TwelveStage = (typeof TWELVE_STAGES)[number];
3
+ export declare function getTwelveStage(stem: string, branch: string): TwelveStage;
4
+ export interface TwelveStagesResult {
5
+ year: TwelveStage;
6
+ month: TwelveStage;
7
+ day: TwelveStage;
8
+ hour: TwelveStage;
9
+ }
10
+ export declare function analyzeTwelveStages(yearPillar: string, monthPillar: string, dayPillar: string, hourPillar: string): TwelveStagesResult;
11
+ export declare const STAGE_INFO: Record<TwelveStage, {
12
+ korean: string;
13
+ hanja: string;
14
+ meaning: string;
15
+ strength: "strong" | "neutral" | "weak";
16
+ }>;
17
+ //# sourceMappingURL=twelve-stages.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"twelve-stages.d.ts","sourceRoot":"","sources":["../../src/core/twelve-stages.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,aAAa,4JAahB,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAkBzD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,CAwBxE;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,EAAE,WAAW,CAAC;IACnB,GAAG,EAAE,WAAW,CAAC;IACjB,IAAI,EAAE,WAAW,CAAC;CACnB;AAED,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,kBAAkB,CASpB;AAED,eAAO,MAAM,UAAU,EAAE,MAAM,CAC7B,WAAW,EACX;IACE,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC;CACzC,CAmBF,CAAC"}
@@ -0,0 +1,77 @@
1
+ import { getBranchIndex, isYangStem } from "../utils";
2
+ export const TWELVE_STAGES = [
3
+ "longLife",
4
+ "bathing",
5
+ "crownBelt",
6
+ "establishment",
7
+ "imperial",
8
+ "decline",
9
+ "illness",
10
+ "death",
11
+ "tomb",
12
+ "extinction",
13
+ "conception",
14
+ "nurturing",
15
+ ];
16
+ const YANG_STEM_BIRTH_BRANCH = {
17
+ 甲: "亥",
18
+ 丙: "寅",
19
+ 戊: "寅",
20
+ 庚: "巳",
21
+ 壬: "申",
22
+ };
23
+ const YIN_STEM_BIRTH_BRANCH = {
24
+ 乙: "午",
25
+ 丁: "酉",
26
+ 己: "酉",
27
+ 辛: "子",
28
+ 癸: "卯",
29
+ };
30
+ export function getTwelveStage(stem, branch) {
31
+ const isYang = isYangStem(stem);
32
+ const birthBranch = isYang ? YANG_STEM_BIRTH_BRANCH[stem] : YIN_STEM_BIRTH_BRANCH[stem];
33
+ if (!birthBranch) {
34
+ throw new Error(`Invalid stem: ${stem}`);
35
+ }
36
+ const birthIndex = getBranchIndex(birthBranch);
37
+ const targetIndex = getBranchIndex(branch);
38
+ if (birthIndex === -1 || targetIndex === -1) {
39
+ throw new Error(`Invalid branch: ${branch}`);
40
+ }
41
+ let stageIndex;
42
+ if (isYang) {
43
+ stageIndex = (targetIndex - birthIndex + 12) % 12;
44
+ }
45
+ else {
46
+ stageIndex = (birthIndex - targetIndex + 12) % 12;
47
+ }
48
+ return TWELVE_STAGES[stageIndex];
49
+ }
50
+ export function analyzeTwelveStages(yearPillar, monthPillar, dayPillar, hourPillar) {
51
+ const dayMaster = dayPillar[0];
52
+ return {
53
+ year: getTwelveStage(dayMaster, yearPillar[1]),
54
+ month: getTwelveStage(dayMaster, monthPillar[1]),
55
+ day: getTwelveStage(dayMaster, dayPillar[1]),
56
+ hour: getTwelveStage(dayMaster, hourPillar[1]),
57
+ };
58
+ }
59
+ export const STAGE_INFO = {
60
+ longLife: {
61
+ korean: "장생",
62
+ hanja: "長生",
63
+ meaning: "새로운 시작, 성장의 기운",
64
+ strength: "strong",
65
+ },
66
+ bathing: { korean: "목욕", hanja: "沐浴", meaning: "불안정, 변화, 도화", strength: "neutral" },
67
+ crownBelt: { korean: "관대", hanja: "冠帶", meaning: "성장, 준비, 학업", strength: "strong" },
68
+ establishment: { korean: "건록", hanja: "建祿", meaning: "안정, 직업, 녹봉", strength: "strong" },
69
+ imperial: { korean: "제왕", hanja: "帝旺", meaning: "최고 전성기, 권력", strength: "strong" },
70
+ decline: { korean: "쇠", hanja: "衰", meaning: "기운 약화, 후퇴", strength: "weak" },
71
+ illness: { korean: "병", hanja: "病", meaning: "질병, 곤란", strength: "weak" },
72
+ death: { korean: "사", hanja: "死", meaning: "끝, 전환점", strength: "weak" },
73
+ tomb: { korean: "묘", hanja: "墓", meaning: "저장, 숨김, 보관", strength: "neutral" },
74
+ extinction: { korean: "절", hanja: "絶", meaning: "단절, 새로운 국면", strength: "weak" },
75
+ conception: { korean: "태", hanja: "胎", meaning: "잉태, 계획, 구상", strength: "neutral" },
76
+ nurturing: { korean: "양", hanja: "養", meaning: "양육, 준비, 축적", strength: "neutral" },
77
+ };
@@ -0,0 +1,20 @@
1
+ import { type Element } from "./ten-gods";
2
+ export type YongShenMethod = "격국" | "억부" | "조후" | "통관" | "병약";
3
+ export interface YongShenResult {
4
+ primary: Element;
5
+ secondary: Element | null;
6
+ method: YongShenMethod;
7
+ reasoning: string;
8
+ allElements: Record<Element, {
9
+ isYongShen: boolean;
10
+ isKiShen: boolean;
11
+ }>;
12
+ johuAdjustment: Element | null;
13
+ }
14
+ export declare function analyzeYongShen(yearPillar: string, monthPillar: string, dayPillar: string, hourPillar: string): YongShenResult;
15
+ export declare function getElementRecommendations(yongShen: YongShenResult): {
16
+ colors: string[];
17
+ directions: string[];
18
+ numbers: number[];
19
+ };
20
+ //# sourceMappingURL=yongshen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yongshen.d.ts","sourceRoot":"","sources":["../../src/core/yongshen.ts"],"names":[],"mappings":"AACA,OAAO,EAAY,KAAK,OAAO,EAAoC,MAAM,YAAY,CAAC;AAEtF,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE9D,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,OAAO,GAAG,IAAI,CAAC;IAC1B,MAAM,EAAE,cAAc,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC,OAAO,EAAE;QAAE,UAAU,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACzE,cAAc,EAAE,OAAO,GAAG,IAAI,CAAC;CAChC;AAmJD,wBAAgB,eAAe,CAC7B,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,cAAc,CA+EhB;AAED,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,cAAc,GAAG;IACnE,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,CA0BA"}
@@ -0,0 +1,216 @@
1
+ import { analyzeStrength } from "./strength";
2
+ import { ELEMENTS, getBranchElement, getStemElement } from "./ten-gods";
3
+ const SEASON_MONTH_BRANCHES = {
4
+ spring: ["寅", "卯", "辰"],
5
+ summer: ["巳", "午", "未"],
6
+ autumn: ["申", "酉", "戌"],
7
+ winter: ["亥", "子", "丑"],
8
+ };
9
+ function getSeason(monthBranch) {
10
+ for (const [season, branches] of Object.entries(SEASON_MONTH_BRANCHES)) {
11
+ if (branches.includes(monthBranch)) {
12
+ return season;
13
+ }
14
+ }
15
+ return "spring";
16
+ }
17
+ const JOHU_YONGSHEN = {
18
+ spring: {
19
+ wood: ["fire", "water"],
20
+ fire: ["water", "wood"],
21
+ earth: ["fire", "wood"],
22
+ metal: ["fire", "earth"],
23
+ water: ["fire", "metal"],
24
+ },
25
+ summer: {
26
+ wood: ["water", "metal"],
27
+ fire: ["water", "metal"],
28
+ earth: ["water", "metal"],
29
+ metal: ["water", "earth"],
30
+ water: ["metal", "water"],
31
+ },
32
+ autumn: {
33
+ wood: ["water", "fire"],
34
+ fire: ["wood", "earth"],
35
+ earth: ["fire", "water"],
36
+ metal: ["fire", "water"],
37
+ water: ["fire", "metal"],
38
+ },
39
+ winter: {
40
+ wood: ["fire", "earth"],
41
+ fire: ["wood", "earth"],
42
+ earth: ["fire", "wood"],
43
+ metal: ["fire", "earth"],
44
+ water: ["fire", "earth"],
45
+ },
46
+ };
47
+ const GENERATES = {
48
+ wood: "fire",
49
+ fire: "earth",
50
+ earth: "metal",
51
+ metal: "water",
52
+ water: "wood",
53
+ };
54
+ const GENERATED_BY = {
55
+ fire: "wood",
56
+ earth: "fire",
57
+ metal: "earth",
58
+ water: "metal",
59
+ wood: "water",
60
+ };
61
+ const CONTROLS = {
62
+ wood: "earth",
63
+ earth: "water",
64
+ water: "fire",
65
+ fire: "metal",
66
+ metal: "wood",
67
+ };
68
+ const CONTROLLED_BY = {
69
+ earth: "wood",
70
+ water: "earth",
71
+ fire: "water",
72
+ metal: "fire",
73
+ wood: "metal",
74
+ };
75
+ function getYokbuYongShen(dayMasterElement, strengthLevel) {
76
+ const isStrong = ["신강", "태강", "극왕", "중화신강"].includes(strengthLevel);
77
+ if (isStrong) {
78
+ const primary = CONTROLS[dayMasterElement];
79
+ const secondary = GENERATES[dayMasterElement];
80
+ return { primary, secondary };
81
+ }
82
+ const primary = GENERATED_BY[dayMasterElement];
83
+ const secondary = dayMasterElement;
84
+ return { primary, secondary };
85
+ }
86
+ function hasSpecialFormation(dayMasterElement, strengthLevel, allElements) {
87
+ const _isExtreme = ["극약", "극왕"].includes(strengthLevel);
88
+ if (strengthLevel === "극약") {
89
+ const elementCounts = {
90
+ wood: 0,
91
+ fire: 0,
92
+ earth: 0,
93
+ metal: 0,
94
+ water: 0,
95
+ };
96
+ for (const elem of allElements) {
97
+ elementCounts[elem]++;
98
+ }
99
+ let dominantElement = null;
100
+ let maxCount = 0;
101
+ for (const [elem, count] of Object.entries(elementCounts)) {
102
+ if (count > maxCount && elem !== dayMasterElement) {
103
+ maxCount = count;
104
+ dominantElement = elem;
105
+ }
106
+ }
107
+ if (dominantElement && maxCount >= 3) {
108
+ return { isSpecial: true, type: "종격", followElement: dominantElement };
109
+ }
110
+ }
111
+ return { isSpecial: false, type: null, followElement: null };
112
+ }
113
+ function getJohuAdjustment(dayMasterElement, season, yokbuPrimary) {
114
+ const johuElements = JOHU_YONGSHEN[season][dayMasterElement];
115
+ const johuPrimary = johuElements[0];
116
+ if (johuPrimary !== yokbuPrimary) {
117
+ return johuPrimary;
118
+ }
119
+ return null;
120
+ }
121
+ export function analyzeYongShen(yearPillar, monthPillar, dayPillar, hourPillar) {
122
+ const dayMaster = dayPillar[0];
123
+ const dayMasterElement = getStemElement(dayMaster);
124
+ const monthBranch = monthPillar[1];
125
+ const season = getSeason(monthBranch);
126
+ const strength = analyzeStrength(yearPillar, monthPillar, dayPillar, hourPillar);
127
+ const allBranches = [yearPillar[1], monthPillar[1], dayPillar[1], hourPillar[1]];
128
+ const allBranchElements = allBranches.map((b) => getBranchElement(b));
129
+ const specialFormation = hasSpecialFormation(dayMasterElement, strength.level, allBranchElements);
130
+ let primary;
131
+ let secondary = null;
132
+ let method;
133
+ let reasoning;
134
+ let johuAdjustment = null;
135
+ if (specialFormation.isSpecial && specialFormation.followElement) {
136
+ primary = specialFormation.followElement;
137
+ secondary = GENERATES[specialFormation.followElement];
138
+ method = "격국";
139
+ reasoning = `${specialFormation.type} 성립. ${specialFormation.followElement} 세력을 따름`;
140
+ }
141
+ else {
142
+ const yokbu = getYokbuYongShen(dayMasterElement, strength.level);
143
+ primary = yokbu.primary;
144
+ secondary = yokbu.secondary;
145
+ method = "억부";
146
+ const isStrong = ["신강", "태강", "극왕", "중화신강"].includes(strength.level);
147
+ if (isStrong) {
148
+ reasoning = `${strength.level} 상태로 설기(洩氣) 필요. ${primary}로 기운을 발산`;
149
+ }
150
+ else {
151
+ reasoning = `${strength.level} 상태로 부조(扶助) 필요. ${primary}로 일간을 생조`;
152
+ }
153
+ johuAdjustment = getJohuAdjustment(dayMasterElement, season, primary);
154
+ if (johuAdjustment) {
155
+ reasoning += `. 조후 보정: ${season} 계절에 ${johuAdjustment} 참고`;
156
+ }
157
+ }
158
+ const allElements = {
159
+ wood: { isYongShen: false, isKiShen: false },
160
+ fire: { isYongShen: false, isKiShen: false },
161
+ earth: { isYongShen: false, isKiShen: false },
162
+ metal: { isYongShen: false, isKiShen: false },
163
+ water: { isYongShen: false, isKiShen: false },
164
+ };
165
+ allElements[primary].isYongShen = true;
166
+ if (secondary)
167
+ allElements[secondary].isYongShen = true;
168
+ const isStrong = ["신강", "태강", "극왕", "중화신강"].includes(strength.level);
169
+ if (method !== "격국") {
170
+ if (isStrong) {
171
+ allElements[dayMasterElement].isKiShen = true;
172
+ allElements[GENERATED_BY[dayMasterElement]].isKiShen = true;
173
+ }
174
+ else {
175
+ allElements[CONTROLS[dayMasterElement]].isKiShen = true;
176
+ allElements[CONTROLLED_BY[dayMasterElement]].isKiShen = true;
177
+ }
178
+ }
179
+ for (const elem of ELEMENTS) {
180
+ if (allElements[elem].isYongShen) {
181
+ allElements[elem].isKiShen = false;
182
+ }
183
+ }
184
+ return {
185
+ primary,
186
+ secondary,
187
+ method,
188
+ reasoning,
189
+ allElements,
190
+ johuAdjustment,
191
+ };
192
+ }
193
+ export function getElementRecommendations(yongShen) {
194
+ const elementData = {
195
+ wood: { colors: ["청색", "녹색"], direction: "동", numbers: [3, 8] },
196
+ fire: { colors: ["적색", "자주색"], direction: "남", numbers: [2, 7] },
197
+ earth: { colors: ["황색", "갈색"], direction: "중앙", numbers: [5, 10] },
198
+ metal: { colors: ["백색", "금색"], direction: "서", numbers: [4, 9] },
199
+ water: { colors: ["흑색", "남색"], direction: "북", numbers: [1, 6] },
200
+ };
201
+ const primary = elementData[yongShen.primary];
202
+ const colors = [...primary.colors];
203
+ const directions = [primary.direction];
204
+ const numbers = [...primary.numbers];
205
+ if (yongShen.secondary) {
206
+ const secondary = elementData[yongShen.secondary];
207
+ colors.push(...secondary.colors);
208
+ directions.push(secondary.direction);
209
+ numbers.push(...secondary.numbers);
210
+ }
211
+ return {
212
+ colors: [...new Set(colors)],
213
+ directions: [...new Set(directions)],
214
+ numbers: [...new Set(numbers)].sort((a, b) => a - b),
215
+ };
216
+ }