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>
impl<S, L, F, I> Impartial<S, F>
Sourcefn grundy(&mut self, state: &S) -> u64
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 }Sourcefn outcome(&mut self, state: &S, misere: bool) -> GameOutcome
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 }Sourcefn winning_moves(
&mut self,
state: &S,
misere: bool,
) -> impl Iterator<Item = Neighbor<S, L>>
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 }Sourcefn sum_grundy(&mut self, state: &[S]) -> u64
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 }Sourcefn sum_winning_moves(
&mut self,
state: &[S],
) -> Vec<Neighbor<Vec<S>, (usize, L)>>
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 }Sourcefn sum_outcome(&mut self, state: &[S], misere: bool) -> GameOutcome
fn sum_outcome(&mut self, state: &[S], misere: bool) -> GameOutcome
Sourcefn reduce_sum<'a>(&self, state: &'a [S]) -> (u64, HashSet<&'a S>)
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>
impl<S, F> Move for Impartial<S, F>
impl<S, F> RefUnwindSafe for Impartial<S, F>where
F: RefUnwindSafe,
HashMap<S, u64>: RefUnwindSafe,
[HashMap<S, bool>; 2]: RefUnwindSafe,
HashMap<Vec<S>, bool>: RefUnwindSafe,
HashMap<S, usize>: RefUnwindSafe,
impl<S, F> Send for Impartial<S, F>
impl<S, F> Sync for Impartial<S, F>
impl<S, F> Unpin for Impartial<S, F>
impl<S, F> UnsafeUnpin for Impartial<S, F>where
F: UnsafeUnpin,
HashMap<S, u64>: UnsafeUnpin,
[HashMap<S, bool>; 2]: UnsafeUnpin,
HashMap<Vec<S>, bool>: UnsafeUnpin,
HashMap<S, usize>: UnsafeUnpin,
impl<S, F> UnwindSafe for Impartial<S, F>where
F: UnwindSafe,
HashMap<S, u64>: UnwindSafe,
[HashMap<S, bool>; 2]: UnwindSafe,
HashMap<Vec<S>, bool>: UnwindSafe,
HashMap<S, usize>: UnwindSafe,
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more