pub struct GraphSums {
indices: HashMap<Vec<usize>, usize>,
outcomes: GraphOutcomes,
}Fields§
§indices: HashMap<Vec<usize>, usize>§outcomes: GraphOutcomesImplementations§
Source§impl GraphSums
impl GraphSums
Sourcefn prepare(&mut self, state: Vec<usize>, components: &[Vec<usize>]) -> usize
fn prepare(&mut self, state: Vec<usize>, components: &[Vec<usize>]) -> usize
Examples found in repository?
crates/competitive/src/algorithm/game_analyzer/graph.rs (line 513)
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 }Sourcefn canonical(state: Vec<usize>, components: &[Vec<usize>]) -> Vec<usize>
fn canonical(state: Vec<usize>, components: &[Vec<usize>]) -> Vec<usize>
Examples found in repository?
crates/competitive/src/algorithm/game_analyzer/graph.rs (line 310)
272 pub fn winning_moves(&mut self, state: &[S]) -> Vec<Neighbor<Vec<S>, (usize, L)>> {
273 if state.is_empty() {
274 return Vec::new();
275 }
276 if let [single] = state {
277 let mut query: GameAnalysis<'_, _, S, GameOutcome, MISERE> =
278 GameAnalysis::new(self.game, |g: &mut ImpartialGraph<'_, G>, s| {
279 g.outcome(s, MISERE)
280 });
281 return query
282 .winning_moves(single)
283 .into_iter()
284 .map(|next| Neighbor::new(vec![next.to], (0, next.label)))
285 .collect();
286 }
287 if self.eval(state) != GameOutcome::Win {
288 return Vec::new();
289 }
290 let mut indices = self.game.sum_indices(state);
291 let game = &self.game;
292 let mut xor = 0;
293 let mut infinite = 0;
294 if !MISERE {
295 for &v in &indices {
296 if let Some((value, _)) = game.generalized_grundy[v] {
297 xor ^= value;
298 } else {
299 infinite += 1;
300 }
301 }
302 }
303 let mut result = Vec::new();
304 for (i, &component) in state.iter().enumerate() {
305 let source = indices[i];
306 for next in game.graph.neighbors(component) {
307 indices[i] = game.index_of(next.to);
308 let wins = if MISERE {
309 let key =
310 GraphSums::canonical(indices.clone(), &game.index.as_ref().unwrap().edges);
311 game.sums.outcomes.values[game.sums.indices[&key]] == GameOutcome::Lose
312 } else if let Some((value, _)) = game.generalized_grundy[indices[i]] {
313 match game.generalized_grundy[source] {
314 Some((g, _)) => infinite == 0 && xor ^ g == value,
315 None => infinite == 1 && xor == value,
316 }
317 } else {
318 false
319 };
320 if wins {
321 let mut to = state.to_vec();
322 to[i] = next.to;
323 result.push(Neighbor::new(to, (i, next.label)));
324 }
325 }
326 indices[i] = source;
327 }
328 result
329 }
330
331 /// A move that guarantees termination when winning, or preserves a draw.
332 /// Returns `None` at terminal or losing positions.
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 }
666}
667
668impl GraphSums {
669 fn prepare(&mut self, state: Vec<usize>, components: &[Vec<usize>]) -> usize {
670 let state = Self::canonical(state, components);
671 if let Some(&root) = self.indices.get(&state) {
672 return root;
673 }
674 let root = self.outcomes.values.len();
675 self.indices.insert(state.clone(), root);
676 let mut edges = vec![Vec::new()];
677 let mut reverse = vec![Vec::new()];
678 let mut pending = VecDeque::from([(root, state)]);
679 while let Some((v, state)) = pending.pop_front() {
680 for (i, &component) in state.iter().enumerate() {
681 if i != 0 && state[i - 1] == component {
682 continue;
683 }
684 for &next in &components[component] {
685 let mut to = state.clone();
686 to[i] = next;
687 let to = Self::canonical(to, components);
688 let u = *self.indices.entry(to).or_insert_with_key(|to| {
689 let u = root + edges.len();
690 pending.push_back((u, to.clone()));
691 edges.push(Vec::new());
692 reverse.push(Vec::new());
693 u
694 });
695 edges[v - root].push(u);
696 }
697 }
698 edges[v - root].sort_unstable();
699 edges[v - root].dedup();
700 for &u in &edges[v - root] {
701 if u >= root {
702 reverse[u - root].push(v);
703 }
704 }
705 }
706 self.outcomes.extend(&edges, &reverse, true);
707 root
708 }Trait Implementations§
Auto Trait Implementations§
impl Forget for GraphSums
impl Freeze for GraphSums
impl Move for GraphSums
impl RefUnwindSafe for GraphSums
impl Send for GraphSums
impl Sync for GraphSums
impl Unpin for GraphSums
impl UnsafeUnpin for GraphSums
impl UnwindSafe for GraphSums
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