1use std::fmt;
2use std::str::FromStr;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
6pub struct Column(u8);
7
8impl Column {
9 pub const COUNT: usize = 7;
10
11 pub const fn new(index: u8) -> Self {
17 assert!(index < Self::COUNT as u8, "column index must be 0..7");
18 Self(index)
19 }
20
21 pub const fn index(self) -> usize {
22 self.0 as usize
23 }
24
25 pub const fn mirrored(self) -> Self {
26 Self::new(Self::COUNT as u8 - 1 - self.0)
27 }
28
29 pub fn all() -> impl Iterator<Item = Self> {
30 (0..Self::COUNT as u8).map(Self)
31 }
32}
33
34impl fmt::Display for Column {
35 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36 write!(f, "{}", char::from(b'a' + self.0))
37 }
38}
39
40impl FromStr for Column {
41 type Err = &'static str;
42
43 fn from_str(value: &str) -> Result<Self, Self::Err> {
44 let bytes = value.as_bytes();
45 if bytes.len() != 1 {
46 return Err("column must be a..g or 1..7");
47 }
48 match bytes[0] {
49 b'a'..=b'g' => Ok(Self::new(bytes[0] - b'a')),
50 b'A'..=b'G' => Ok(Self::new(bytes[0] - b'A')),
51 b'1'..=b'7' => Ok(Self::new(bytes[0] - b'1')),
52 _ => Err("column must be a..g or 1..7"),
53 }
54 }
55}
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
59pub struct Move(Column);
60
61impl Move {
62 pub const fn new(column: Column) -> Self {
63 Self(column)
64 }
65
66 pub const fn column(self) -> Column {
67 self.0
68 }
69
70 pub const fn mirrored(self) -> Self {
71 Self(self.0.mirrored())
72 }
73}
74
75impl fmt::Display for Move {
76 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77 self.0.fmt(f)
78 }
79}
80
81impl FromStr for Move {
82 type Err = &'static str;
83
84 fn from_str(value: &str) -> Result<Self, Self::Err> {
85 value.parse().map(Self)
86 }
87}
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
91pub struct Cell {
92 column: Column,
93 row: u8,
94}
95
96impl Cell {
97 pub const ROWS: usize = 6;
98
99 pub const fn new(column: Column, row: u8) -> Self {
105 assert!(row < Self::ROWS as u8, "row index must be 0..6");
106 Self { column, row }
107 }
108
109 pub const fn column(self) -> Column {
110 self.column
111 }
112
113 pub const fn row(self) -> usize {
114 self.row as usize
115 }
116
117 pub const fn bit_index(self) -> usize {
118 self.column.index() * 7 + self.row()
119 }
120
121 pub const fn mirrored(self) -> Self {
122 Self::new(self.column.mirrored(), self.row)
123 }
124}
125
126impl fmt::Display for Cell {
127 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128 write!(f, "{}{}", self.column, self.row + 1)
129 }
130}
131
132#[derive(Debug, Clone, Copy, PartialEq, Eq)]
134pub struct MoveList {
135 moves: [Move; Column::COUNT],
136 len: u8,
137}
138
139impl Default for MoveList {
140 fn default() -> Self {
141 Self {
142 moves: [Move::new(Column::new(0)); Column::COUNT],
143 len: 0,
144 }
145 }
146}
147
148impl MoveList {
149 pub const fn len(self) -> usize {
150 self.len as usize
151 }
152
153 pub const fn is_empty(self) -> bool {
154 self.len == 0
155 }
156
157 pub fn as_slice(&self) -> &[Move] {
158 &self.moves[..self.len()]
159 }
160
161 pub(crate) fn push(&mut self, mv: Move) {
162 debug_assert!(self.len() < Column::COUNT);
163 self.moves[self.len()] = mv;
164 self.len += 1;
165 }
166}
167
168impl IntoIterator for MoveList {
169 type Item = Move;
170 type IntoIter = std::iter::Take<std::array::IntoIter<Move, { Column::COUNT }>>;
171
172 fn into_iter(self) -> Self::IntoIter {
173 self.moves.into_iter().take(self.len())
174 }
175}