competitive/algorithm/
impartial_game.rs1use std::{
2 collections::{HashMap, HashSet},
3 hash::Hash,
4 marker::PhantomData,
5};
6
7type Marker<T> = PhantomData<fn() -> T>;
8
9pub trait ImpartialGame {
10 type State;
11 type Iter: Iterator<Item = Self::State>;
12 fn next_state(&mut self, s: &Self::State) -> Self::Iter;
13}
14
15pub struct ImpartialGamer<S, F, I>
16where
17 F: FnMut(&S) -> I,
18 I: Iterator<Item = S>,
19{
20 f: F,
21 _marker: Marker<(S, I)>,
22}
23
24impl<S, F, I> ImpartialGamer<S, F, I>
25where
26 F: FnMut(&S) -> I,
27 I: Iterator<Item = S>,
28{
29 pub fn new(f: F) -> Self {
30 Self {
31 f,
32 _marker: PhantomData,
33 }
34 }
35}
36
37impl<S, F, I> ImpartialGame for ImpartialGamer<S, F, I>
38where
39 F: FnMut(&S) -> I,
40 I: Iterator<Item = S>,
41{
42 type State = S;
43 type Iter = I;
44 fn next_state(&mut self, s: &Self::State) -> Self::Iter {
45 (self.f)(s)
46 }
47}
48
49#[derive(Debug, Clone)]
50pub struct ImpartialGameAnalyzer<G>
51where
52 G: ImpartialGame,
53 G::State: Eq + Hash,
54{
55 game: G,
56 grundy: HashMap<G::State, u64>,
57}
58
59impl<G> ImpartialGameAnalyzer<G>
60where
61 G: ImpartialGame,
62 G::State: Eq + Hash + Clone,
63{
64 pub fn new(game: G) -> Self {
65 Self {
66 game,
67 grundy: Default::default(),
68 }
69 }
70 pub fn eval(&mut self, s: &G::State) -> u64 {
71 if let Some(g) = self.grundy.get(s).cloned() {
72 g
73 } else {
74 let next: HashSet<_> = self.game.next_state(s).map(|ns| self.eval(&ns)).collect();
75 let mut g = 0u64;
76 while next.contains(&g) {
77 g += 1;
78 }
79 self.grundy.insert(s.clone(), g);
80 g
81 }
82 }
83}