Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
//! 八字神煞 (奇门盘上的查法适配)。
//!
//! 实现 10 种常见神煞:
//!
//! | 神煞 | 查法对象 | 目标 |
//! |---|---|---|
//! | 驿马 (`YiMa`) | 日支 | 地支 |
//! | 桃花 (`TaoHua`) | 日支 | 地支 |
//! | 华盖 (`HuaGai`) | 日支 | 地支 |
//! | 天乙贵人 (`TianYi`) | 日干 | 地支 (两个) |
//! | 天德贵人 (`TianDe`) | 月支 | 天干或地支 |
//! | 月德贵人 (`YueDe`) | 月支 | 天干 |
//! | 国印贵人 (`GuoYin`) | 年干 | 地支 |
//! | 文昌 (`WenChang`) | 日干 | 地支 |
//! | 禄神 (`LuShen`) | 日干 | 地支 |
//! | 羊刃 (`YangRen`) | 阳日干 | 地支 |
//!
//! 每个神煞输出包括:种类 ([`ShenShaKind`])、对应的天干/地支 ([`ShenShaTarget`])、
//! 落宫 (`palace`)。一个神煞落多个宫 (如天乙贵人) 会生成多个 [`ShenSha`] 实例。

use std::fmt::Display;
use std::fmt::Formatter;

use tyme4rs::tyme::sixtycycle::EarthBranch;
use tyme4rs::tyme::sixtycycle::HeavenStem;

use crate::auspice::Auspice;
use crate::auspice::Auspicious;
use crate::plate::BRANCH_TO_PALACE;
use crate::plate::Plate;

/// 神煞种类。
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ShenShaKind {
    /// 驿马 — 主迁移、出行、变动。
    YiMa,
    /// 桃花 — 主异性缘、魅力。又名"咸池"。
    TaoHua,
    /// 华盖 — 主孤高、艺术、宗教、清高。
    HuaGai,
    /// 天乙贵人 — 八字神煞之首。
    TianYi,
    /// 天德贵人 — 上天之德庇佑。
    TianDe,
    /// 月德贵人 — 月令之德庇佑。
    YueDe,
    /// 国印贵人 — 主掌权印信、仕途亨通。
    GuoYin,
    /// 文昌 — 主学业、文采、考试、功名。
    WenChang,
    /// 禄神 — 主俸禄、衣食、财源。
    LuShen,
    /// 羊刃 — 八字凶神,刚猛之气的极端表现 (仅阳干)。
    YangRen,
}

impl Auspicious for ShenShaKind {
    fn name(&self) -> &'static str {
        match self {
            Self::YiMa => "驿马",
            Self::TaoHua => "桃花",
            Self::HuaGai => "华盖",
            Self::TianYi => "天乙贵人",
            Self::TianDe => "天德贵人",
            Self::YueDe => "月德贵人",
            Self::GuoYin => "国印贵人",
            Self::WenChang => "文昌",
            Self::LuShen => "禄神",
            Self::YangRen => "羊刃",
        }
    }

    fn summary(&self) -> &'static str {
        match self {
            Self::YiMa => "主迁移、出行、变动",
            Self::TaoHua => "主异性缘、魅力、风流韵事",
            Self::HuaGai => "主孤高、艺术、宗教、清高",
            Self::TianYi => "八字神煞之首,主逢凶化吉、贵人相助",
            Self::TianDe => "上天之德庇佑,主化灾解难",
            Self::YueDe => "月令之德庇佑,主品行端正",
            Self::GuoYin => "主掌权印信、仕途亨通",
            Self::WenChang => "主学业、文采、考试、功名",
            Self::LuShen => "代表俸禄、衣食、财源",
            Self::YangRen => "刚猛之气的极端表现,主血光、刑伤、果敢",
        }
    }

    /// - 天乙/天德/月德贵人 → 大吉 (诸神煞之首)
    /// - 国印、文昌、禄神 → 吉
    /// - 驿马、桃花、华盖 → 中和 (中性,主变动/异性缘/孤高)
    /// - 羊刃 → 凶 (主血光、刑伤)
    fn auspice(&self) -> Auspice {
        match self {
            Self::TianYi | Self::TianDe | Self::YueDe => Auspice::GreatAuspicious,
            Self::GuoYin | Self::WenChang | Self::LuShen => Auspice::Auspicious,
            Self::YiMa | Self::TaoHua | Self::HuaGai => Auspice::Neutral,
            Self::YangRen => Auspice::Inauspicious,
        }
    }
}

