Skip to main content

Impartial

Struct Impartial 

Source
pub struct Impartial<S, F> {
    moves: F,
    grundy: HashMap<S, u64>,
    outcomes: [HashMap<S, bool>; 2],
    sums: HashMap<Vec<S>, bool>,
    sum_ids: HashMap<S, usize>,
}

Fields§

§moves: F§grundy: HashMap<S, u64>§outcomes: [HashMap<S, bool>; 2]§sums: HashMap<Vec<S>, bool>§sum_ids: HashMap<S, usize>

Implementations§

Source§

impl<S, L, F, I> Impartial<S, F>
where S: Clone + Eq + Hash, F: FnMut(&S) -> I, I: IntoIterator<Item = Neighbor<S, L>>,

Source

fn grundy(&mut self, state: &S) -> u64

Examples found in repository?
crates/competitive/src/algorithm/game_analyzer/impartial.rs (line 79)
78    pub fn value(&mut self) -> GameAnalysis<'_, Impartial<S, F>, S, GameValue> {
79        GameAnalysis::new(&mut self.game, |g, s| GameValue::nimber(g.grundy(s)))
80    }
81
82    pub fn outcome(&mut self) -> GameAnalysis<'_, Impartial<S, F>, S, GameOutcome> {
83        GameAnalysis::new(&mut self.game, |g, s| g.outcome(s, false))
84    }
85
86    /// `combine` receives each move's label and the value of its destination.
87    #[allow(clippy::type_complexity)] // The query owns the fold and its cache.
88    pub fn fold<V, C>(
89        &mut self,
90        mut combine: C,
91    ) -> GameAnalysis<'_, Impartial<S, F>, S, V, false, impl FnMut(&mut Impartial<S, F>, &S) -> V>
92    where
93        V: Clone,
94        C: FnMut(&S, &[(L, V)]) -> V,
95    {
96        let mut cache = HashMap::new();
97        GameAnalysis::new(
98            &mut self.game,
99            move |game: &mut Impartial<S, F>, state: &S| {
100                memoized_game(
101                    state,
102                    &mut cache,
103                    &mut HashSet::new(),
104                    &mut |s| {
105                        ControlFlow::Continue((
106                            (game.moves)(s).into_iter().map(|n| (n.to, n.label)),
107                            Vec::new(),
108                        ))
109                    },
110                    &mut |_, moves| moves.next(),
111                    &mut |values, label, value| {
112                        values.push((label, value));
113                        ControlFlow::Continue(())
114                    },
115                    &mut |s, values| combine(s, &values),
116                )
117            },
118        )
119    }
120}
121
122impl<'a, S, L, F, I> Misere<'a, Impartial<S, F>>
123where
124    S: Clone + Eq + Hash,
125    F: FnMut(&S) -> I,
126    I: IntoIterator<Item = Neighbor<S, L>>,
127{
128    pub fn outcome(self) -> GameAnalysis<'a, Impartial<S, F>, S, GameOutcome, true> {
129        GameAnalysis::new(self.game, |g, s| g.outcome(s, true))
130    }
131}
132impl<'a, S, L, F, I> GameSum<'a, Impartial<S, F>>
133where
134    S: Clone + Eq + Hash,
135    F: FnMut(&S) -> I,
136    I: IntoIterator<Item = Neighbor<S, L>>,
137{
138    pub fn grundy(self) -> GameAnalysis<'a, Impartial<S, F>, [S], u64> {
139        GameAnalysis::new(self.game, Impartial::sum_grundy)
140    }
141}
142impl<'a, S, L, F, I, const MISERE: bool> GameSum<'a, Impartial<S, F>, MISERE>
143where
144    S: Clone + Eq + Hash,
145    F: FnMut(&S) -> I,
146    I: IntoIterator<Item = Neighbor<S, L>>,
147{
148    pub fn outcome(self) -> GameAnalysis<'a, Impartial<S, F>, [S], GameOutcome, MISERE> {
149        GameAnalysis::new(self.game, |g, s| g.sum_outcome(s, MISERE))
150    }
151}
152
153impl<S, L, F, I, V, const MISERE: bool, E> GameAnalysis<'_, Impartial<S, F>, S, V, MISERE, E>
154where
155    F: FnMut(&S) -> I,
156    I: IntoIterator<Item = Neighbor<S, L>>,
157    E: FnMut(&mut Impartial<S, F>, &S) -> V,
158{
159    pub fn moves(&mut self, state: &S) -> Vec<EvaluatedMove<S, L, V>> {
160        (self.game.moves)(state)
161            .into_iter()
162            .map(|next| {
163                let value = self.eval(&next.to);
164                EvaluatedMove { next, value }
165            })
166            .collect()
167    }
168}
169
170impl<S, L, F, I> GameAnalysis<'_, Impartial<S, F>, S, u64>
171where
172    S: Clone + Eq + Hash,
173    F: FnMut(&S) -> I,
174    I: IntoIterator<Item = Neighbor<S, L>>,
175{
176    pub fn winning_moves(&mut self, state: &S) -> Vec<Neighbor<S, L>> {
177        self.game.winning_moves(state, false).collect()
178    }
179}
180impl<S, L, F, I> GameAnalysis<'_, Impartial<S, F>, S, GameValue>
181where
182    S: Clone + Eq + Hash,
183    F: FnMut(&S) -> I,
184    I: IntoIterator<Item = Neighbor<S, L>>,
185{
186    pub fn winning_moves(&mut self, state: &S) -> Vec<Neighbor<S, L>> {
187        self.game.winning_moves(state, false).collect()
188    }
189}
190impl<S, L, F, I, const MISERE: bool> GameAnalysis<'_, Impartial<S, F>, S, GameOutcome, MISERE>
191where
192    S: Clone + Eq + Hash,
193    F: FnMut(&S) -> I,
194    I: IntoIterator<Item = Neighbor<S, L>>,
195{
196    pub fn winning_moves(&mut self, state: &S) -> Vec<Neighbor<S, L>> {
197        self.game.winning_moves(state, MISERE).collect()
198    }
199}
200
201#[allow(clippy::type_complexity)] // Sum states and labels both carry component information.
202impl<S, L, F, I, V, const MISERE: bool, E> GameAnalysis<'_, Impartial<S, F>, [S], V, MISERE, E>
203where
204    S: Clone,
205    F: FnMut(&S) -> I,
206    I: IntoIterator<Item = Neighbor<S, L>>,
207    E: FnMut(&mut Impartial<S, F>, &[S]) -> V,
208{
209    pub fn moves(&mut self, state: &[S]) -> Vec<EvaluatedMove<Vec<S>, (usize, L), V>> {
210        let mut result = Vec::new();
211        for (i, component) in state.iter().enumerate() {
212            for next in (self.game.moves)(component) {
213                let mut to = state.to_vec();
214                to[i] = next.to;
215                let value = self.eval(&to);
216                result.push(EvaluatedMove {
217                    next: Neighbor::new(to, (i, next.label)),
218                    value,
219                });
220            }
221        }
222        result
223    }
224}
225#[allow(clippy::type_complexity)] // Sum states and labels both carry component information.
226impl<S, L, F, I> GameAnalysis<'_, Impartial<S, F>, [S], u64>
227where
228    S: Clone + Eq + Hash,
229    F: FnMut(&S) -> I,
230    I: IntoIterator<Item = Neighbor<S, L>>,
231{
232    pub fn winning_moves(&mut self, state: &[S]) -> Vec<Neighbor<Vec<S>, (usize, L)>> {
233        self.game.sum_winning_moves(state)
234    }
235}
236#[allow(clippy::type_complexity)] // Sum states and labels both carry component information.
237impl<S, L, F, I, const MISERE: bool> GameAnalysis<'_, Impartial<S, F>, [S], GameOutcome, MISERE>
238where
239    S: Clone + Eq + Hash,
240    F: FnMut(&S) -> I,
241    I: IntoIterator<Item = Neighbor<S, L>>,
242{
243    pub fn winning_moves(&mut self, state: &[S]) -> Vec<Neighbor<Vec<S>, (usize, L)>> {
244        if let [single] = state {
245            return self
246                .game
247                .winning_moves(single, MISERE)
248                .map(|next| Neighbor::new(vec![next.to], (0, next.label)))
249                .collect();
250        }
251        if !MISERE {
252            return self.game.sum_winning_moves(state);
253        }
254        let mut result = Vec::new();
255        for (i, component) in state.iter().enumerate() {
256            for next in (self.game.moves)(component) {
257                let mut to = state.to_vec();
258                to[i] = next.to;
259                if self.eval(&to) == GameOutcome::Lose {
260                    result.push(Neighbor::new(to, (i, next.label)));
261                }
262            }
263        }
264        result
265    }
266}
267
268impl<S, L, F, I> Impartial<S, F>
269where
270    S: Clone + Eq + Hash,
271    F: FnMut(&S) -> I,
272    I: IntoIterator<Item = Neighbor<S, L>>,
273{
274    fn grundy(&mut self, state: &S) -> u64 {
275        memoized_game(
276            state,
277            &mut self.grundy,
278            &mut HashSet::new(),
279            &mut |s| {
280                if self.outcomes[0].get(s) == Some(&false) {
281                    return ControlFlow::Break(0);
282                }
283                let moves = (self.moves)(s).into_iter().map(|n| (n.to, ()));
284                ControlFlow::Continue((moves, Vec::new()))
285            },
286            &mut |_, moves| moves.next(),
287            &mut |values, (), value| {
288                values.push(value);
289                ControlFlow::Continue(())
290            },
291            &mut |_, values| game_mex(values),
292        )
293    }
294
295    fn outcome(&mut self, state: &S, misere: bool) -> GameOutcome {
296        let win = memoized_wins(
297            state,
298            &mut self.outcomes[usize::from(misere)],
299            |s| {
300                if !misere && let Some(&value) = self.grundy.get(s) {
301                    return ControlFlow::Break(value != 0);
302                }
303                let moves = (self.moves)(s).into_iter().map(|n| n.to);
304                ControlFlow::Continue(moves)
305            },
306            |_, moves| moves.next(),
307            misere,
308        );
309        GameOutcome::from_win(win)
310    }
311
312    fn winning_moves(&mut self, state: &S, misere: bool) -> impl Iterator<Item = Neighbor<S, L>> {
313        (self.moves)(state).into_iter().filter(move |next| {
314            if !misere && let Some(&value) = self.grundy.get(&next.to) {
315                value == 0
316            } else {
317                self.outcome(&next.to, misere) == GameOutcome::Lose
318            }
319        })
320    }
321
322    fn sum_grundy(&mut self, state: &[S]) -> u64 {
323        match state {
324            [] => return 0,
325            [single] => return self.grundy(single),
326            _ => {}
327        }
328        let (xor, odd) = self.reduce_sum(state);
329        odd.into_iter().fold(xor, |value, s| value ^ self.grundy(s))
330    }
331
332    #[allow(clippy::type_complexity)] // Sum states and labels both carry component information.
333    fn sum_winning_moves(&mut self, state: &[S]) -> Vec<Neighbor<Vec<S>, (usize, L)>> {
334        let xor = self.sum_grundy(state);
335        let mut result = Vec::new();
336        if xor == 0 {
337            return result;
338        }
339        for (i, component) in state.iter().enumerate() {
340            let target = xor ^ self.grundy(component);
341            for next in (self.moves)(component) {
342                if self.grundy(&next.to) == target {
343                    let mut to = state.to_vec();
344                    to[i] = next.to;
345                    result.push(Neighbor::new(to, (i, next.label)));
346                }
347            }
348        }
349        result
350    }
351
352    fn sum_outcome(&mut self, state: &[S], misere: bool) -> GameOutcome {
353        if state.is_empty() {
354            return GameOutcome::from_win(misere);
355        }
356        if let [single] = state {
357            return self.outcome(single, misere);
358        }
359        let win = if misere {
360            if let Some(&win) = self.sums.get(state) {
361                win
362            } else {
363                memoized_wins(
364                    &canonical_sum(state.to_vec(), &mut self.sum_ids),
365                    &mut self.sums,
366                    |_| ControlFlow::Continue((0, None)),
367                    |s, (i, moves)| {
368                        while *i < s.len() {
369                            if *i != 0 && s[*i - 1] == s[*i] {
370                                *i += 1;
371                                continue;
372                            }
373                            let iterator =
374                                moves.get_or_insert_with(|| (self.moves)(&s[*i]).into_iter());
375                            if let Some(next) = iterator.next() {
376                                let mut to = s.clone();
377                                to[*i] = next.to;
378                                return Some(canonical_sum(to, &mut self.sum_ids));
379                            }
380                            *i += 1;
381                            *moves = None;
382                        }
383                        None
384                    },
385                    true,
386                )
387            }
388        } else {
389            let (xor, odd) = self.reduce_sum(state);
390            if xor == 0 && odd.len() == 1 {
391                return self.outcome(odd.into_iter().next().unwrap(), false);
392            }
393            odd.into_iter().fold(xor, |value, s| value ^ self.grundy(s)) != 0
394        };
395        GameOutcome::from_win(win)
396    }
Source

fn outcome(&mut self, state: &S, misere: bool) -> GameOutcome

Examples found in repository?
crates/competitive/src/algorithm/game_analyzer/impartial.rs (line 83)
82    pub fn outcome(&mut self) -> GameAnalysis<'_, Impartial<S, F>, S, GameOutcome> {
83        GameAnalysis::new(&mut self.game, |g, s| g.outcome(s, false))
84    }
85
86    /// `combine` receives each move's label and the value of its destination.
87    #[allow(clippy::type_complexity)] // The query owns the fold and its cache.
88    pub fn fold<V, C>(
89        &mut self,
90        mut combine: C,
91    ) -> GameAnalysis<'_, Impartial<S, F>, S, V, false, impl FnMut(&mut Impartial<S, F>, &S) -> V>
92    where
93        V: Clone,
94        C: FnMut(&S, &[(L, V)]) -> V,
95    {
96        let mut cache = HashMap::new();
97        GameAnalysis::new(
98            &mut self.game,
99            move |game: &mut Impartial<S, F>, state: &S| {
100                memoized_game(
101                    state,
102                    &mut cache,
103                    &mut HashSet::new(),
104                    &mut |s| {
105                        ControlFlow::Continue((
106                            (game.moves)(s).into_iter().map(|n| (n.to, n.label)),
107                            Vec::new(),
108                        ))
109                    },
110                    &mut |_, moves| moves.next(),
111                    &mut |values, label, value| {
112                        values.push((label, value));
113                        ControlFlow::Continue(())
114                    },
115                    &mut |s, values| combine(s, &values),
116                )
117            },
118        )
119    }
120}
121
122impl<'a, S, L, F, I> Misere<'a, Impartial<S, F>>
123where
124    S: Clone + Eq + Hash,
125    F: FnMut(&S) -> I,
126    I: IntoIterator<Item = Neighbor<S, L>>,
127{
128    pub fn outcome(self) -> GameAnalysis<'a, Impartial<S, F>, S, GameOutcome, true> {
129        GameAnalysis::new(self.game, |g, s| g.outcome(s, true))
130    }
131}
132impl<'a, S, L, F, I> GameSum<'a, Impartial<S, F>>
133where
134    S: Clone + Eq + Hash,
135    F: FnMut(&S) -> I,
136    I: IntoIterator<Item = Neighbor<S, L>>,
137{
138    pub fn grundy(self) -> GameAnalysis<'a, Impartial<S, F>, [S], u64> {
139        GameAnalysis::new(self.game, Impartial::sum_grundy)
140    }
141}
142impl<'a, S, L, F, I, const MISERE: bool> GameSum<'a, Impartial<S, F>, MISERE>
143where
144    S: Clone + Eq + Hash,
145    F: FnMut(&S) -> I,
146    I: IntoIterator<Item = Neighbor<S, L>>,
147{
148    pub fn outcome(self) -> GameAnalysis<'a, Impartial<S, F>, [S], GameOutcome, MISERE> {
149        GameAnalysis::new(self.game, |g, s| g.sum_outcome(s, MISERE))
150    }
151}
152
153impl<S, L, F, I, V, const MISERE: bool, E> GameAnalysis<'_, Impartial<S, F>, S, V, MISERE, E>
154where
155    F: FnMut(&S) -> I,
156    I: IntoIterator<Item = Neighbor<S, L>>,
157    E: FnMut(&mut Impartial<S, F>, &S) -> V,
158{
159    pub fn moves(&mut self, state: &S) -> Vec<EvaluatedMove<S, L, V>> {
160        (self.game.moves)(state)
161            .into_iter()
162            .map(|next| {
163                let value = self.eval(&next.to);
164                EvaluatedMove { next, value }
165            })
166            .collect()
167    }
168}
169
170impl<S, L, F, I> GameAnalysis<'_, Impartial<S, F>, S, u64>
171where
172    S: Clone + Eq + Hash,
173    F: FnMut(&S) -> I,
174    I: IntoIterator<Item = Neighbor<S, L>>,
175{
176    pub fn winning_moves(&mut self, state: &S) -> Vec<Neighbor<S, L>> {
177        self.game.winning_moves(state, false).collect()
178    }
179}
180impl<S, L, F, I> GameAnalysis<'_, Impartial<S, F>, S, GameValue>
181where
182    S: Clone + Eq + Hash,
183    F: FnMut(&S) -> I,
184    I: IntoIterator<Item = Neighbor<S, L>>,
185{
186    pub fn winning_moves(&mut self, state: &S) -> Vec<Neighbor<S, L>> {
187        self.game.winning_moves(state, false).collect()
188    }
189}
190impl<S, L, F, I, const MISERE: bool> GameAnalysis<'_, Impartial<S, F>, S, GameOutcome, MISERE>
191where
192    S: Clone + Eq + Hash,
193    F: FnMut(&S) -> I,
194    I: IntoIterator<Item = Neighbor<S, L>>,
195{
196    pub fn winning_moves(&mut self, state: &S) -> Vec<Neighbor<S, L>> {
197        self.game.winning_moves(state, MISERE).collect()
198    }
199}
200
201#[allow(clippy::type_complexity)] // Sum states and labels both carry component information.
202impl<S, L, F, I, V, const MISERE: bool, E> GameAnalysis<'_, Impartial<S, F>, [S], V, MISERE, E>
203where
204    S: Clone,
205    F: FnMut(&S) -> I,
206    I: IntoIterator<Item = Neighbor<S, L>>,
207    E: FnMut(&mut Impartial<S, F>, &[S]) -> V,
208{
209    pub fn moves(&mut self, state: &[S]) -> Vec<EvaluatedMove<Vec<S>, (usize, L), V>> {
210        let mut result = Vec::new();
211        for (i, component) in state.iter().enumerate() {
212            for next in (self.game.moves)(component) {
213                let mut to = state.to_vec();
214                to[i] = next.to;
215                let value = self.eval(&to);
216                result.push(EvaluatedMove {
217                    next: Neighbor::new(to, (i, next.label)),
218                    value,
219                });
220            }
221        }
222        result
223    }
224}
225#[allow(clippy::type_complexity)] // Sum states and labels both carry component information.
226impl<S, L, F, I> GameAnalysis<'_, Impartial<S, F>, [S], u64>
227where
228    S: Clone + Eq + Hash,
229    F: FnMut(&S) -> I,
230    I: IntoIterator<Item = Neighbor<S, L>>,
231{
232    pub fn winning_moves(&mut self, state: &[S]) -> Vec<Neighbor<Vec<S>, (usize, L)>> {
233        self.game.sum_winning_moves(state)
234    }
235}
236#[allow(clippy::type_complexity)] // Sum states and labels both carry component information.
237impl<S, L, F, I, const MISERE: bool> GameAnalysis<'_, Impartial<S, F>, [S], GameOutcome, MISERE>
238where
239    S: Clone + Eq + Hash,
240    F: FnMut(&S) -> I,
241    I: IntoIterator<Item = Neighbor<S, L>>,
242{
243    pub fn winning_moves(&mut self, state: &[S]) -> Vec<Neighbor<Vec<S>, (usize, L)>> {
244        if let [single] = state {
245            return self
246                .game
247                .winning_moves(single, MISERE)
248                .map(|next| Neighbor::new(vec![next.to], (0, next.label)))
249                .collect();
250        }
251        if !MISERE {
252            return self.game.sum_winning_moves(state);
253        }
254        let mut result = Vec::new();
255        for (i, component) in state.iter().enumerate() {
256            for next in (self.game.moves)(component) {
257                let mut to = state.to_vec();
258                to[i] = next.to;
259                if self.eval(&to) == GameOutcome::Lose {
260                    result.push(Neighbor::new(to, (i, next.label)));
261                }
262            }
263        }
264        result
265    }
266}
267
268impl<S, L, F, I> Impartial<S, F>
269where
270    S: Clone + Eq + Hash,
271    F: FnMut(&S) -> I,
272    I: IntoIterator<Item = Neighbor<S, L>>,
273{
274    fn grundy(&mut self, state: &S) -> u64 {
275        memoized_game(
276            state,
277            &mut self.grundy,
278            &mut HashSet::new(),
279            &mut |s| {
280                if self.outcomes[0].get(s) == Some(&false) {
281                    return ControlFlow::Break(0);
282                }
283                let moves = (self.moves)(s).into_iter().map(|n| (n.to, ()));
284                ControlFlow::Continue((moves, Vec::new()))
285            },
286            &mut |_, moves| moves.next(),
287            &mut |values, (), value| {
288                values.push(value);
289                ControlFlow::Continue(())
290            },
291            &mut |_, values| game_mex(values),
292        )
293    }
294
295    fn outcome(&mut self, state: &S, misere: bool) -> GameOutcome {
296        let win = memoized_wins(
297            state,
298            &mut self.outcomes[usize::from(misere)],
299            |s| {
300                if !misere && let Some(&value) = self.grundy.get(s) {
301                    return ControlFlow::Break(value != 0);
302                }
303                let moves = (self.moves)(s).into_iter().map(|n| n.to);
304                ControlFlow::Continue(moves)
305            },
306            |_, moves| moves.next(),
307            misere,
308        );
309        GameOutcome::from_win(win)
310    }
311
312    fn winning_moves(&mut self, state: &S, misere: bool) -> impl Iterator<Item = Neighbor<S, L>> {
313        (self.moves)(state).into_iter().filter(move |next| {
314            if !misere && let Some(&value) = self.grundy.get(&next.to) {
315                value == 0
316            } else {
317                self.outcome(&next.to, misere) == GameOutcome::Lose
318            }
319        })
320    }
321
322    fn sum_grundy(&mut self, state: &[S]) -> u64 {
323        match state {
324            [] => return 0,
325            [single] => return self.grundy(single),
326            _ => {}
327        }
328        let (xor, odd) = self.reduce_sum(state);
329        odd.into_iter().fold(xor, |value, s| value ^ self.grundy(s))
330    }
331
332    #[allow(clippy::type_complexity)] // Sum states and labels both carry component information.
333    fn sum_winning_moves(&mut self, state: &[S]) -> Vec<Neighbor<Vec<S>, (usize, L)>> {
334        let xor = self.sum_grundy(state);
335        let mut result = Vec::new();
336        if xor == 0 {
337            return result;
338        }
339        for (i, component) in state.iter().enumerate() {
340            let target = xor ^ self.grundy(component);
341            for next in (self.moves)(component) {
342                if self.grundy(&next.to) == target {
343                    let mut to = state.to_vec();
344                    to[i] = next.to;
345                    result.push(Neighbor::new(to, (i, next.label)));
346                }
347            }
348        }
349        result
350    }
351
352    fn sum_outcome(&mut self, state: &[S], misere: bool) -> GameOutcome {
353        if state.is_empty() {
354            return GameOutcome::from_win(misere);
355        }
356        if let [single] = state {
357            return self.outcome(single, misere);
358        }
359        let win = if misere {
360            if let Some(&win) = self.sums.get(state) {
361                win
362            } else {
363                memoized_wins(
364                    &canonical_sum(state.to_vec(), &mut self.sum_ids),
365                    &mut self.sums,
366                    |_| ControlFlow::Continue((0, None)),
367                    |s, (i, moves)| {
368                        while *i < s.len() {
369                            if *i != 0 && s[*i - 1] == s[*i] {
370                                *i += 1;
371                                continue;
372                            }
373                            let iterator =
374                                moves.get_or_insert_with(|| (self.moves)(&s[*i]).into_iter());
375                            if let Some(next) = iterator.next() {
376                                let mut to = s.clone();
377                                to[*i] = next.to;
378                                return Some(canonical_sum(to, &mut self.sum_ids));
379                            }
380                            *i += 1;
381                            *moves = None;
382                        }
383                        None
384                    },
385                    true,
386                )
387            }
388        } else {
389            let (xor, odd) = self.reduce_sum(state);
390            if xor == 0 && odd.len() == 1 {
391                return self.outcome(odd.into_iter().next().unwrap(), false);
392            }
393            odd.into_iter().fold(xor, |value, s| value ^ self.grundy(s)) != 0
394        };
395        GameOutcome::from_win(win)
396    }
Source

fn winning_moves( &mut self, state: &S, misere: bool, ) -> impl Iterator<Item = Neighbor<S, L>>

Examples found in repository?
crates/competitive/src/algorithm/game_analyzer/impartial.rs (line 177)
176    pub fn winning_moves(&mut self, state: &S) -> Vec<Neighbor<S, L>> {
177        self.game.winning_moves(state, false).collect()
178    }
179}
180impl<S, L, F, I> GameAnalysis<'_, Impartial<S, F>, S, GameValue>
181where
182    S: Clone + Eq + Hash,
183    F: FnMut(&S) -> I,
184    I: IntoIterator<Item = Neighbor<S, L>>,
185{
186    pub fn winning_moves(&mut self, state: &S) -> Vec<Neighbor<S, L>> {
187        self.game.winning_moves(state, false).collect()
188    }
189}
190impl<S, L, F, I, const MISERE: bool> GameAnalysis<'_, Impartial<S, F>, S, GameOutcome, MISERE>
191where
192    S: Clone + Eq + Hash,
193    F: FnMut(&S) -> I,
194    I: IntoIterator<Item = Neighbor<S, L>>,
195{
196    pub fn winning_moves(&mut self, state: &S) -> Vec<Neighbor<S, L>> {
197        self.game.winning_moves(state, MISERE).collect()
198    }
199}
200
201#[allow(clippy::type_complexity)] // Sum states and labels both carry component information.
202impl<S, L, F, I, V, const MISERE: bool, E> GameAnalysis<'_, Impartial<S, F>, [S], V, MISERE, E>
203where
204    S: Clone,
205    F: FnMut(&S) -> I,
206    I: IntoIterator<Item = Neighbor<S, L>>,
207    E: FnMut(&mut Impartial<S, F>, &[S]) -> V,
208{
209    pub fn moves(&mut self, state: &[S]) -> Vec<EvaluatedMove<Vec<S>, (usize, L), V>> {
210        let mut result = Vec::new();
211        for (i, component) in state.iter().enumerate() {
212            for next in (self.game.moves)(component) {
213                let mut to = state.to_vec();
214                to[i] = next.to;
215                let value = self.eval(&to);
216                result.push(EvaluatedMove {
217                    next: Neighbor::new(to, (i, next.label)),
218                    value,
219                });
220            }
221        }
222        result
223    }
224}
225#[allow(clippy::type_complexity)] // Sum states and labels both carry component information.
226impl<S, L, F, I> GameAnalysis<'_, Impartial<S, F>, [S], u64>
227where
228    S: Clone + Eq + Hash,
229    F: FnMut(&S) -> I,
230    I: IntoIterator<Item = Neighbor<S, L>>,
231{
232    pub fn winning_moves(&mut self, state: &[S]) -> Vec<Neighbor<Vec<S>, (usize, L)>> {
233        self.game.sum_winning_moves(state)
234    }
235}
236#[allow(clippy::type_complexity)] // Sum states and labels both carry component information.
237impl<S, L, F, I, const MISERE: bool> GameAnalysis<'_, Impartial<S, F>, [S], GameOutcome, MISERE>
238where
239    S: Clone + Eq + Hash,
240    F: FnMut(&S) -> I,
241    I: IntoIterator<Item = Neighbor<S, L>>,
242{
243    pub fn winning_moves(&mut self, state: &[S]) -> Vec<Neighbor<Vec<S>, (usize, L)>> {
244        if let [single] = state {
245            return self
246                .game
247                .winning_moves(single, MISERE)
248                .map(|next| Neighbor::new(vec![next.to], (0, next.label)))
249                .collect();
250        }
251        if !MISERE {
252            return self.game.sum_winning_moves(state);
253        }
254        let mut result = Vec::new();
255        for (i, component) in state.iter().enumerate() {
256            for next in (self.game.moves)(component) {
257                let mut to = state.to_vec();
258                to[i] = next.to;
259                if self.eval(&to) == GameOutcome::Lose {
260                    result.push(Neighbor::new(to, (i, next.label)));
261                }
262            }
263        }
264        result
265    }
Source

fn sum_grundy(&mut self, state: &[S]) -> u64

Examples found in repository?
crates/competitive/src/algorithm/game_analyzer/impartial.rs (line 334)
333    fn sum_winning_moves(&mut self, state: &[S]) -> Vec<Neighbor<Vec<S>, (usize, L)>> {
334        let xor = self.sum_grundy(state);
335        let mut result = Vec::new();
336        if xor == 0 {
337            return result;
338        }
339        for (i, component) in state.iter().enumerate() {
340            let target = xor ^ self.grundy(component);
341            for next in (self.moves)(component) {
342                if self.grundy(&next.to) == target {
343                    let mut to = state.to_vec();
344                    to[i] = next.to;
345                    result.push(Neighbor::new(to, (i, next.label)));
346                }
347            }
348        }
349        result
350    }
Source

fn sum_winning_moves( &mut self, state: &[S], ) -> Vec<Neighbor<Vec<S>, (usize, L)>>

Examples found in repository?
crates/competitive/src/algorithm/game_analyzer/impartial.rs (line 233)
232    pub fn winning_moves(&mut self, state: &[S]) -> Vec<Neighbor<Vec<S>, (usize, L)>> {
233        self.game.sum_winning_moves(state)
234    }
235}
236#[allow(clippy::type_complexity)] // Sum states and labels both carry component information.
237impl<S, L, F, I, const MISERE: bool> GameAnalysis<'_, Impartial<S, F>, [S], GameOutcome, MISERE>
238where
239    S: Clone + Eq + Hash,
240    F: FnMut(&S) -> I,
241    I: IntoIterator<Item = Neighbor<S, L>>,
242{
243    pub fn winning_moves(&mut self, state: &[S]) -> Vec<Neighbor<Vec<S>, (usize, L)>> {
244        if let [single] = state {
245            return self
246                .game
247                .winning_moves(single, MISERE)
248                .map(|next| Neighbor::new(vec![next.to], (0, next.label)))
249                .collect();
250        }
251        if !MISERE {
252            return self.game.sum_winning_moves(state);
253        }
254        let mut result = Vec::new();
255        for (i, component) in state.iter().enumerate() {
256            for next in (self.game.moves)(component) {
257                let mut to = state.to_vec();
258                to[i] = next.to;
259                if self.eval(&to) == GameOutcome::Lose {
260                    result.push(Neighbor::new(to, (i, next.label)));
261                }
262            }
263        }
264        result
265    }
Source

fn sum_outcome(&mut self, state: &[S], misere: bool) -> GameOutcome

Examples found in repository?
crates/competitive/src/algorithm/game_analyzer/impartial.rs (line 149)
148    pub fn outcome(self) -> GameAnalysis<'a, Impartial<S, F>, [S], GameOutcome, MISERE> {
149        GameAnalysis::new(self.game, |g, s| g.sum_outcome(s, MISERE))
150    }
Source

