from modules.qimen_interpreter import QiMenInterpreter class QiMen: """ 奇门遁甲核心算法类 实现定局、排盘(九星、八门、八神、天干)并集成占断解释 """ TERM_JU = { '冬至': (1, [1, 7, 4]), '小寒': (1, [2, 8, 5]), '大寒': (1, [3, 9, 6]), '立春': (1, [8, 5, 2]), '雨水': (1, [9, 6, 3]), '惊蛰': (1, [1, 7, 4]), '春分': (1, [3, 9, 6]), '清明': (1, [4, 1, 7]), '谷雨': (1, [5, 2, 8]), '立夏': (1, [4, 1, 7]), '小满': (1, [5, 2, 8]), '芒种': (1, [6, 3, 9]), '夏至': (-1, [9, 3, 6]), '小暑': (-1, [8, 2, 5]), '大暑': (-1, [7, 1, 4]), '立秋': (-1, [2, 5, 8]), '处暑': (-1, [1, 4, 7]), '白露': (-1, [9, 3, 6]), '秋分': (-1, [7, 1, 4]), '寒露': (-1, [6, 9, 3]), '霜降': (-1, [5, 8, 2]), '立冬': (-1, [6, 9, 3]), '小雪': (-1, [5, 8, 2]), '大雪': (-1, [4, 7, 1]) } STEMS = ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸'] BRANCHES = ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥'] XUN_SHOU = {'甲子': '戊', '甲戌': '己', '甲申': '庚', '甲午': '辛', '甲辰': '壬', '甲寅': '癸'} STAR_MAP = {1: '天蓬', 2: '天芮', 3: '天冲', 4: '天辅', 5: '天禽', 6: '天心', 7: '天柱', 8: '天任', 9: '天英'} DOOR_MAP = {1: '休门', 2: '死门', 3: '伤门', 4: '杜门', 5: '中门', 6: '开门', 7: '惊门', 8: '生门', 9: '景门'} PALACE_BRANCHES = { 1: '子', 2: '未申', 3: '卯', 4: '辰巳', 6: '戌亥', 7: '酉', 8: '丑寅', 9: '午', 5: '中' } def __init__(self, lunar_data): self.data = lunar_data self.bazi = lunar_data['bazi'].split() self.day_stem_branch = self.bazi[2] self.hour_stem_branch = self.bazi[3] def _get_ju(self): term = self.data['solar_term'] if term not in self.TERM_JU: # 找到最近的节气 term = '冬至' is_yang, ju_list = self.TERM_JU[term] return is_yang, ju_list[0] def _get_xun_shou(self): stem_idx = self.STEMS.index(self.hour_stem_branch[0]) branch_idx = self.BRANCHES.index(self.hour_stem_branch[1]) xun_idx = (branch_idx - stem_idx) % 12 xun_map = {0: '甲子', 10: '甲戌', 8: '甲申', 6: '甲午', 4: '甲辰', 2: '甲寅'} xun_head = xun_map.get(xun_idx, '甲子') return xun_head, self.XUN_SHOU[xun_head] def _get_earth_pan(self, is_yang, ju_num): loop = ['戊', '己', '庚', '辛', '壬', '癸', '丁', '丙', '乙'] pan = [""] * 10 for i, stem in enumerate(loop): pos = (ju_num + i - 1) % 9 + 1 if is_yang == 1 else (ju_num - i - 1) % 9 + 1 if pos <= 0: pos += 9 pan[pos] = stem return pan[1:] def _get_duty(self, earth_pan, helper_stem): original_palace = earth_pan.index(helper_stem) + 1 return original_palace, self.STAR_MAP[original_palace], self.DOOR_MAP[original_palace] def _rotate_stars(self, duty_star, target_palace): stars_order = ['天蓬', '天任', '天冲', '天辅', '天英', '天芮', '天柱', '天心'] palaces = [1, 8, 3, 4, 9, 2, 7, 6] try: duty_idx = stars_order.index(duty_star) except: duty_idx = 5 target_idx = palaces.index(target_palace) if target_palace in palaces else 5 offset = (target_idx - duty_idx) % 8 result = [""] * 10 for i in range(8): result[palaces[(i+offset)%8]] = stars_order[i] if result[5] == "": result[5] = "天禽" return result[1:] def _rotate_doors(self, is_yang, duty_door, original_palace, hour_branch): xun_heads = {'甲子': '子', '甲戌': '戌', '甲申': '申', '甲午': '午', '甲辰': '辰', '甲寅': '寅'} xun_head, _ = self._get_xun_shou() diff = (self.BRANCHES.index(hour_branch) - self.BRANCHES.index(xun_heads[xun_head])) % 12 doors_order = ['休门', '生门', '伤门', '杜门', '景门', '死门', '惊门', '开门'] palaces = [1, 8, 3, 4, 9, 2, 7, 6] curr_palace = (original_palace + diff - 1) % 9 + 1 if is_yang == 1 else (original_palace - diff - 1) % 9 + 1 if curr_palace <= 0: curr_palace += 9 try: duty_idx = doors_order.index(duty_door) except: duty_idx = 5 target_idx = palaces.index(curr_palace) if curr_palace in palaces else 5 offset = (target_idx - duty_idx) % 8 result = [""] * 10 for i in range(8): result[palaces[(i+offset)%8]] = doors_order[i] return result[1:] def generate_chart(self): is_yang, ju_num = self._get_ju() earth_pan = self._get_earth_pan(is_yang, ju_num) xun_head, helper_stem = self._get_xun_shou() orig_palace, duty_star, duty_door = self._get_duty(earth_pan, helper_stem) hour_stem = self.hour_stem_branch[0] # 值符随旬首 target_palace = earth_pan.index(hour_stem) + 1 if hour_stem in earth_pan else 5 stars = self._rotate_stars(duty_star, target_palace) doors = self._rotate_doors(is_yang, duty_door, orig_palace, self.hour_stem_branch[1]) gods_order = ['值符', '腾蛇', '太阴', '六合', '白虎', '玄武', '九地', '九天'] if is_yang == -1: gods_order = ['值符', '九天', '九地', '玄武', '白虎', '六合', '太阴', '腾蛇'] palaces = [1, 8, 3, 4, 9, 2, 7, 6] target_idx = palaces.index(target_palace) if target_palace in palaces else 5 gods = [""] * 10 for i in range(8): gods[palaces[(i+target_idx)%8]] = gods_order[i] # 天盘干:随九星转动,星原宫的地盘干即为现在的天盘干 heaven_pan = [""] * 10 for i in range(1, 10): star_now = stars[i-1] if star_now: star_name_to_orig = {v: k for k, v in self.STAR_MAP.items()} orig_idx = star_name_to_orig.get(star_now, 5) heaven_pan[i] = earth_pan[orig_idx-1] pan = [] for i in range(1, 10): palace_data = { "id": i, "star": stars[i-1], "door": doors[i-1], "god": gods[i-1], "earth": earth_pan[i-1], "heaven": heaven_pan[i], "branches": self.PALACE_BRANCHES.get(i, "") } # 注入占断 palace_data["interpretation"] = QiMenInterpreter.interpret_palace(palace_data) pan.append(palace_data) return { "type": "阳遁" if is_yang == 1 else "阴遁", "ju_num": ju_num, "term": self.data['solar_term'], "xun_head": xun_head, "pan": pan, "global_patterns": QiMenInterpreter.detect_global_patterns({ "pan": pan, "xun_head": xun_head, "is_yang": is_yang }) }