three_style_lib/moves/
core.rs

1use crate::error::Error;
2use std::{fmt, ops::Mul, str::FromStr};
3
4pub trait Inverse {
5    fn inverse(&self) -> Self;
6}
7
8#[rustfmt::skip]
9#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Hash)]
10pub enum MoveKind {
11    U, R, F, D, L, B,
12    X, Y, Z, M, E, S,
13    Uw, Rw, Fw, Dw, Lw, Bw,
14}
15
16impl MoveKind {
17    pub fn is_side(self) -> bool {
18        matches!(
19            self,
20            Self::U | Self::R | Self::F | Self::D | Self::L | Self::B
21        )
22    }
23
24    pub fn is_rotation(self) -> bool {
25        matches!(self, Self::X | Self::Y | Self::Z)
26    }
27
28    pub fn is_slice(self) -> bool {
29        matches!(self, Self::M | Self::E | Self::S)
30    }
31
32    pub fn is_wide(self) -> bool {
33        matches!(
34            self,
35            Self::Uw | Self::Rw | Self::Fw | Self::Dw | Self::Lw | Self::Bw
36        )
37    }
38
39    pub fn to_moves(&self) -> [Move; 3] {
40        [
41            Move::new(*self, MoveCount::Simple),
42            Move::new(*self, MoveCount::Double),
43            Move::new(*self, MoveCount::Prime),
44        ]
45    }
46
47    pub fn parallel(&self) -> Vec<MoveKind> {
48        match self {
49            MoveKind::E => vec![MoveKind::U, MoveKind::D],
50            MoveKind::M => vec![MoveKind::R, MoveKind::L],
51            MoveKind::S => vec![MoveKind::F, MoveKind::B],
52            MoveKind::U | MoveKind::D => vec![self.inverse(), MoveKind::E],
53            MoveKind::R | MoveKind::L => vec![self.inverse(), MoveKind::M],
54            MoveKind::F | MoveKind::B => vec![self.inverse(), MoveKind::S],
55            _ => vec![self.inverse()],
56        }
57    }
58}
59
60impl Inverse for MoveKind {
61    fn inverse(&self) -> Self {
62        match self {
63            MoveKind::U => MoveKind::D,
64            MoveKind::R => MoveKind::L,
65            MoveKind::F => MoveKind::B,
66            MoveKind::D => MoveKind::U,
67            MoveKind::L => MoveKind::R,
68            MoveKind::B => MoveKind::F,
69            MoveKind::Uw => MoveKind::Dw,
70            MoveKind::Rw => MoveKind::Lw,
71            MoveKind::Fw => MoveKind::Bw,
72            MoveKind::Dw => MoveKind::Uw,
73            MoveKind::Lw => MoveKind::Rw,
74            MoveKind::Bw => MoveKind::Fw,
75            _ => *self,
76        }
77    }
78}
79
80impl FromStr for MoveKind {
81    type Err = Error;
82
83    fn from_str(s: &str) -> Result<Self, Self::Err> {
84        match s {
85            "U" => Ok(MoveKind::U),
86            "F" => Ok(MoveKind::F),
87            "R" => Ok(MoveKind::R),
88            "B" => Ok(MoveKind::B),
89            "L" => Ok(MoveKind::L),
90            "D" => Ok(MoveKind::D),
91            "M" => Ok(MoveKind::M),
92            "S" => Ok(MoveKind::S),
93            "E" => Ok(MoveKind::E),
94            "x" => Ok(MoveKind::X),
95            "y" => Ok(MoveKind::Y),
96            "z" => Ok(MoveKind::Z),
97            "u" => Ok(MoveKind::Uw),
98            "f" => Ok(MoveKind::Fw),
99            "r" => Ok(MoveKind::Rw),
100            "b" => Ok(MoveKind::Bw),
101            "l" => Ok(MoveKind::Lw),
102            "d" => Ok(MoveKind::Dw),
103            _ => Err(Error::InvalidMove(s.to_owned())),
104        }
105    }
106}
107
108impl fmt::Display for MoveKind {
109    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110        write!(f, "{:?}", self)
111    }
112}
113
114#[derive(Debug, PartialEq, Clone, Copy)]
115pub enum MoveCount {
116    Simple = 1,
117    Double = 2,
118    Prime = 3,
119}
120
121impl Inverse for MoveCount {
122    fn inverse(&self) -> Self {
123        match self {
124            MoveCount::Simple => MoveCount::Prime,
125            MoveCount::Double => MoveCount::Double,
126            MoveCount::Prime => MoveCount::Simple,
127        }
128    }
129}
130
131impl FromStr for MoveCount {
132    type Err = Error;
133
134    fn from_str(s: &str) -> Result<Self, Self::Err> {
135        match s {
136            "2" => Ok(MoveCount::Double),
137            "'" => Ok(MoveCount::Prime),
138            "" => Ok(MoveCount::Simple),
139            _ => Err(Error::InvalidMove(s.to_owned())),
140        }
141    }
142}
143
144impl fmt::Display for MoveCount {
145    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
146        match self {
147            MoveCount::Simple => write!(f, ""),
148            MoveCount::Double => write!(f, "2"),
149            MoveCount::Prime => write!(f, "'"),
150        }
151    }
152}
153
154#[derive(Debug, PartialEq, Clone, Copy)]
155pub struct Move {
156    pub kind: MoveKind,
157    pub count: MoveCount,
158}
159
160impl Move {
161    pub fn new(kind: MoveKind, count: MoveCount) -> Self {
162        Self { kind, count }
163    }
164
165    fn reduce(&self, rhs: Move) -> Option<Move> {
166        use {MoveCount as C, MoveKind as M};
167
168        let is_inversed = self.count.inverse() == rhs.count;
169        let is_slice = self.kind.is_slice();
170        let count = if is_slice { rhs.count } else { self.count }; // determined by wide or side moves
171
172        match (self.kind, self.count, rhs.kind, rhs.count) {
173            // wide move generators
174            (M::U, _, M::E, _) => Some(Move::new(M::Uw, count)),
175            (M::E, _, M::D, _) if is_inversed => Some(Move::new(M::Dw, count)),
176            (M::L, _, M::M, _) => Some(Move::new(M::Lw, count)),
177            (M::M, _, M::R, _) if is_inversed => Some(Move::new(M::Rw, count)),
178            (M::F, _, M::S, _) => Some(Move::new(M::Fw, count)),
179            (M::S, _, M::B, _) if is_inversed => Some(Move::new(M::Bw, count)),
180
181            // wide move reduction
182            (M::R, C::Prime, M::Rw, C::Simple) => Some(Move::new(M::M, C::Prime)),
183            (M::R, C::Simple, M::Rw, C::Prime) => Some(Move::new(M::M, C::Simple)),
184            (M::L, C::Prime, M::Lw, C::Simple) => Some(Move::new(M::M, C::Simple)),
185            (M::L, C::Simple, M::Lw, C::Prime) => Some(Move::new(M::M, C::Prime)),
186            (M::U, C::Prime, M::Uw, C::Simple) => Some(Move::new(M::E, C::Simple)),
187            (M::U, C::Simple, M::Uw, C::Prime) => Some(Move::new(M::E, C::Prime)),
188            (M::D, C::Prime, M::Dw, C::Simple) => Some(Move::new(M::E, C::Prime)),
189            (M::D, C::Simple, M::Dw, C::Prime) => Some(Move::new(M::E, C::Simple)),
190            (M::F, C::Prime, M::Fw, C::Simple) => Some(Move::new(M::S, C::Simple)),
191            (M::F, C::Simple, M::Fw, C::Prime) => Some(Move::new(M::S, C::Prime)),
192            (M::B, C::Prime, M::Bw, C::Simple) => Some(Move::new(M::S, C::Prime)),
193            (M::B, C::Simple, M::Bw, C::Prime) => Some(Move::new(M::S, C::Simple)),
194
195            (M::M, _, M::Lw, _) if is_inversed => Some(Move::new(M::L, count)),
196            (M::M, _, M::Rw, _) if self.count == rhs.count => Some(Move::new(M::R, count)),
197            (M::E, _, M::Uw, _) if is_inversed => Some(Move::new(M::U, count)),
198            (M::E, _, M::Dw, _) if self.count == rhs.count => Some(Move::new(M::D, count)),
199            (M::S, _, M::Fw, _) if is_inversed => Some(Move::new(M::F, count)),
200            (M::S, _, M::Bw, _) if self.count == rhs.count => Some(Move::new(M::B, count)),
201
202            // move count reduction
203            (_, _, _, _) if self.kind == rhs.kind && !is_inversed => {
204                let kind = self.kind;
205                let count = match (self.count as usize + rhs.count as usize) % 4 {
206                    2 => MoveCount::Double,
207                    3 => MoveCount::Prime,
208                    _ => MoveCount::Simple,
209                };
210                Some(Move { kind, count })
211            }
212            _ => None,
213        }
214    }
215}
216
217impl Inverse for Move {
218    fn inverse(&self) -> Self {
219        Self::new(self.kind, self.count.inverse())
220    }
221}
222
223impl Mul<Move> for Move {
224    type Output = Option<Move>;
225
226    fn mul(self, rhs: Move) -> Self::Output {
227        self.reduce(rhs).or(rhs.reduce(self))
228    }
229}
230
231impl FromStr for Move {
232    type Err = Error;
233
234    fn from_str(s: &str) -> Result<Self, Self::Err> {
235        let kind = s.get(..1).unwrap_or_default();
236        let kind = MoveKind::from_str(kind)?;
237        let count = s.get(1..2).unwrap_or_default();
238        let count = MoveCount::from_str(count)?;
239
240        Ok(Self { kind, count })
241    }
242}
243
244impl fmt::Display for Move {
245    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
246        write!(f, "{}{}", self.kind, self.count)
247    }
248}
249
250#[cfg(test)]
251mod tests {
252    use super::*;
253
254    #[test]
255    fn test_move_str() {
256        assert_eq!(
257            Move::from_str("R"),
258            Ok(Move::new(MoveKind::R, MoveCount::Simple))
259        );
260        assert_eq!(
261            Move::from_str("R2"),
262            Ok(Move::new(MoveKind::R, MoveCount::Double))
263        );
264        assert_eq!(
265            Move::from_str("R'"),
266            Ok(Move::new(MoveKind::R, MoveCount::Prime))
267        );
268    }
269}