pub fn game_mex(values: Vec<u64>) -> u64Examples found in repository?
crates/competitive/src/algorithm/game_analyzer/impartial.rs (line 291)
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 }More examples
crates/competitive/src/algorithm/game_analyzer/graph.rs (line 395)
333 pub fn best_move(&mut self, state: &[S]) -> Option<Neighbor<Vec<S>, (usize, L)>> {
334 if state.is_empty() {
335 return None;
336 }
337 if let [single] = state {
338 let mut query: GameAnalysis<'_, _, S, GameOutcome, MISERE> =
339 GameAnalysis::new(self.game, |g: &mut ImpartialGraph<'_, G>, s| {
340 g.outcome(s, MISERE)
341 });
342 return query
343 .best_move(single)
344 .map(|next| Neighbor::new(vec![next.to], (0, next.label)));
345 }
346 let outcome = self.eval(state);
347 if outcome == GameOutcome::Lose {
348 return None;
349 }
350 let mut indices = self.game.sum_indices(state);
351 let game = &self.game;
352 if MISERE {
353 let key = GraphSums::canonical(indices.clone(), &game.index.as_ref().unwrap().edges);
354 let winner = game.sums.outcomes.winning[game.sums.indices[&key]];
355 for (i, &component) in state.iter().enumerate() {
356 let original = indices[i];
357 for next in game.graph.neighbors(component) {
358 indices[i] = game.index_of(next.to);
359 let key =
360 GraphSums::canonical(indices.clone(), &game.index.as_ref().unwrap().edges);
361 let id = game.sums.indices[&key];
362 if Some(id) == winner
363 || outcome == GameOutcome::Draw
364 && game.sums.outcomes.values[id] == GameOutcome::Draw
365 {
366 let mut to = state.to_vec();
367 to[i] = next.to;
368 return Some(Neighbor::new(to, (i, next.label)));
369 }
370 }
371 indices[i] = original;
372 }
373 return None;
374 }
375 let mut xor = 0;
376 let mut infinite = None;
377 let mut multiple = false;
378 for (i, &v) in indices.iter().enumerate() {
379 if let Some((value, _)) = game.generalized_grundy[v] {
380 xor ^= value;
381 } else if infinite.replace(i).is_some() {
382 multiple = true;
383 break;
384 }
385 }
386 if outcome == GameOutcome::Draw {
387 let i = infinite.unwrap();
388 let v = indices[i];
389 let edges = &game.index.as_ref().unwrap().edges;
390 let target = (!multiple).then(|| {
391 let values = edges[v]
392 .iter()
393 .filter_map(|&u| game.generalized_grundy[u].map(|(g, _)| g))
394 .collect();
395 game_mex(values)
396 });
397 // Preserve two infinite components, or keep the finite XOR outside
398 // the remaining infinite component's finite follower values.
399 if target.is_none_or(|g| g == xor) {
400 let to = edges[v]
401 .iter()
402 .copied()
403 .find(|&u| {
404 game.generalized_grundy[u].is_none()
405 && target.is_none_or(|g| {
406 !edges[u].iter().any(|&w| {
407 game.generalized_grundy[w].is_some_and(|(value, _)| value == g)
408 })
409 })
410 })
411 .unwrap();
412 let next = game
413 .graph
414 .neighbors(state[i])
415 .find(|next| game.index_of(next.to) == to)
416 .unwrap();
417 let mut to = state.to_vec();
418 to[i] = next.to;
419 return Some(Neighbor::new(to, (i, next.label)));
420 }
421 // The current finite XOR exceeds this mex, so a finite component
422 // can decrease it to the mex without moving the infinite component.
423 xor ^= target.unwrap();
424 infinite = None;
425 }
426 // Reduce the largest (Grundy, counter) pair, then minimize the
427 // destination counter so repeated winning moves terminate.
428 let (i, target) = if let Some(i) = infinite {
429 (i, xor)
430 } else {
431 let (i, (g, _)) = indices
432 .iter()
433 .enumerate()
434 .filter_map(|(i, &v)| game.generalized_grundy[v].map(|g| (i, g)))
435 .filter(|(_, (g, _))| g ^ xor < *g)
436 .max_by_key(|&(i, g)| (g, Reverse(i)))?;
437 (i, g ^ xor)
438 };
439 let next = game
440 .graph
441 .neighbors(state[i])
442 .filter_map(|next| {
443 let (g, counter) = game.generalized_grundy[game.index_of(next.to)]?;
444 (g == target).then_some((next, counter))
445 })
446 .min_by_key(|&(_, counter)| counter)?
447 .0;
448 let mut to = state.to_vec();
449 to[i] = next.to;
450 Some(Neighbor::new(to, (i, next.label)))
451 }
452}
453
454impl<G> ImpartialGraph<'_, G>
455where
456 G: Graph + VertexMap<Option<usize>>,
457{
458 fn grundy(&mut self, state: &G::Vertex) -> u64 {
459 self.prepare();
460 let root = self.index_of(*state);
461 let index = self.index.as_ref().unwrap();
462 self.grundy.resize(index.edges.len(), None);
463 if let Some(value) = self.grundy[root] {
464 return value;
465 }
466 let mut active = HashSet::from([root]);
467 let mut stack = vec![(root, index.edges[root].iter())];
468 while let Some((v, moves)) = stack.last_mut() {
469 if let Some(&u) = moves.next() {
470 if self.grundy[u].is_none() {
471 assert!(active.insert(u), "cycle in a finite game");
472 stack.push((u, index.edges[u].iter()));
473 }
474 } else {
475 let values = index.edges[*v]
476 .iter()
477 .map(|&u| self.grundy[u].unwrap())
478 .collect();
479 self.grundy[*v] = Some(game_mex(values));
480 active.remove(v);
481 stack.pop();
482 }
483 }
484 self.grundy[root].unwrap()
485 }
486
487 fn outcome(&mut self, state: &G::Vertex, misere: bool) -> GameOutcome {
488 self.prepare();
489 let index = self.index.as_ref().unwrap();
490 let outcome = &mut self.outcomes[usize::from(misere)];
491 if outcome.values.is_empty() {
492 outcome.extend(&index.edges, &index.reverse, misere);
493 }
494 self.outcomes[usize::from(misere)].values[self.index_of(*state)]
495 }
496
497 fn sum_grundy(&mut self, state: &[G::Vertex]) -> u64 {
498 state.iter().fold(0, |value, s| value ^ self.grundy(s))
499 }
500
501 fn sum_outcome(&mut self, state: &[G::Vertex], misere: bool) -> GameOutcome {
502 match state {
503 [] => return GameOutcome::from_win(misere),
504 [single] => return self.outcome(single, misere),
505 _ => {}
506 }
507 let indices = self.sum_indices(state);
508 if !misere {
509 self.solve_generalized_grundy();
510 return self.sum_normal_outcome(&indices);
511 }
512 let edges = &self.index.as_ref().unwrap().edges;
513 let root = self.sums.prepare(indices, edges);
514 self.sums.outcomes.values[root]
515 }
516
517 fn sum_normal_outcome(&self, state: &[usize]) -> GameOutcome {
518 let mut xor = 0;
519 let mut infinite = None;
520 for &v in state {
521 if let Some((value, _)) = self.generalized_grundy[v] {
522 xor ^= value;
523 } else if infinite.replace(v).is_some() {
524 return GameOutcome::Draw;
525 }
526 }
527 if let Some(v) = infinite {
528 let can_win = self.index.as_ref().unwrap().edges[v]
529 .iter()
530 .any(|&u| self.generalized_grundy[u].is_some_and(|(value, _)| value == xor));
531 if can_win {
532 GameOutcome::Win
533 } else {
534 GameOutcome::Draw
535 }
536 } else if xor == 0 {
537 GameOutcome::Lose
538 } else {
539 GameOutcome::Win
540 }
541 }
542
543 fn sum_indices(&mut self, state: &[G::Vertex]) -> Vec<usize> {
544 self.prepare();
545 state.iter().map(|&s| self.index_of(s)).collect()
546 }
547
548 fn prepare(&mut self) {
549 if self.index.is_some() {
550 return;
551 }
552 let vertices: Vec<_> = self.graph.vertices().collect();
553 let mut indices = self.graph.construct_vmap(|| None);
554 for (i, &v) in vertices.iter().enumerate() {
555 self.graph.vmap_set(&mut indices, v, Some(i));
556 }
557 let mut edges = vec![Vec::new(); vertices.len()];
558 let mut reverse = vec![Vec::new(); vertices.len()];
559 let mut seen = vec![None; vertices.len()];
560 for (i, &v) in vertices.iter().enumerate() {
561 for next in self.graph.neighbors(v) {
562 let j = self
563 .graph
564 .vmap_get(&indices, next.to)
565 .expect("vertex is not enumerated by the graph");
566 if seen[j] != Some(i) {
567 seen[j] = Some(i);
568 edges[i].push(j);
569 reverse[j].push(i);
570 }
571 }
572 }
573 self.index = Some(GraphIndex {
574 indices,
575 edges,
576 reverse,
577 });
578 }
579
580 fn index_of(&self, v: G::Vertex) -> usize {
581 self.graph
582 .vmap_get(&self.index.as_ref().unwrap().indices, v)
583 .expect("vertex is not enumerated by the graph")
584 }
585
586 fn solve_generalized_grundy(&mut self) {
587 let index = self.index.as_ref().unwrap();
588 let n = index.edges.len();
589 if self.generalized_grundy.len() == n {
590 return;
591 }
592 let mut values: Vec<Option<(u64, usize)>> = vec![None; n];
593 // Visit acyclic followers first, so DAGs need only one pass.
594 let reverse = UsizeGraph::new(n, |v: usize| index.reverse[v].iter().map(|&u| (u, ())));
595 let order = reverse.topological_sort();
596 if order.len() == n {
597 for (counter, v) in order.into_iter().enumerate() {
598 let mex = self.grundy.get(v).copied().flatten().unwrap_or_else(|| {
599 let options = index.edges[v]
600 .iter()
601 .map(|&u| values[u].unwrap().0)
602 .collect();
603 game_mex(options)
604 });
605 values[v] = Some((mex, counter));
606 }
607 self.generalized_grundy = values;
608 return;
609 }
610 let mut queue: VecDeque<_> = order.into();
611 let mut queued = vec![false; n];
612 for &v in &queue {
613 queued[v] = true;
614 }
615 for (v, pending) in queued.iter_mut().enumerate() {
616 if !*pending {
617 *pending = true;
618 queue.push_back(v);
619 }
620 }
621 let mut counter = 0;
622 let mut options = vec![HashSet::new(); n];
623 let mut mexes = vec![0; n];
624 while let Some(v) = queue.pop_front() {
625 queued[v] = false;
626 let mex = mexes[v];
627 // Every unresolved option must be able to return to this mex.
628 let ready = index.edges[v]
629 .iter()
630 .all(|&u| values[u].is_some() || options[u].contains(&mex));
631 if !ready {
632 continue;
633 }
634 values[v] = Some((mex, counter));
635 counter += 1;
636 for &u in &index.reverse[v] {
637 if values[u].is_some() {
638 continue;
639 }
640 let inserted = options[u].insert(mex);
641 if inserted {
642 while options[u].contains(&mexes[u]) {
643 mexes[u] += 1;
644 }
645 }
646 // A larger value already had a follower at this vertex's mex.
647 if mex < mexes[u] && !queued[u] {
648 queued[u] = true;
649 queue.push_back(u);
650 }
651 if inserted {
652 for &w in &index.reverse[u] {
653 if queue.len() == n - counter {
654 break; // All unresolved vertices are already queued.
655 }
656 if values[w].is_none() && !queued[w] && mexes[w] == mex {
657 queued[w] = true;
658 queue.push_back(w);
659 }
660 }
661 }
662 }
663 }
664 self.generalized_grundy = values;
665 }crates/competitive/src/algorithm/game_analyzer/value.rs (line 274)
237 fn from_options_memoized(
238 mut left: Vec<Self>,
239 mut right: Vec<Self>,
240 cache: &mut HashMap<GameValueNode, HashMap<GameValueNode, bool>>,
241 ) -> Self {
242 loop {
243 if left.iter().chain(&right).all(|g| g.as_number().is_some()) {
244 let l = left.iter().filter_map(Self::as_number).max();
245 let r = right.iter().filter_map(Self::as_number).min();
246 if let Some(n) = Dyadic::simplest_between(l, r) {
247 return n.into();
248 }
249 let (l, r) = (l.unwrap(), r.unwrap());
250 if l == r {
251 return Self::number_up_nimber(l, 0, 1);
252 }
253 let (sum, difference) = (l + r, l - r);
254 let temperature = Dyadic::new(difference.numerator(), difference.exponent() + 1);
255 return Self::from_switches(
256 Dyadic::new(sum.numerator(), sum.exponent() + 1),
257 0,
258 0,
259 vec![temperature],
260 Self::temperature_hash(temperature),
261 );
262 }
263 if !right.is_empty()
264 && let Some((number, _)) = left.first().and_then(Self::as_number_nimber)
265 {
266 let nimber = |g: &Self| {
267 g.as_number_nimber()
268 .filter(|&(q, _)| q == number)
269 .map(|(_, n)| n)
270 };
271 let l: Option<Vec<_>> = left.iter().map(nimber).collect();
272 let r: Option<Vec<_>> = right.iter().map(nimber).collect();
273 if let (Some(l), Some(r)) = (l, r) {
274 let value = game_mex(l);
275 if value == game_mex(r) {
276 return Self::number_up_nimber(number, 0, value);
277 }
278 }
279 }
280 for (moves, replies, sign) in [(&left, &right, 1), (&right, &left, -1)] {
281 if replies.len() != 1 {
282 continue;
283 }
284 if let Some((q, k, n)) = replies[0].as_number_up_nimber() {
285 if moves.len() == 1
286 && moves[0].as_number() == Some(q)
287 && (k == 0 || k.signum() == sign)
288 {
289 return Self::number_up_nimber(q, k + sign, n ^ 1);
290 }
291 if k == 0 && n == 0 && moves.len() == 2 {
292 let a = moves[0].as_number_nimber();
293 let b = moves[1].as_number_nimber();
294 if (a == Some((q, 0)) && b == Some((q, 1)))
295 || (a == Some((q, 1)) && b == Some((q, 0)))
296 {
297 return Self::number_up_nimber(q, sign, 1);
298 }
299 }
300 }
301 }
302 if left.len() == 1
303 && right.len() == 1
304 && let (Some((a, k, n, ts)), Some((b, l, m, us))) =
305 (left[0].as_switch_sum(), right[0].as_switch_sum())
306 && k == l
307 && n == m
308 && ts == us
309 && a > b
310 {
311 let (sum, difference) = (a + b, a - b);
312 let temperature = Dyadic::new(difference.numerator(), difference.exponent() + 1);
313 if ts.first().is_none_or(|&t| temperature > t) {
314 return Self::from_switches(
315 Dyadic::new(sum.numerator(), sum.exponent() + 1),
316 k,
317 n,
318 std::iter::once(temperature)
319 .chain(ts.iter().copied())
320 .collect(),
321 Self::temperature_hash(temperature) ^ left[0].switch_hash(),
322 );
323 }
324 }
325 let count = left.len() + right.len();
326 Self::remove_dominated(&mut left, Player::Left, cache);
327 Self::remove_dominated(&mut right, Player::Right, cache);
328 if count != left.len() + right.len() {
329 continue;
330 }
331 let options = Rc::new((left, right));
332 let game = Self(GameValueNode::Options(0.into(), options.clone()));
333 // Bypasses preserve the game, so all checks use the same candidate.
334 let mut reversible = Vec::new();
335 for player in [Player::Left, Player::Right] {
336 let moves = match player {
337 Player::Left => &options.0,
338 Player::Right => &options.1,
339 };
340 for (i, g) in moves.iter().enumerate() {
341 if let Some((q, n)) = g.as_number_nimber()
342 && n > 1
343 {
344 let number = Self::from(q);
345 let (lower, upper) = match player {
346 Player::Left => (&game, &number),
347 Player::Right => (&number, &game),
348 };
349 // A strict inequality against q excludes every reply q + *m.
350 if lower.leq_memoized(upper, cache) && !upper.leq_memoized(lower, cache) {
351 continue;
352 }
353 }
354 if let Some(reply) = g.options(player.opponent()).find(|r| match player {
355 Player::Left => r.leq_memoized(&game, cache),
356 Player::Right => game.leq_memoized(r, cache),
357 }) {
358 reversible.push((player, i, reply));
359 }
360 }
361 }
362 if reversible.is_empty() {
363 return game;
364 }
365 drop(game);
366 (left, right) = Rc::unwrap_or_clone(options);
367 for (player, i, reply) in reversible.into_iter().rev() {
368 let moves = if player == Player::Left {
369 &mut left
370 } else {
371 &mut right
372 };
373 moves.remove(i);
374 moves.extend(reply.options(player));
375 }
376 }
377 }