iching/
hexagram.rs

1use crate::{
2    divination_method::DivinationMethod, line::Line, symbols::big_line::LINE_SPACER,
3    trigram::Trigram,
4};
5
6/// The 64 Hexagrams have several different orderings, the most
7/// common of which is the King Wen sequence.
8/// [See here for more details / history](https://en.wikipedia.org/wiki/King_Wen_sequence)
9#[derive(Clone, Copy)]
10pub enum HexagramOrdering {
11    /// The most common sequence
12    KingWen,
13    /// a.k.a Fu Xi sequence, Shao Yong sequence
14    Binary,
15    /// From the [Mawangdui Silk Texts](https://en.wikipedia.org/wiki/Mawangdui_Silk_Texts)
16    Mawangdui,
17}
18
19/// A `Hexagram` is a collection of lines divided into two groups: lines above and lines below.
20/// The order of the lines determines the specific hexagram (the primary hexagram) and its
21/// meaning. If lines are marked as "changing", then a second hexagram (the relating hexagram)
22/// will be produced that provides additional meaning. Special attention should be paid to
23/// "changing" lines as they can change the meaning of the primary hexagram.
24/// [See here for more details / history](https://en.wikipedia.org/wiki/Hexagram_\(I_Ching\))
25pub struct Hexagram {
26    above: Trigram,
27    below: Trigram,
28}
29
30impl Hexagram {
31    /// Create a new `Hexagram` from two [`Trigram`]s. The `Trigram`s are consumed in the process.
32    pub fn new(above: Trigram, below: Trigram) -> Self {
33        Hexagram { above, below }
34    }
35
36    pub fn above(&self) -> &Trigram {
37        &self.above
38    }
39
40    pub fn below(&self) -> &Trigram {
41        &self.below
42    }
43
44    /// Create a new `Hexagram` from random [`Trigram`]s.
45    pub fn new_random(divination_method: DivinationMethod) -> Self {
46        Hexagram {
47            above: Trigram::new_random(divination_method),
48            below: Trigram::new_random(divination_method),
49        }
50    }
51
52    /// Get the unicode symbol representing this Hexagram. The symbol is retrieved from the given
53    /// `HexagramRepository`.
54    pub fn symbol(&self, with_changes: bool) -> &str {
55        use crate::symbols::hexagram::*;
56        use crate::trigram::TrigramName::*;
57
58        let relating_hexagram = with_changes.then(|| self.relating_hexagram()).flatten();
59        let hexagram = relating_hexagram.as_ref().unwrap_or(self);
60
61        match (hexagram.above.into(), hexagram.below.into()) {
62            (Qian, Qian) => QIAN_QIAN_SYMBOL,
63            (Qian, Kun) => QIAN_KUN_SYMBOL,
64            (Qian, Zhen) => QIAN_ZHEN_SYMBOL,
65            (Qian, Kan) => QIAN_KAN_SYMBOL,
66            (Qian, Gen) => QIAN_GEN_SYMBOL,
67            (Qian, Xun) => QIAN_XUN_SYMBOL,
68            (Qian, Li) => QIAN_LI_SYMBOL,
69            (Qian, Dui) => QIAN_DUI_SYMBOL,
70            (Kun, Qian) => KUN_QIAN_SYMBOL,
71            (Kun, Kun) => KUN_KUN_SYMBOL,
72            (Kun, Zhen) => KUN_ZHEN_SYMBOL,
73            (Kun, Kan) => KUN_KAN_SYMBOL,
74            (Kun, Gen) => KUN_GEN_SYMBOL,
75            (Kun, Xun) => KUN_XUN_SYMBOL,
76            (Kun, Li) => KUN_LI_SYMBOL,
77            (Kun, Dui) => KUN_DUI_SYMBOL,
78            (Zhen, Qian) => ZHEN_QIAN_SYMBOL,
79            (Zhen, Kun) => ZHEN_KUN_SYMBOL,
80            (Zhen, Zhen) => ZHEN_ZHEN_SYMBOL,
81            (Zhen, Kan) => ZHEN_KAN_SYMBOL,
82            (Zhen, Gen) => ZHEN_GEN_SYMBOL,
83            (Zhen, Xun) => ZHEN_XUN_SYMBOL,
84            (Zhen, Li) => ZHEN_LI_SYMBOL,
85            (Zhen, Dui) => ZHEN_DUI_SYMBOL,
86            (Kan, Qian) => KAN_QIAN_SYMBOL,
87            (Kan, Kun) => KAN_KUN_SYMBOL,
88            (Kan, Zhen) => KAN_ZHEN_SYMBOL,
89            (Kan, Kan) => KAN_KAN_SYMBOL,
90            (Kan, Gen) => KAN_GEN_SYMBOL,
91            (Kan, Xun) => KAN_XUN_SYMBOL,
92            (Kan, Li) => KAN_LI_SYMBOL,
93            (Kan, Dui) => KAN_DUI_SYMBOL,
94            (Gen, Qian) => GEN_QIAN_SYMBOL,
95            (Gen, Kun) => GEN_KUN_SYMBOL,
96            (Gen, Zhen) => GEN_ZHEN_SYMBOL,
97            (Gen, Kan) => GEN_KAN_SYMBOL,
98            (Gen, Gen) => GEN_GEN_SYMBOL,
99            (Gen, Xun) => GEN_XUN_SYMBOL,
100            (Gen, Li) => GEN_LI_SYMBOL,
101            (Gen, Dui) => GEN_DUI_SYMBOL,
102            (Xun, Qian) => XUN_QIAN_SYMBOL,
103            (Xun, Kun) => XUN_KUN_SYMBOL,
104            (Xun, Zhen) => XUN_ZHEN_SYMBOL,
105            (Xun, Kan) => XUN_KAN_SYMBOL,
106            (Xun, Gen) => XUN_GEN_SYMBOL,
107            (Xun, Xun) => XUN_XUN_SYMBOL,
108            (Xun, Li) => XUN_LI_SYMBOL,
109            (Xun, Dui) => XUN_DUI_SYMBOL,
110            (Li, Qian) => LI_QIAN_SYMBOL,
111            (Li, Kun) => LI_KUN_SYMBOL,
112            (Li, Zhen) => LI_ZHEN_SYMBOL,
113            (Li, Kan) => LI_KAN_SYMBOL,
114            (Li, Gen) => LI_GEN_SYMBOL,
115            (Li, Xun) => LI_XUN_SYMBOL,
116            (Li, Li) => LI_LI_SYMBOL,
117            (Li, Dui) => LI_DUI_SYMBOL,
118            (Dui, Qian) => DUI_QIAN_SYMBOL,
119            (Dui, Kun) => DUI_KUN_SYMBOL,
120            (Dui, Zhen) => DUI_ZHEN_SYMBOL,
121            (Dui, Kan) => DUI_KAN_SYMBOL,
122            (Dui, Gen) => DUI_GEN_SYMBOL,
123            (Dui, Xun) => DUI_XUN_SYMBOL,
124            (Dui, Li) => DUI_LI_SYMBOL,
125            (Dui, Dui) => DUI_DUI_SYMBOL,
126        }
127    }
128
129    /// Get a `Vec` of `usize`s representing the positions of lines that are marked as "changing".
130    pub fn get_changing_line_positions(&self) -> Vec<usize> {
131        self.lines()
132            .enumerate()
133            .filter_map(|(index, &line)| {
134                if matches!(line, Line::Broken { changing: true })
135                    || matches!(line, Line::Unbroken { changing: true })
136                {
137                    Some(index + 1)
138                } else {
139                    None
140                }
141            })
142            .collect()
143    }
144
145    /// Get a vec of this `Hexagram`'s `Line`s _("above" lines followed by "below" lines.)_
146    pub fn lines(&self) -> impl Iterator<Item = &Line> {
147        self.above.lines().chain(self.below.lines())
148    }
149
150    /// Print the `Hexagram` as large ASCII-art lines.
151    pub fn print_big(&self) {
152        print!("{LINE_SPACER}");
153        self.above.print_big();
154        self.below.print_big();
155    }
156
157    /// Return this Hexagram's "relating" Hexagram, if it has one. Only Hexagrams with changing lines
158    /// have relating Hexagrams.
159    pub fn relating_hexagram(&self) -> Option<Self> {
160        let Hexagram { above, below } = self;
161        let Trigram(a1, a2, a3) = above;
162        let Trigram(b1, b2, b3) = below;
163        let lines_are_changing = [a1, a2, a3, b1, b2, b3]
164            .iter()
165            .any(|line| line.is_changing());
166
167        if lines_are_changing {
168            Some(Hexagram::new(
169                Trigram(a1.settle(), a2.settle(), a3.settle()),
170                Trigram(b1.settle(), b2.settle(), b3.settle()),
171            ))
172        } else {
173            None
174        }
175    }
176}