fn reduce_sum<'a>(&self, state: &'a [S]) -> (u64, HashSet<&'a S>)

Examples found in repository?
crates/competitive/src/algorithm/game_analyzer/impartial.rs (line 328)
322    fn sum_grundy(&mut self, state: &[S]) -> u64 {
323        match state {
324            [] => return 0,
325            [single] => return self.grundy(single),
326            _ => {}
327        }
328        let (xor, odd) = self.reduce_sum(state);
329        odd.into_iter().fold(xor, |value, s| value ^ self.grundy(s))
330    }
331
332    #[allow(clippy::type_complexity)] // Sum states and labels both carry component information.
333    fn sum_winning_moves(&mut self, state: &[S]) -> Vec<Neighbor<Vec<S>, (usize, L)>> {
334        let xor = self.sum_grundy(state);
335        let mut result = Vec::new();
336        if xor == 0 {
337            return result;
338        }
339        for (i, component) in state.iter().enumerate() {
340            let target = xor ^ self.grundy(component);
341            for next in (self.moves)(component) {
342                if self.grundy(&next.to) == target {
343                    let mut to = state.to_vec();
344                    to[i] = next.to;
345                    result.push(Neighbor::new(to, (i, next.label)));
346                }
347            }
348        }
349        result
350    }
351
352    fn sum_outcome(&mut self, state: &[S], misere: bool) -> GameOutcome {
353        if state.is_empty() {
354            return GameOutcome::from_win(misere);
355        }
356        if let [single] = state {
357            return self.outcome(single, misere);
358        }
359        let win = if misere {
360            if let Some(&win) = self.sums.get(state) {
361                win
362            } else {
363                memoized_wins(
364                    &canonical_sum(state.to_vec(), &mut self.sum_ids),
365                    &mut self.sums,
366                    |_| ControlFlow::Continue((0, None)),
367                    |s, (i, moves)| {
368                        while *i < s.len() {
369                            if *i != 0 && s[*i - 1] == s[*i] {
370                                *i += 1;
371                                continue;
372                            }
373                            let iterator =
374                                moves.get_or_insert_with(|| (self.moves)(&s[*i]).into_iter());
375                            if let Some(next) = iterator.next() {
376                                let mut to = s.clone();
377                                to[*i] = next.to;
378                                return Some(canonical_sum(to, &mut self.sum_ids));
379                            }
380                            *i += 1;
381                            *moves = None;
382                        }
383                        None
384                    },
385                    true,
386                )
387            }
388        } else {
389            let (xor, odd) = self.reduce_sum(state);
390            if xor == 0 && odd.len() == 1 {
391                return self.outcome(odd.into_iter().next().unwrap(), false);
392            }
393            odd.into_iter().fold(xor, |value, s| value ^ self.grundy(s)) != 0
394        };
395        GameOutcome::from_win(win)
396    }

Auto Trait Implementations§

§

impl<S, F> Forget for Impartial<S, F>

§

impl<S, F> Freeze for Impartial<S, F>
where F: Freeze, HashMap<S, u64>: Freeze, [HashMap<S, bool>; 2]: Freeze, HashMap<Vec<S>, bool>: Freeze, HashMap<S, usize>: Freeze,

§

impl<S, F> Move for Impartial<S, F>

§

impl<S, F> RefUnwindSafe for Impartial<S, F>

§

impl<S, F> Send for Impartial<S, F>
where F: Send, HashMap<S, u64>: Send, [HashMap<S, bool>; 2]: Send, HashMap<Vec<S>, bool>: Send, HashMap<S, usize>: Send,

§

impl<S, F> Sync for Impartial<S, F>
where F: Sync, HashMap<S, u64>: Sync, [HashMap<S, bool>; 2]: Sync, HashMap<Vec<S>, bool>: Sync, HashMap<S, usize>: Sync,

§

impl<S, F> Unpin for Impartial<S, F>
where F: Unpin, HashMap<S, u64>: Unpin, [HashMap<S, bool>; 2]: Unpin, HashMap<Vec<S>, bool>: Unpin, HashMap<S, usize>: Unpin,

§

impl<S, F> UnsafeUnpin for Impartial<S, F>

§

impl<S, F> UnwindSafe for Impartial<S, F>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToArrayVecScalar for T

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.