impl Display for ShenShaKind {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.write_str(self.name()) }
}

/// 神煞对应的目标 (天干或地支)。
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ShenShaTarget {
    /// 天干目标 (如月德=丙)
    Stem(HeavenStem),
    /// 地支目标 (如驿马=寅)
    Branch(EarthBranch),
}

impl Display for ShenShaTarget {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Stem(s) => write!(f, "{}", s),
            Self::Branch(b) => write!(f, "{}", b),
        }
    }
}

/// 单个神煞实例。
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ShenSha {
    pub(crate) kind: ShenShaKind,
    pub(crate) target: ShenShaTarget,
    pub(crate) palace: u8,
}

impl ShenSha {
    /// 神煞种类。
    pub fn kind(&self) -> ShenShaKind { self.kind }

    /// 神煞对应的天干或地支。
    pub fn target(&self) -> &ShenShaTarget { &self.target }

    /// 神煞落入的宫位号 (1..=9)。
    ///
    /// 落多宫的神煞 (如天乙贵人有两支) 会生成多个 [`ShenSha`] 实例,各自落一宫。
    pub fn palace(&self) -> u8 { self.palace }
}

impl Auspicious for ShenSha {
    /// 委派到 [`ShenShaKind`] 的实现。
    fn name(&self) -> &'static str { self.kind.name() }

    fn summary(&self) -> &'static str { self.kind.summary() }

    fn auspice(&self) -> Auspice { self.kind.auspice() }
}

impl Display for ShenSha {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}({}{}宫)", self.kind, self.target, self.palace)
    }
}

// ---------- 查表常量 ----------

/// 驿马:日/年支 (0..=11) → 驿马地支索引。
const YI_MA_TABLE: [u8; 12] = [
    2,  // 子 → 寅
    11, // 丑 → 亥
    8,  // 寅 → 申
    5,  // 卯 → 巳
    2,  // 辰 → 寅
    11, // 巳 → 亥
    8,  // 午 → 申
    5,  // 未 → 巳
    2,  // 申 → 寅
    11, // 酉 → 亥
    8,  // 戌 → 申
    5,  // 亥 → 巳
];

/// 桃花:日/年支 → 桃花地支索引。
const TAO_HUA_TABLE: [u8; 12] = [
    9, // 子 → 酉
    6, // 丑 → 午
    3, // 寅 → 卯
    0, // 卯 → 子
    9, // 辰 → 酉
    6, // 巳 → 午
    3, // 午 → 卯
    0, // 未 → 子
    9, // 申 → 酉
    6, // 酉 → 午
    3, // 戌 → 卯
    0, // 亥 → 子
];

/// 华盖:日/年支 → 华盖地支索引 (三合局之墓库)。
const HUA_GAI_TABLE: [u8; 12] = [
    4,  // 子 → 辰
    1,  // 丑 → 丑
    10, // 寅 → 戌
    7,  // 卯 → 未
    4,  // 辰 → 辰
    1,  // 巳 → 丑
    10, // 午 → 戌
    7,  // 未 → 未
    4,  // 申 → 辰
    1,  // 酉 → 丑
    10, // 戌 → 戌
    7,  // 亥 → 未
];

