import gradio as gr import datetime import re from calendar import monthrange class SajuCalculator: def __init__(self): # 천간 (하늘줄기) self.heavenly_stems = ['갑', '을', '병', '정', '무', '기', '경', '신', '임', '계'] # 지지 (땅가지) self.earthly_branches = ['자', '축', '인', '묘', '진', '사', '오', '미', '신', '유', '술', '해'] # 오행 self.five_elements = { '갑': '목', '을': '목', '병': '화', '정': '화', '무': '토', '기': '토', '경': '금', '신': '금', '임': '수', '계': '수', '자': '수', '축': '토', '인': '목', '묘': '목', '진': '토', '사': '화', '오': '화', '미': '토', '신': '금', '유': '금', '술': '토', '해': '수' } # 십신 self.ten_gods = { '목': {'목': '비견', '화': '식신', '토': '편재', '금': '편관', '수': '편인'}, '화': {'목': '편인', '화': '비견', '토': '식신', '금': '편재', '수': '편관'}, '토': {'목': '편관', '화': '편인', '토': '비견', '금': '식신', '수': '편재'}, '금': {'목': '편재', '화': '편관', '토': '편인', '금': '비견', '수': '식신'}, '수': {'목': '식신', '화': '편재', '토': '편관', '금': '편인', '수': '비견'} } # 지장간 (지지 안에 숨어있는 천간들) self.hidden_stems = { '자': ['계'], '축': ['기', '계', '신'], '인': ['갑', '병', '무'], '묘': ['을'], '진': ['무', '을', '계'], '사': ['병', '무', '경'], '오': ['정', '기'], '미': ['기', '정', '을'], '신': ['경', '임', '무'], '유': ['신'], '술': ['무', '신', '정'], '해': ['임', '갑'] } # 12운성 self.twelve_phases = ['장생', '목욕', '관대', '건록', '제왕', '쇠', '병', '사', '묘', '절', '태', '양'] # 절기 정보 (월별 대략적인 날짜 - 실제로는 년도별로 다름) self.seasonal_divisions = { 1: {'소한': 6, '대한': 21}, 2: {'입춘': 4, '우수': 19}, 3: {'경칩': 6, '춘분': 21}, 4: {'청명': 5, '곡우': 20}, 5: {'입하': 6, '소만': 21}, 6: {'망종': 6, '하지': 21}, 7: {'소서': 7, '대서': 23}, 8: {'입추': 8, '처서': 23}, 9: {'백로': 8, '추분': 23}, 10: {'한로': 8, '상강': 24}, 11: {'입동': 7, '소설': 22}, 12: {'대설': 7, '동지': 22} } # 지역별 시간 보정 (서울 기준 분 단위) self.location_offsets = { '서울': 0, '서울특별시': 0, '경기': 0, '경기도': 0, '인천': 0, '인천광역시': 0, '강원': +12, '강원도': +12, '춘천': +12, '원주': +8, '강릉': +20, '충북': -8, '충청북도': -8, '청주': -8, '충주': 0, '충남': -16, '충청남도': -16, '대전': -12, '대전광역시': -12, '천안': -12, '전북': -20, '전라북도': -20, '전주': -20, '군산': -24, '전남': -24, '전라남도': -24, '광주': -20, '광주광역시': -20, '목포': -32, '여수': -16, '경북': +8, '경상북도': +8, '대구': +4, '대구광역시': +4, '포항': +20, '경주': +16, '경남': -4, '경상남도': -4, '부산': +12, '부산광역시': +12, '울산': +16, '울산광역시': +16, '창원': +4, '마산': +4, '진주': -8, '제주': -20, '제주도': -20, '제주시': -20, '서귀포': -20 } def get_location_offset(self, location): """출생지에 따른 시간 보정값 반환 (분 단위)""" if not location: return 0 location = location.strip() # 정확한 매칭 시도 if location in self.location_offsets: return self.location_offsets[location] # 부분 매칭 시도 for key, offset in self.location_offsets.items(): if key in location or location in key: return offset return 0 # 기본값 (서울 기준) def parse_date(self, date_str): """날짜 파싱""" numbers = re.findall(r'\d+', str(date_str)) if not numbers: raise ValueError("날짜에서 숫자를 찾을 수 없습니다") if len(numbers) == 1: num_str = numbers[0] if len(num_str) == 8: # YYYYMMDD year = int(num_str[:4]) month = int(num_str[4:6]) day = int(num_str[6:8]) elif len(num_str) == 6: # YYMMDD year_part = int(num_str[:2]) year = 1900 + year_part if year_part > 30 else 2000 + year_part month = int(num_str[2:4]) day = int(num_str[4:6]) else: raise ValueError(f"날짜 형식을 인식할 수 없습니다: {num_str}") elif len(numbers) >= 3: year = int(numbers[0]) month = int(numbers[1]) day = int(numbers[2]) if year < 100: year = 1900 + year if year > 30 else 2000 + year else: raise ValueError("날짜 정보가 부족합니다") return year, month, day def parse_time(self, time_str): """시간 파싱""" if not time_str: return 12, 0 numbers = re.findall(r'\d+', str(time_str)) if not numbers: return 12, 0 if len(numbers) == 1: time_num = numbers[0] if len(time_num) == 4: # HHMM hour = int(time_num[:2]) minute = int(time_num[2:4]) elif len(time_num) == 3: # HMM hour = int(time_num[0]) minute = int(time_num[1:3]) else: # H or HH hour = int(time_num) minute = 0 else: hour = int(numbers[0]) minute = int(numbers[1]) if len(numbers) > 1 else 0 hour = hour % 24 return hour, minute def get_seasonal_month(self, year, month, day): """절기를 고려한 월주 계산""" # 간단한 절기 계산 (정확하지 않지만 대략적) seasonal_month = month # 입춘(2월 4일경) 기준으로 월 조정 if month == 1: seasonal_month = 12 # 12월 (축월) elif month == 2: if day < 4: # 입춘 전 seasonal_month = 12 else: # 입춘 후 seasonal_month = 1 # 1월 (인월) else: seasonal_month = month - 1 return seasonal_month def get_ganzhi(self, year, month, day, hour): """간지 계산 (절기 고려)""" base_date = datetime.datetime(1900, 1, 1) target_date = datetime.datetime(year, month, day) days_diff = (target_date - base_date).days # 연주 계산 year_stem_index = (year - 1900 + 6) % 10 year_branch_index = (year - 1900 + 0) % 12 # 월주 계산 (절기 고려) seasonal_month = self.get_seasonal_month(year, month, day) month_branch_index = (seasonal_month + 1) % 12 # 인월부터 시작 month_stem_index = (year_stem_index * 2 + seasonal_month) % 10 # 일주 계산 day_stem_index = (days_diff + 10) % 10 day_branch_index = (days_diff + 2) % 12 # 시주 계산 hour_branch_index = ((hour + 1) // 2) % 12 hour_stem_index = (day_stem_index * 2 + hour_branch_index) % 10 return { 'year': (self.heavenly_stems[year_stem_index], self.earthly_branches[year_branch_index]), 'month': (self.heavenly_stems[month_stem_index], self.earthly_branches[month_branch_index]), 'day': (self.heavenly_stems[day_stem_index], self.earthly_branches[day_branch_index]), 'hour': (self.heavenly_stems[hour_stem_index], self.earthly_branches[hour_branch_index]) } def analyze_elements(self, ganzhi): """오행 분석 (지장간 포함)""" element_count = {'목': 0, '화': 0, '토': 0, '금': 0, '수': 0} # 천간 오행 (가중치 1.0) for pillar in ganzhi.values(): stem_element = self.five_elements[pillar[0]] element_count[stem_element] += 1.0 # 지지 오행 (가중치 0.8) for pillar in ganzhi.values(): branch_element = self.five_elements[pillar[1]] element_count[branch_element] += 0.8 # 지장간 오행 (가중치 0.3) for pillar in ganzhi.values(): hidden = self.hidden_stems[pillar[1]] for stem in hidden: stem_element = self.five_elements[stem] element_count[stem_element] += 0.3 / len(hidden) return element_count def get_ten_gods_analysis(self, ganzhi): """십신 분석 (지장간 포함)""" day_stem = ganzhi['day'][0] day_element = self.five_elements[day_stem] analysis = {} for pillar_name, pillar in ganzhi.items(): stem_element = self.five_elements[pillar[0]] branch_element = self.five_elements[pillar[1]] stem_relation = self.ten_gods[day_element][stem_element] branch_relation = self.ten_gods[day_element][branch_element] # 지장간 십신 hidden_relations = [] for hidden_stem in self.hidden_stems[pillar[1]]: hidden_element = self.five_elements[hidden_stem] hidden_relation = self.ten_gods[day_element][hidden_element] hidden_relations.append(f"{hidden_stem}({hidden_relation})") analysis[pillar_name] = { 'stem_relation': stem_relation, 'branch_relation': branch_relation, 'hidden_relations': hidden_relations } return analysis def get_element_personality(element): """오행별 성격 특성""" personalities = { '목': "창의적이고 성장 지향적이며, 유연하고 협력적인 성격을 가지고 있습니다.", '화': "열정적이고 활동적이며, 밝고 사교적인 성격을 가지고 있습니다.", '토': "안정적이고 신중하며, 포용력이 있고 책임감이 강한 성격을 가지고 있습니다.", '금': "원칙적이고 정의로우며, 결단력이 있고 리더십이 강한 성격을 가지고 있습니다.", '수': "지혜롭고 적응력이 있으며, 깊이 있고 신중한 성격을 가지고 있습니다." } return personalities.get(element, "균형 잡힌 성격을 가지고 있습니다.") def get_element_balance_advice(elements): """오행 균형에 따른 조언""" max_element = max(elements, key=elements.get) min_element = min(elements, key=elements.get) advice = f"현재 {max_element}가 가장 강하고 {min_element}가 가장 약합니다. " if elements[max_element] - elements[min_element] > 2: advice += f"{min_element}를 보강하고 {max_element}의 기운을 조절하는 것이 좋겠습니다." else: advice += "전체적으로 균형이 잘 잡혀 있는 편입니다." return advice def format_saju_result(calculator, ganzhi, elements, ten_gods, birth_info): """사주 결과 포맷팅 - 전문적인 표 형태로 출력""" # 오행별 색상 설정 element_colors = { '목': '#28a745', # 녹색 '화': '#dc3545', # 빨강 '토': '#ffc107', # 노랑 '금': '#6c757d', # 회색 '수': '#007bff' # 파랑 } def get_colored_element(char, element): color = element_colors.get(element, '#000000') return f'{char}' def get_colored_ganzhi(stem, branch): stem_element = calculator.five_elements[stem] branch_element = calculator.five_elements[branch] colored_stem = get_colored_element(stem, stem_element) colored_branch = get_colored_element(branch, branch_element) return f'{colored_stem}{colored_branch}' # 시간 보정 정보 time_correction_info = "" if birth_info['location_offset'] != 0: sign = "+" if birth_info['location_offset'] > 0 else "" time_correction_info = f""" ### ⏰ 출생지 시간 보정 - **입력 시간**: {birth_info['original_time']} - **보정 시간**: {birth_info['corrected_time']} ({birth_info['birth_place']} 기준 {sign}{birth_info['location_offset']}분 보정) """ birth_datetime = birth_info['birth_datetime'] gender = birth_info['gender'] birth_place = birth_info['birth_place'] corrected_time = birth_info['corrected_time'] result = f""" # 🔮 사주명리 만세력 분석결과 ## 📋 기본정보 - **생년월일**: {birth_datetime.strftime('%Y년 %m월 %d일')} - **출생시간**: {corrected_time} ({birth_place}) - **성별**: {gender} {time_correction_info} ## 🏛️ 사주(四柱) 만세력표
절기 고려 · 지장간 분석 · 출생지 시간 보정
생년월일시와 출생지 정보를 입력하시면 전문적인 만세력을 분석해드립니다.
※ 본 시스템은 전통 사주명리학을 기반으로 하며, 참고용으로만 활용해주시기 바랍니다.