From 413544314776d0f6b5999b6c81169182bccd5a5f Mon Sep 17 00:00:00 2001 From: "Tristan F." Date: Sat, 2 Nov 2024 01:52:19 -0700 Subject: [PATCH] feat: expose more nim utils --- crates/game-solver/src/player.rs | 4 +++- crates/nimnim/src/lib.rs | 27 ++++++++++++++++++++++++--- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/crates/game-solver/src/player.rs b/crates/game-solver/src/player.rs index c4d44c0..1cc9229 100644 --- a/crates/game-solver/src/player.rs +++ b/crates/game-solver/src/player.rs @@ -77,7 +77,9 @@ impl TwoPlayer for PartizanPlayer {} /// Represents a player in a zero-sum (2-player) game, /// where the game is impartial. That is, -/// the only difference between players is who goes first. +/// the only difference between players is who goes first, +/// and the only thing that defines a game is its 'state': +/// both players need to have the same winning criteria for this player to be used. #[derive(PartialEq, Eq, Debug, Clone, Copy, Hash)] pub enum ImpartialPlayer { /// The player that will play on the current game state, diff --git a/crates/nimnim/src/lib.rs b/crates/nimnim/src/lib.rs index ffc4023..5232bda 100644 --- a/crates/nimnim/src/lib.rs +++ b/crates/nimnim/src/lib.rs @@ -1,7 +1,12 @@ -use std::ops::Add; +use std::{collections::HashSet, ops::Add}; -#[derive(PartialEq, PartialOrd, Eq, Ord, Debug)] -struct Nimber(usize); +/// A nimber is the size of a heap in a single-stack nim game. +/// +/// Nim is crucial for loop-free* impartial combinatorial game theory analysis. +/// +/// *This structure does not define utilities for loopy nimbers. +#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] +pub struct Nimber(pub usize); impl Add for Nimber { type Output = Nimber; @@ -13,6 +18,22 @@ impl Add for Nimber { } } +/// Returns Some(minimum excluded value of `list`), or `None` iff `list.is_empty()` +pub fn mex(list: &[Nimber]) -> Option { + let mut mex: Option = None; + let mut set: HashSet = HashSet::with_capacity(list.len()); + + for item in list { + if set.insert(*item) { + if item > &mex.unwrap_or(Nimber(0)) { + mex = Some(*item) + } + } + } + + mex +} + #[cfg(test)] mod tests { use crate::Nimber;