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 }
pub fn palace(&self) -> u8 { self.palace }
}
impl Auspicious for ShenSha {
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)
}
}
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, ];
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], ];
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, ];
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, ];
const YANG_REN_TABLE: [Option<u8>; 10] = [
Some(3), None, Some(6), None, Some(6), None, Some(9), None, Some(0), None, ];
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 });
}
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 });
}
}
}
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,
);
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(), "寅");
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));
}
}