1use std::fmt::{Debug, Display, Formatter};
2use std::hash::Hash;
3use std::marker::PhantomData;
4use std::ops::ControlFlow;
5
6use internal_iterator::InternalIterator;
7use rand::Rng;
8
9use crate::symmetry::Symmetry;
10
11#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
15pub enum Player {
16 A,
17 B,
18}
19
20#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
22pub enum Outcome {
23 WonBy(Player),
24 Draw,
25}
26
27#[derive(Debug, Copy, Clone, Eq, PartialEq)]
29pub struct BoardDone;
30
31#[derive(Debug, Copy, Clone, Eq, PartialEq)]
33pub enum PlayError {
34 BoardDone,
35 UnavailableMove,
36}
37
38pub trait Board: 'static + Debug + Display + Clone + Eq + Send + Sync + BoardSymmetry<Self>
47where
48 for<'a> Self: BoardMoves<'a, Self>,
49{
50 type Move: Debug + Display + Eq + Copy + Send + Sync;
52
53 fn next_player(&self) -> Player;
56
57 fn is_available_move(&self, mv: Self::Move) -> Result<bool, BoardDone>;
59
60 fn random_available_move(&self, rng: &mut impl Rng) -> Result<Self::Move, BoardDone> {
63 let count = self.available_moves()?.count();
64 let index = rng.gen_range(0..count);
65 Ok(self.available_moves()?.nth(index).unwrap())
68 }
69
70 fn play(&mut self, mv: Self::Move) -> Result<(), PlayError>;
73
74 fn clone_and_play(&self, mv: Self::Move) -> Result<Self, PlayError> {
77 let mut next = self.clone();
78 next.play(mv)?;
79 Ok(next)
80 }
81
82 fn outcome(&self) -> Option<Outcome>;
84
85 fn is_done(&self) -> bool {
87 self.outcome().is_some()
88 }
89
90 fn can_lose_after_move() -> bool;
94
95 fn check_done(&self) -> Result<(), BoardDone> {
99 match self.is_done() {
100 true => Err(BoardDone),
101 false => Ok(()),
102 }
103 }
104
105 fn check_can_play(&self, mv: Self::Move) -> Result<(), PlayError> {
110 match self.is_available_move(mv)? {
111 true => Ok(()),
112 false => Err(PlayError::UnavailableMove),
113 }
114 }
115
116 fn play_random_available_move(&mut self, rng: &mut impl Rng) -> Result<(), BoardDone> {
119 self.play(self.random_available_move(rng)?).unwrap();
120 Ok(())
121 }
122
123 fn children(&self) -> Result<BoardChildrenIterator<Self>, BoardDone> {
126 BoardChildrenIterator::new(self)
127 }
128}
129
130pub trait Alternating {}
132
133pub trait AltBoard: Board + Alternating {}
135
136impl<B: Board + Alternating> AltBoard for B {}
137
138pub trait BoardMoves<'a, B: Board> {
141 type AllMovesIterator: InternalIterator<Item = B::Move> + Clone;
142 type AvailableMovesIterator: InternalIterator<Item = B::Move> + Clone;
143
144 fn all_possible_moves() -> Self::AllMovesIterator;
148
149 fn available_moves(&'a self) -> Result<Self::AvailableMovesIterator, BoardDone>;
153}
154
155#[macro_export]
157macro_rules! impl_unit_symmetry_board {
158 ($B:ty) => {
159 impl $crate::board::BoardSymmetry<$B> for $B {
160 type Symmetry = $crate::symmetry::UnitSymmetry;
161 type CanonicalKey = ();
162
163 fn map(&self, _: Self::Symmetry) -> Self {
164 self.clone()
165 }
166
167 fn map_move(
168 &self,
169 _: Self::Symmetry,
170 mv: <$B as $crate::board::Board>::Move,
171 ) -> <$B as $crate::board::Board>::Move {
172 mv
173 }
174
175 fn canonical_key(&self) -> Self::CanonicalKey {}
176 }
177 };
178}
179
180pub trait BoardSymmetry<B: Board>: Sized {
184 type Symmetry: Symmetry;
186
187 type CanonicalKey: Ord;
189
190 fn map(&self, sym: Self::Symmetry) -> Self;
192
193 fn map_move(&self, sym: Self::Symmetry, mv: B::Move) -> B::Move;
195
196 fn canonical_key(&self) -> Self::CanonicalKey;
199
200 fn canonicalize(&self) -> Self {
208 Self::Symmetry::all()
209 .iter()
210 .map(|&sym| self.map(sym))
211 .min_by_key(|cand| cand.canonical_key())
212 .unwrap()
213 }
214}
215
216impl Player {
217 pub const BOTH: [Player; 2] = [Player::A, Player::B];
218
219 pub fn other(self) -> Player {
220 match self {
221 Player::A => Player::B,
222 Player::B => Player::A,
223 }
224 }
225
226 pub fn index(self) -> u8 {
227 match self {
228 Player::A => 0,
229 Player::B => 1,
230 }
231 }
232
233 pub fn to_char(self) -> char {
234 match self {
235 Player::A => 'A',
236 Player::B => 'B',
237 }
238 }
239
240 pub fn sign<V: num_traits::One + std::ops::Neg<Output = V>>(self, pov: Player) -> V {
241 if self == pov {
242 V::one()
243 } else {
244 -V::one()
245 }
246 }
247}
248
249#[derive(Debug, Clone)]
251pub struct AllMovesIterator<B: Board>(PhantomData<B>);
252
253impl<B: Board> Default for AllMovesIterator<B> {
254 fn default() -> Self {
255 AllMovesIterator(PhantomData)
256 }
257}
258
259#[derive(Debug, Clone)]
261pub struct AvailableMovesIterator<'a, B: Board> {
262 board: &'a B,
263}
264
265#[derive(Debug, Clone)]
269pub struct BruteforceMoveIterator<'a, B: Board> {
270 board: &'a B,
271}
272
273impl<'a, B: Board> AvailableMovesIterator<'a, B> {
274 pub fn new(board: &'a B) -> Result<Self, BoardDone> {
275 board.check_done()?;
276 Ok(AvailableMovesIterator { board })
277 }
278
279 pub fn board(&self) -> &'a B {
280 self.board
281 }
282}
283
284impl<'a, B: Board> BruteforceMoveIterator<'a, B> {
285 pub fn new(board: &'a B) -> Result<Self, BoardDone> {
286 board.check_done()?;
287 Ok(BruteforceMoveIterator { board })
288 }
289}
290
291impl<'a, B: Board> InternalIterator for BruteforceMoveIterator<'a, B> {
292 type Item = B::Move;
293
294 fn try_for_each<R, F>(self, mut f: F) -> ControlFlow<R>
295 where
296 F: FnMut(Self::Item) -> ControlFlow<R>,
297 {
298 B::all_possible_moves().try_for_each(|mv: B::Move| {
299 if self.board.is_available_move(mv).unwrap() {
301 f(mv)
302 } else {
303 ControlFlow::Continue(())
304 }
305 })
306 }
307}
308
309pub struct BoardChildrenIterator<'a, B: Board> {
310 board: &'a B,
311 available: <B as BoardMoves<'a, B>>::AvailableMovesIterator,
312}
313
314impl<'a, B: Board> BoardChildrenIterator<'a, B> {
315 pub fn new(board: &'a B) -> Result<Self, BoardDone> {
316 let available = board.available_moves()?;
317 Ok(BoardChildrenIterator { board, available })
318 }
319}
320
321impl<'a, B: Board> InternalIterator for BoardChildrenIterator<'a, B> {
322 type Item = (B::Move, B);
323
324 fn try_for_each<R, F>(self, f: F) -> ControlFlow<R>
325 where
326 F: FnMut(Self::Item) -> ControlFlow<R>,
327 {
328 let board = self.board;
329 self.available
330 .map(|mv: B::Move| (mv, board.clone_and_play(mv).unwrap()))
331 .try_for_each(f)
332 }
333}
334
335impl<'a, B: Board> Debug for BoardChildrenIterator<'a, B> {
336 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
337 f.debug_struct("BoardChildrenIterator")
338 .field("board", self.board)
339 .finish_non_exhaustive()
340 }
341}
342
343impl<'a, B: Board> Clone for BoardChildrenIterator<'a, B>
344where
345 <B as BoardMoves<'a, B>>::AvailableMovesIterator: Clone,
346{
347 fn clone(&self) -> Self {
348 BoardChildrenIterator {
349 board: self.board,
350 available: self.available.clone(),
351 }
352 }
353}
354
355impl From<BoardDone> for PlayError {
356 fn from(_: BoardDone) -> Self {
357 PlayError::BoardDone
358 }
359}