/// 天乙贵人:日干 (0..=9) → 两个贵人地支索引。
const TIAN_YI_TABLE: [[u8; 2]; 10] = [
    [1, 7],  // 甲 → 丑、未
    [0, 8],  // 乙 → 子、申
    [11, 9], // 丙 → 亥、酉
    [11, 9], // 丁 → 亥、酉
    [1, 7],  // 戊 → 丑、未
    [0, 8],  // 己 → 子、申
    [6, 2],  // 庚 → 午、寅
    [6, 2],  // 辛 → 午、寅
    [5, 3],  // 壬 → 巳、卯
    [5, 3],  // 癸 → 巳、卯
];

/// 天德贵人:月支 (0..=11) → (kind, value)。
/// kind=0 表示天干(value 是天干索引);kind=1 表示地支(value 是地支索引)。
const TIAN_DE_TABLE: [(u8, u8); 12] = [
    (1, 5),  // 子月 → 巳
    (0, 6),  // 丑月 → 庚
    (0, 3),  // 寅月 → 丁
    (1, 8),  // 卯月 → 申
    (0, 8),  // 辰月 → 壬
    (0, 7),  // 巳月 → 辛
    (1, 11), // 午月 → 亥
    (0, 0),  // 未月 → 甲
    (0, 9),  // 申月 → 癸
    (1, 2),  // 酉月 → 寅
    (0, 2),  // 戌月 → 丙
    (0, 1),  // 亥月 → 乙
];

/// 月德贵人:月支 → 天干索引 (三合局之阳干)。
const YUE_DE_TABLE: [u8; 12] = [
    8, // 子 → 壬
    6, // 丑 → 庚
    2, // 寅 → 丙
    0, // 卯 → 甲
    8, // 辰 → 壬
    6, // 巳 → 庚
    2, // 午 → 丙
    0, // 未 → 甲
    8, // 申 → 壬
    6, // 酉 → 庚
    2, // 戌 → 丙
    0, // 亥 → 甲
];

/// 国印贵人:年干 (0..=9) → 地支索引。
const GUO_YIN_TABLE: [u8; 10] = [
    10, // 甲 → 戌
    11, // 乙 → 亥
    1,  // 丙 → 丑
    2,  // 丁 → 寅
    1,  // 戊 → 丑
    2,  // 己 → 寅
    4,  // 庚 → 辰
    5,  // 辛 → 巳
    7,  // 壬 → 未
    8,  // 癸 → 申
];

/// 文昌:日/年干 → 地支索引 (天干食神之临官位)。
const WEN_CHANG_TABLE: [u8; 10] = [
    5,  // 甲 → 巳
    6,  // 乙 → 午
    8,  // 丙 → 申
    9,  // 丁 → 酉
    8,  // 戊 → 申
    9,  // 己 → 酉
    11, // 庚 → 亥
    0,  // 辛 → 子
    2,  // 壬 → 寅
    3,  // 癸 → 卯
];

/// 禄神:日干 → 地支索引 (天干临官位)。
const LU_SHEN_TABLE: [u8; 10] = [
    2,  // 甲 → 寅
    3,  // 乙 → 卯
    5,  // 丙 → 巳
    6,  // 丁 → 午
    5,  // 戊 → 巳
    6,  // 己 → 午
    8,  // 庚 → 申
    9,  // 辛 → 酉
    11, // 壬 → 亥
    0,  // 癸 → 子
];

