feat(saju-lab): fortune_scores.py — 4 카테고리 점수 + overall (6 tests)

This commit is contained in:
2026-05-26 07:58:02 +09:00
parent 8ef0ba81f2
commit 579e7387be
2 changed files with 170 additions and 0 deletions

View File

@@ -0,0 +1,114 @@
"""4 카테고리 점수 — 재물/연애/인간관계/직장 + 종합점."""
from typing import Dict
from .constants import (
FIVE_ELEMENTS, IS_YANG_STEM, SHENG_CYCLE, KE_CYCLE,
)
def _ten_god_counts(saju: dict) -> Dict[str, int]:
"""4기둥의 ten_god 카운트."""
counts: Dict[str, int] = {}
for p in ("year", "month", "day", "hour"):
pillar = saju.get(p)
if not pillar:
continue
tg = pillar.get("ten_god", "")
counts[tg] = counts.get(tg, 0) + 1
return counts
def _branch_has_chong(saju: dict) -> bool:
"""일지가 다른 기둥과 6충 관계인지."""
LIU_CHONG = {
frozenset(["", ""]), frozenset(["", ""]),
frozenset(["", ""]), frozenset(["", ""]),
frozenset(["", ""]), frozenset(["", ""]),
}
day_branch = saju["day"]["branch"]
for p in ("year", "month", "hour"):
pillar = saju.get(p)
if not pillar:
continue
if frozenset([day_branch, pillar["branch"]]) in LIU_CHONG:
return True
return False
def _branch_has_he(saju: dict) -> bool:
"""일지가 다른 기둥과 6합 관계인지."""
LIU_HE = {
frozenset(["", ""]), frozenset(["", ""]),
frozenset(["", ""]), frozenset(["", ""]),
frozenset(["", ""]), frozenset(["", ""]),
}
day_branch = saju["day"]["branch"]
for p in ("year", "month", "hour"):
pillar = saju.get(p)
if not pillar:
continue
if frozenset([day_branch, pillar["branch"]]) in LIU_HE:
return True
return False
def _clamp(v: int) -> int:
return max(0, min(100, v))
def calculate_fortune_scores(saju: dict, analysis: dict, current_year: int) -> Dict[str, int]:
"""4 카테고리 + overall (가중평균)."""
counts = _ten_god_counts(saju)
has_chong = _branch_has_chong(saju)
has_he = _branch_has_he(saju)
strength = analysis.get("day_master_strength", {}).get("result", "중화")
# 재물운: 정재/편재 강도
wealth = 60
wealth += counts.get("정재", 0) * 8
wealth += counts.get("편재", 0) * 6
wealth += counts.get("식신", 0) * 4
wealth -= counts.get("비견", 0) * 5
wealth -= counts.get("겁재", 0) * 5
wealth = _clamp(wealth)
# 연애운: 일지 합/충 + 정관/정재
romance = 60
if has_he:
romance += 15
if has_chong:
romance -= 15
romance += counts.get("정관", 0) * 5
romance += counts.get("정재", 0) * 5
romance = _clamp(romance)
# 인간관계: 인성 + 비겁 적정 + 식상
social = 60
social += counts.get("정인", 0) * 5
social += counts.get("편인", 0) * 4
social += min(counts.get("비견", 0), 2) * 5
social += counts.get("식신", 0) * 3
social += counts.get("상관", 0) * 3
social = _clamp(social)
# 직장운: 정관 + 편관(제어) + 일간 강도
career = 60
career += counts.get("정관", 0) * 10
career += counts.get("편관", 0) * 5
if strength == "신강":
career += 8
elif strength == "신약":
career -= 5
career = _clamp(career)
overall = round(wealth * 0.3 + career * 0.3 + romance * 0.2 + social * 0.2)
overall = _clamp(overall)
return {
"wealth": wealth,
"romance": romance,
"social": social,
"career": career,
"overall": overall,
}