1use std::fmt;
2use std::str::FromStr;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
6pub struct Square(u8);
7
8impl Square {
9 pub const COUNT: usize = 64;
10
11 pub const fn new(index: u8) -> Self {
17 assert!(index < 64, "square index must be 0..64");
18 Self(index)
19 }
20
21 pub const fn index(self) -> usize {
22 self.0 as usize
23 }
24
25 pub const fn file(self) -> u8 {
26 self.0 % 8
27 }
28
29 pub const fn rank(self) -> u8 {
30 self.0 / 8
31 }
32
33 pub const fn mirrored(self) -> Self {
34 Self::new(self.rank() * 8 + (7 - self.file()))
35 }
36
37 pub fn all() -> impl Iterator<Item = Self> {
38 (0..64).map(Self)
39 }
40}
41
42impl fmt::Display for Square {
43 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44 write!(
45 f,
46 "{}{}",
47 char::from(b'a' + self.file()),
48 char::from(b'1' + self.rank())
49 )
50 }
51}
52
53impl FromStr for Square {
54 type Err = &'static str;
55
56 fn from_str(value: &str) -> Result<Self, Self::Err> {
57 let bytes = value.as_bytes();
58 if bytes.len() != 2
59 || !(b'a'..=b'h').contains(&bytes[0])
60 || !(b'1'..=b'8').contains(&bytes[1])
61 {
62 return Err("square must be a1..h8");
63 }
64 Ok(Self::new((bytes[1] - b'1') * 8 + (bytes[0] - b'a')))
65 }
66}
67
68#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
70pub enum Move {
71 Place(Square),
72 Pass,
73}
74
75impl Move {
76 pub const fn square(self) -> Option<Square> {
77 match self {
78 Self::Place(square) => Some(square),
79 Self::Pass => None,
80 }
81 }
82
83 pub const fn mirrored(self) -> Self {
84 match self {
85 Self::Place(square) => Self::Place(square.mirrored()),
86 Self::Pass => Self::Pass,
87 }
88 }
89}
90
91impl fmt::Display for Move {
92 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93 match self {
94 Self::Place(square) => square.fmt(f),
95 Self::Pass => f.write_str("pass"),
96 }
97 }
98}
99
100impl FromStr for Move {
101 type Err = &'static str;
102
103 fn from_str(value: &str) -> Result<Self, Self::Err> {
104 if value.eq_ignore_ascii_case("pass") {
105 Ok(Self::Pass)
106 } else {
107 value.parse().map(Self::Place)
108 }
109 }
110}
111
112#[derive(Debug, Clone, Copy, PartialEq, Eq)]
114pub struct MoveList {
115 moves: [Move; Square::COUNT],
116 len: u8,
117}
118
119impl Default for MoveList {
120 fn default() -> Self {
121 Self {
122 moves: [Move::Pass; Square::COUNT],
123 len: 0,
124 }
125 }
126}
127
128impl MoveList {
129 pub const fn len(self) -> usize {
130 self.len as usize
131 }
132
133 pub const fn is_empty(self) -> bool {
134 self.len == 0
135 }
136
137 pub fn as_slice(&self) -> &[Move] {
138 &self.moves[..self.len()]
139 }
140
141 pub(crate) fn push(&mut self, mv: Move) {
142 debug_assert!(self.len() < self.moves.len());
143 self.moves[self.len()] = mv;
144 self.len += 1;
145 }
146}
147
148impl IntoIterator for MoveList {
149 type Item = Move;
150 type IntoIter = std::iter::Take<std::array::IntoIter<Move, { Square::COUNT }>>;
151
152 fn into_iter(self) -> Self::IntoIter {
153 self.moves.into_iter().take(self.len())
154 }
155}