1use lazy_static::lazy_static;
2use regex::{Captures, Regex};
3use thiserror::Error;
4
5use crate::bitboard::{
6 BOTTOM_SQUARES,
7 LEFT_SQUARES,
8 MonoBitBoard,
9 RIGHT_SQUARES,
10 TOP_SQUARES,
11};
12use crate::board::{Board, Player};
13
14#[derive(PartialEq, Debug)]
15pub enum Position {
16 Bottom,
17 Left,
18 BottomLeft,
19 BottomRight,
20 Top,
21 Right,
22 TopRight,
23 TopLeft,
24 Interior,
25}
26
27impl From<MonoBitBoard> for Position {
28 fn from(bitboard: MonoBitBoard) -> Self {
29 let is_left = LEFT_SQUARES.contains(bitboard);
30 let is_right = RIGHT_SQUARES.contains(bitboard);
31 let is_top = TOP_SQUARES.contains(bitboard);
32 let is_bottom = BOTTOM_SQUARES.contains(bitboard);
33
34 match (is_left, is_right, is_top, is_bottom) {
35 (false, false, false, true) => Position::Bottom,
36 (true, false, false, false) => Position::Left,
37 (true, false, false, true) => Position::BottomLeft,
38 (false, true, false, true) => Position::BottomRight,
39 (false, false, true, false) => Position::Top,
40 (false, true, false, false) => Position::Right,
41 (false, true, true, false) => Position::TopRight,
42 (true, false, true, false) => Position::TopLeft,
43 (false, false, false, false) => Position::Interior,
44 _ => panic!("This should be impossible to reach.")
45 }
46 }
47}
48
49#[derive(Debug, Error)]
51pub enum NotationError {
52 #[error("Provided value did not conform to a valid checkers notation format.")]
53 InvalidFormat,
54
55 #[error("Provided value operates outside the realm of a classical checkers board.")]
56 OutOfRange,
57
58 #[error("Provided value represented a piece standing still. The destination square was equal to the source.")]
59 Idle,
60}
61
62#[derive(Debug, Error)]
63#[error("Square could not be converted")]
64pub struct SquareConversionError;
65
66#[derive(Copy, Clone, Debug, FromPrimitive, ToPrimitive, PartialEq)]
68pub enum Square {
69 One = 1,
70 Two = 2,
71 Three = 3,
72 Four = 4,
73 Five = 5,
74 Six = 6,
75 Seven = 7,
76 Eight = 8,
77 Nine = 9,
78 Ten = 10,
79 Eleven = 11,
80 Twelve = 12,
81 Thirteen = 13,
82 Fourteen = 14,
83 Fifteen = 15,
84 Sixteen = 16,
85 Seventeen = 17,
86 Eighteen = 18,
87 Nineteen = 19,
88 Twenty = 20,
89 TwentyOne = 21,
90 TwentyTwo = 22,
91 TwentyThree = 23,
92 TwentyFour = 24,
93 TwentyFive = 25,
94 TwentySix = 26,
95 TwentySeven = 27,
96 TwentyEight = 28,
97 TwentyNine = 29,
98 Thirty = 30,
99 ThirtyOne = 31,
100 ThirtyTwo = 32,
101}
102
103impl Square {
104 pub fn iter() -> impl Iterator<Item=Self> {
114 [
115 Square::One, Square::Two, Square::Three, Square::Four,
116 Square::Five, Square::Six, Square::Seven, Square::Eight,
117 Square::Nine, Square::Ten, Square::Eleven, Square::Twelve,
118 Square::Thirteen, Square::Fourteen, Square::Fifteen, Square::Sixteen,
119 Square::Seventeen, Square::Eighteen, Square::Nineteen, Square::Twenty,
120 Square::TwentyOne, Square::TwentyTwo, Square::TwentyThree, Square::TwentyFour,
121 Square::TwentyFive, Square::TwentySix, Square::TwentySeven, Square::TwentyEight,
122 Square::TwentyNine, Square::Thirty, Square::ThirtyOne, Square::ThirtyTwo
123 ].iter().copied()
124 }
125
126 pub fn to_bitboard(&self) -> MonoBitBoard {
128 let value = match self {
129 Square::One => 0x4000000000000000,
130 Square::Two => 0x1000000000000000,
131 Square::Three => 0x400000000000000,
132 Square::Four => 0x100000000000000,
133 Square::Five => 0x80000000000000,
134 Square::Six => 0x20000000000000,
135 Square::Seven => 0x8000000000000,
136 Square::Eight => 0x2000000000000,
137 Square::Nine => 0x400000000000,
138 Square::Ten => 0x100000000000,
139 Square::Eleven => 0x40000000000,
140 Square::Twelve => 0x10000000000,
141 Square::Thirteen => 0x8000000000,
142 Square::Fourteen => 0x2000000000,
143 Square::Fifteen => 0x800000000,
144 Square::Sixteen => 0x200000000,
145 Square::Seventeen => 0x40000000,
146 Square::Eighteen => 0x10000000,
147 Square::Nineteen => 0x4000000,
148 Square::Twenty => 0x1000000,
149 Square::TwentyOne => 0x800000,
150 Square::TwentyTwo => 0x200000,
151 Square::TwentyThree => 0x80000,
152 Square::TwentyFour => 0x20000,
153 Square::TwentyFive => 0x4000,
154 Square::TwentySix => 0x1000,
155 Square::TwentySeven => 0x400,
156 Square::TwentyEight => 0x100,
157 Square::TwentyNine => 0x80,
158 Square::Thirty => 0x20,
159 Square::ThirtyOne => 0x8,
160 Square::ThirtyTwo => 0x2
161 };
162 MonoBitBoard::new(value).unwrap()
163 }
164
165 pub fn to_number(&self) -> u8 {
175 num::ToPrimitive::to_u8(self).unwrap()
176 }
177}
178
179impl TryFrom<u8> for Square {
180 type Error = NotationError;
181
182 fn try_from(value: u8) -> Result<Self, Self::Error> {
183 num::FromPrimitive::from_u8(value).ok_or(NotationError::OutOfRange)
184 }
185}
186
187impl TryFrom<&str> for Square {
188 type Error = NotationError;
189
190 fn try_from(text: &str) -> Result<Self, Self::Error> {
191 let value = text.parse::<u8>().map_err(|_| NotationError::InvalidFormat)?;
192 Square::try_from(value)
193 }
194}
195
196impl TryFrom<MonoBitBoard> for Square {
197 type Error = SquareConversionError;
198
199 fn try_from(bitboard: MonoBitBoard) -> Result<Self, Self::Error> {
225 Square::iter()
226 .find(|square| square.to_bitboard() == bitboard)
227 .ok_or(SquareConversionError)
228 }
229}
230
231#[derive(Copy, Clone, Debug)]
234pub struct Move(pub Square, pub Square);
235
236impl Move {
237 pub fn new(source: Square, destination: Square) -> Result<Self, NotationError> {
252 match source != destination {
253 true => Ok(Move(source, destination)),
254 false => Err(NotationError::Idle)
255 }
256 }
257
258 pub fn from_notation(text: &str) -> Result<Self, NotationError> {
269 lazy_static! {
270 static ref CN_PATTERN: Regex = Regex::new(r"^([1-9]+[0-9]*)([-xX])([1-9]+[0-9]*)$").unwrap();
271 }
272
273 match CN_PATTERN.captures(text) {
274 Some(captures) => Move::parse_captures(captures),
275 None => Err(NotationError::InvalidFormat)
276 }
277 }
278
279 fn parse_captures(captures: Captures) -> Result<Move, NotationError> {
280 let source_text = captures.get(1).unwrap().as_str();
281 let dest_text = captures.get(3).unwrap().as_str();
282
283 let source = Square::try_from(source_text)?;
284 let dest = Square::try_from(dest_text)?;
285
286 Move::new(source, dest)
287 }
288}
289
290impl TryFrom<&str> for Move {
291 type Error = NotationError;
292
293 fn try_from(text: &str) -> Result<Self, Self::Error> {
304 Move::from_notation(text)
305 }
306}
307
308pub struct MoveIter<'a> {
310 board: &'a Board,
311 player: Player,
312 piece_index: u8,
313}
314
315impl<'a> MoveIter<'a> {
316 pub fn new(board: &'a Board, player: Player) -> Self {
325 MoveIter { board, player, piece_index: 0 }
326 }
327
328 fn get_current_piece(&self) -> Option<MonoBitBoard> {
329 let player_bitboard = self.board.pieces_by_player(self.player);
330 let player_pieces = player_bitboard.pieces();
331 return player_pieces.get(self.piece_index as usize).cloned();
332 }
333}
334
335impl<'a> Iterator for MoveIter<'a> {
336 type Item = Move;
337
338 fn next(&mut self) -> Option<Self::Item> {
339 let current_piece = self.get_current_piece()?;
340 let position = Position::from(current_piece);
341
342 match position {
343 Position::Bottom => {}
344 Position::Left => {}
345 Position::BottomLeft => {}
346 Position::BottomRight => {}
347 Position::Top => {}
348 Position::Right => {}
349 Position::TopRight => {}
350 Position::TopLeft => {}
351 Position::Interior => {}
352 }
353
354 self.piece_index += 1;
355 Some(Move::new(Square::Two, Square::Nine).unwrap())
356 }
357}