use crate::games::partizan::integer_value;
use crate::games::Game;
pub fn atomic_weight(g: &Game) -> Option<Game> {
if !g.is_all_small() {
return None;
}
let g = g.canonical();
if g.left().is_empty() && g.right().is_empty() {
return Some(Game::integer(0)); }
let awl: Vec<Game> = g.left().iter().map(atomic_weight).collect::<Option<_>>()?;
let awr: Vec<Game> = g.right().iter().map(atomic_weight).collect::<Option<_>>()?;
let a_left: Vec<Game> = awl.iter().map(|a| a.add(&Game::integer(-2))).collect();
let a_right: Vec<Game> = awr.iter().map(|a| a.add(&Game::integer(2))).collect();
let a_canon = Game::new(a_left.clone(), a_right.clone()).canonical();
let a_int = match integer_value(&a_canon) {
None => return Some(a_canon),
Some(k) => k,
};
let far = Game::nim_heap(g.birthday() + 1);
let le_gf = g.le(&far);
let le_fg = far.le(&g);
if le_fg && !le_gf {
let pred = |n: i128| {
let gn = Game::integer(n);
a_right.iter().all(|r| !r.le(&gn))
};
let mut n = a_int;
while !pred(n) {
n -= 1;
}
while pred(n + 1) {
n += 1;
}
Some(Game::integer(n))
} else if le_gf && !le_fg {
let pred = |n: i128| {
let gn = Game::integer(n);
a_left.iter().all(|l| !gn.le(l))
};
let mut n = a_int;
while !pred(n) {
n += 1;
}
while pred(n - 1) {
n -= 1;
}
Some(Game::integer(n))
} else {
Some(Game::integer(0))
}
}
pub fn atomic_weight_int(g: &Game) -> Option<i128> {
integer_value(&atomic_weight(g)?)
}
#[cfg(test)]
mod tests {
use super::*;
fn up_n(n: i128) -> Game {
Game::up().times_int(n)
}
fn aw(g: &Game) -> Option<i128> {
atomic_weight_int(g)
}
#[test]
fn is_all_small_predicate() {
assert!(Game::zero().is_all_small());
assert!(Game::star().is_all_small());
assert!(Game::nim_heap(2).is_all_small());
assert!(Game::up().is_all_small());
assert!(Game::up().add(&Game::star()).is_all_small());
assert!(!Game::integer(3).is_all_small());
assert!(!Game::switch(1, -1).is_all_small());
assert!(!Game::from_surreal(&crate::scalar::Surreal::from_int(1))
.unwrap()
.is_all_small());
}
#[test]
fn atomic_weight_oracle_table() {
assert_eq!(aw(&Game::zero()), Some(0));
assert_eq!(aw(&Game::star()), Some(0)); assert_eq!(aw(&Game::nim_heap(2)), Some(0)); assert_eq!(aw(&Game::nim_heap(3)), Some(0)); assert_eq!(aw(&Game::up()), Some(1)); assert_eq!(aw(&Game::up().add(&Game::star())), Some(1)); assert_eq!(aw(&up_n(2)), Some(2)); assert_eq!(aw(&up_n(3)), Some(3)); assert_eq!(aw(&Game::up().neg()), Some(-1)); assert_eq!(aw(&up_n(2).neg()), Some(-2)); assert_eq!(aw(&Game::up().neg().add(&Game::star())), Some(-1)); assert_eq!(aw(&Game::up().add(&Game::nim_heap(2))), Some(1)); }
#[test]
fn atomic_weight_negation_symmetry() {
for g in [
Game::up(),
up_n(2),
Game::up().add(&Game::star()),
Game::nim_heap(2),
Game::zero(),
] {
let a = aw(&g).unwrap();
assert_eq!(aw(&g.neg()), Some(-a));
}
}
#[test]
fn non_all_small_has_no_atomic_weight() {
assert!(atomic_weight(&Game::integer(3)).is_none());
assert!(atomic_weight(&Game::switch(2, 0)).is_none());
assert_eq!(atomic_weight_int(&Game::integer(3)), None);
}
#[test]
fn atomic_weight_is_additive() {
let parts = [
Game::up(),
Game::up().times_int(2),
Game::star(),
Game::nim_heap(2),
Game::up().add(&Game::star()),
Game::up().neg(),
];
for g in &parts {
for h in &parts {
assert_eq!(
aw(&g.add(h)).unwrap(),
aw(g).unwrap() + aw(h).unwrap(),
"aw not additive on a pair"
);
}
}
}
#[test]
fn integer_branch_handles_fractional_option_weights() {
let h = Game::new(vec![up_n(2)], vec![Game::up().neg()]); assert!(atomic_weight_int(&h).is_none()); let g = Game::new(vec![up_n(2)], vec![h]); assert_eq!(aw(&g), Some(2));
}
}