/// 羊刃:阳日干 → 地支索引;阴干为 `None`。
const YANG_REN_TABLE: [Option<u8>; 10] = [
    Some(3), // 甲 → 卯
    None,    //    Some(6), // 丙 → 午
    None,    //    Some(6), // 戊 → 午
    None,    //    Some(9), // 庚 → 酉
    None,    //    Some(0), // 壬 → 子
    None,    //];

// ---------- 检测函数 ----------

/// 把"地支神煞"添加到 `out`:神煞有唯一落宫 (由 [`BRANCH_TO_PALACE`] 决定),生成单实例。
fn push_branch(out: &mut Vec<ShenSha>, kind: ShenShaKind, branch_idx: usize) {
    if branch_idx >= 12 {
        return;
    }
    let branch = EarthBranch::from_index(branch_idx as isize);
    let palace = BRANCH_TO_PALACE[branch_idx];
    out.push(ShenSha { kind, target: ShenShaTarget::Branch(branch), palace });
}

/// 把"天干神煞"添加到 `out`:扫描地盘所有宫,该天干所在宫各生成一个实例 (通常仅一处)。
fn push_stem(out: &mut Vec<ShenSha>, kind: ShenShaKind, stem_idx: usize, earth_plate: &Plate<HeavenStem>) {
    if stem_idx >= 10 {
        return;
    }
    let stem = HeavenStem::from_index(stem_idx as isize);
    for palace in 1..=9u8 {
        if earth_plate.get(palace) == Some(&stem) {
            out.push(ShenSha { kind, target: ShenShaTarget::Stem(stem.clone()), palace });
        }
    }
}

/// 检测全部 10 种神煞。每个落宫各生成一个 [`ShenSha`] 实例。
///
/// - 地支神煞:由 [`BRANCH_TO_PALACE`] 直接映射 (单宫)
/// - 天干神煞:扫描地盘三奇六仪所有宫位 (通常单宫)
/// - 天乙贵人有两支 → 生成两个实例
pub(crate) fn detect_shen_sha(
    year_stem: &HeavenStem, month_branch: &EarthBranch, day_stem: &HeavenStem, day_branch: &EarthBranch,
    earth_plate: &Plate<HeavenStem>,
) -> Vec<ShenSha> {
    let mut out = Vec::with_capacity(12);

    let day_branch_idx = day_branch.get_index();
    let day_stem_idx = day_stem.get_index();
    let month_branch_idx = month_branch.get_index();
    let year_stem_idx = year_stem.get_index();

    if day_branch_idx < 12 {
        push_branch(&mut out, ShenShaKind::YiMa, YI_MA_TABLE[day_branch_idx] as usize);
        push_branch(&mut out, ShenShaKind::TaoHua, TAO_HUA_TABLE[day_branch_idx] as usize);
        push_branch(&mut out, ShenShaKind::HuaGai, HUA_GAI_TABLE[day_branch_idx] as usize);
    }

    if day_stem_idx < 10 {
        let [a, b] = TIAN_YI_TABLE[day_stem_idx];
        push_branch(&mut out, ShenShaKind::TianYi, a as usize);
        push_branch(&mut out, ShenShaKind::TianYi, b as usize);

        push_branch(&mut out, ShenShaKind::WenChang, WEN_CHANG_TABLE[day_stem_idx] as usize);
        push_branch(&mut out, ShenShaKind::LuShen, LU_SHEN_TABLE[day_stem_idx] as usize);
        if let Some(idx) = YANG_REN_TABLE[day_stem_idx] {
            push_branch(&mut out, ShenShaKind::YangRen, idx as usize);
        }
    }

    if month_branch_idx < 12 {
        // 天德贵人:可能是天干或地支
        let (kind_flag, value) = TIAN_DE_TABLE[month_branch_idx];
        if kind_flag == 0 {
            push_stem(&mut out, ShenShaKind::TianDe, value as usize, earth_plate);
        } else {
            push_branch(&mut out, ShenShaKind::TianDe, value as usize);
        }
        // 月德贵人:天干
        push_stem(&mut out, ShenShaKind::YueDe, YUE_DE_TABLE[month_branch_idx] as usize, earth_plate);
    }

    if year_stem_idx < 10 {
        push_branch(&mut out, ShenShaKind::GuoYin, GUO_YIN_TABLE[year_stem_idx] as usize);
    }

    out
}

#[cfg(test)]
mod tests {
    use super::*;

    fn branch_target(s: &ShenSha) -> Option<&EarthBranch> {
        match &s.target {
            ShenShaTarget::Branch(b) => Some(b),
            _ => None,
        }
    }

    fn stem_target(s: &ShenSha) -> Option<&HeavenStem> {
        match &s.target {
            ShenShaTarget::Stem(s) => Some(s),
            _ => None,
        }
    }

    #[test]
    fn yi_ma_lookup() {
        // 申子辰 → 寅
        assert_eq!(YI_MA_TABLE[0], 2);
        assert_eq!(YI_MA_TABLE[8], 2);
        // 寅午戌 → 申
        assert_eq!(YI_MA_TABLE[2], 8);
        assert_eq!(YI_MA_TABLE[6], 8);
    }

    #[test]
    fn tao_hua_lookup() {
        // 申子辰 → 酉
        assert_eq!(TAO_HUA_TABLE[0], 9);
        assert_eq!(TAO_HUA_TABLE[4], 9);
    }

    #[test]
    fn tian_yi_jia_two_branches() {
        let [a, b] = TIAN_YI_TABLE[0];
        assert_eq!([a, b], [1u8, 7u8]); // 甲 → 丑、未
    }

    #[test]
    fn yue_de_san_he_yang() {
        // 寅午戌月 → 丙
        assert_eq!(YUE_DE_TABLE[2], 2);
        assert_eq!(YUE_DE_TABLE[6], 2);
        // 申子辰月 → 壬
        assert_eq!(YUE_DE_TABLE[0], 8);
    }

    #[test]
    fn yang_ren_only_yang_stems() {
        for (i, slot) in YANG_REN_TABLE.iter().enumerate() {
            if i % 2 == 0 {
                assert!(slot.is_some(), "yang stem {} must have YangRen", i);
            } else {
                assert!(slot.is_none(), "yin stem {} must not have YangRen", i);
            }
        }
    }

    #[test]
    fn detect_includes_all_kinds() {
        use tyme4rs::tyme::enums::YinYang;

        use crate::plate::build_earth_plate;

        let earth = build_earth_plate(YinYang::YANG, 1);
        let result = detect_shen_sha(
            &HeavenStem::from_name(""),
            &EarthBranch::from_name(""),
            &HeavenStem::from_name(""),
            &EarthBranch::from_name(""),
            &earth,
        );

        // 至少应包含:驿马、桃花、华盖、天乙×2、天德、月德、国印、文昌、禄神、羊刃 (甲为阳干)
        let has_kind = |k: ShenShaKind| result.iter().any(|s| s.kind == k);
        for k in [
            ShenShaKind::YiMa,
            ShenShaKind::TaoHua,
            ShenShaKind::HuaGai,
            ShenShaKind::TianYi,
            ShenShaKind::TianDe,
            ShenShaKind::YueDe,
            ShenShaKind::GuoYin,
            ShenShaKind::WenChang,
            ShenShaKind::LuShen,
            ShenShaKind::YangRen,
        ] {
            assert!(has_kind(k), "missing kind {}", k.name());
        }

        // 甲日支子 → 驿马 寅
        let yi_ma: Vec<&ShenSha> = result.iter().filter(|s| s.kind == ShenShaKind::YiMa).collect();
        assert_eq!(yi_ma.len(), 1);
        assert_eq!(branch_target(yi_ma[0]).unwrap().to_string(), "");

        // 月德 寅月 → 丙;target 是天干
        let yue_de: Vec<&ShenSha> = result.iter().filter(|s| s.kind == ShenShaKind::YueDe).collect();
        assert_eq!(yue_de.len(), 1);
        assert_eq!(stem_target(yue_de[0]).unwrap().to_string(), "");
    }

    #[test]
    fn yi_stem_has_no_yang_ren() {
        use tyme4rs::tyme::enums::YinYang;

        use crate::plate::build_earth_plate;

        let earth = build_earth_plate(YinYang::YANG, 1);
        let result = detect_shen_sha(
            &HeavenStem::from_name(""),
            &EarthBranch::from_name(""),
            &HeavenStem::from_name(""),
            &EarthBranch::from_name(""),
            &earth,
        );
        assert!(!result.iter().any(|s| s.kind == ShenShaKind::YangRen));
    }
}