Skip to main content

game_mex

Function game_mex 

Source
pub fn game_mex(values: Vec<u64>) -> u64
Examples 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
Hide additional 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    }