1use std::ops::Not;
2
3#[derive(Copy, Clone, Debug, PartialEq, Eq)]
4pub enum Player {
5 One,
6 Two,
7}
8
9impl Not for Player {
10 type Output = Self;
11
12 fn not(self) -> Self::Output {
13 match self {
14 Player::One => Player::Two,
15 Player::Two => Player::One,
16 }
17 }
18}
19
20impl Into<usize> for Player {
21 fn into(self) -> usize {
22 match self {
23 Player::One => 1,
24 Player::Two => 2,
25 }
26 }
27}
28
29pub trait GameState: Clone {
30 type Move: Clone;
31
32 fn next_player(&self) -> Player;
33 fn apply_move(&mut self, mov: Self::Move);
34 fn for_each_move<F: FnMut(Self::Move)>(&self, f: F);
35 fn eval_score(&self, player: Player) -> i32;
36
37 fn can_move(&self) -> bool {
38 let mut can_move = false;
39 self.for_each_move(|_| can_move = true);
40 can_move
41 }
42
43 fn solve(&self, depth: usize) -> Option<Self::Move> {
44 minimax(self, self.next_player(), depth).1
45 }
46}
47
48fn minimax<G: GameState>(game: &G, player: Player, depth: usize) -> (i32, Option<G::Move>) {
49 if depth == 0 {
50 (game.eval_score(player), None)
51 } else {
52 let mut max_mov = None;
53
54 let min = game.next_player() != player;
55 game.for_each_move(|mov| {
56 let mut game = game.clone();
57 game.apply_move(mov.clone());
58 let score = minimax(&game, player, depth - 1).0;
59 if max_mov
60 .as_ref()
61 .map(|(s, _)| (score > *s) ^ min)
62 .unwrap_or(true)
63 {
64 max_mov = Some((score, mov));
65 }
66 });
67
68 max_mov
69 .map(|(score, mov)| (score, Some(mov)))
70 .unwrap_or_else(|| (game.eval_score(player), None))
71 }
72}