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