1use super::{Dyadic, PartisanOutcome, Player, game_mex};
2use std::{
3 cmp::Ordering,
4 collections::HashMap,
5 fmt,
6 hash::{DefaultHasher, Hash, Hasher},
7 iter::Sum,
8 ops::{Add, Neg, Sub},
9 rc::Rc,
10};
11
12#[derive(Clone)]
25pub struct GameValue(GameValueNode);
26
27#[derive(Clone)]
28enum GameValueNode {
29 NumberUpNimber(Dyadic<i64>, i64, u64),
30 Switches(Rc<GameValueSwitches>),
31 Options(Dyadic<i64>, Rc<(Vec<GameValue>, Vec<GameValue>)>),
33}
34
35impl PartialEq for GameValueNode {
37 fn eq(&self, rhs: &Self) -> bool {
38 match (self, rhs) {
39 (Self::NumberUpNimber(a, k, n), Self::NumberUpNimber(b, l, m)) => {
40 a == b && k == l && n == m
41 }
42 (Self::Switches(a), Self::Switches(b)) => {
43 Rc::ptr_eq(a, b)
44 || a.number == b.number
45 && a.up == b.up
46 && a.nimber == b.nimber
47 && a.temperatures[a.start..] == b.temperatures[b.start..]
48 }
49 (Self::Options(q, a), Self::Options(r, b)) => q == r && Rc::ptr_eq(a, b),
50 _ => false,
51 }
52 }
53}
54impl Eq for GameValueNode {}
55impl Hash for GameValueNode {
56 fn hash<H>(&self, state: &mut H)
57 where
58 H: Hasher,
59 {
60 match self {
61 Self::NumberUpNimber(q, k, n) => (q, k, n).hash(state),
62 Self::Switches(v) => (v.number, v.up, v.nimber, v.fingerprint).hash(state),
63 Self::Options(q, v) => {
64 Rc::as_ptr(v).hash(state);
65 if q.numerator() != 0 {
66 q.hash(state);
67 }
68 }
69 }
70 }
71}
72
73#[derive(Clone)]
74struct GameValueSwitches {
75 number: Dyadic<i64>,
76 up: i64,
77 nimber: u64,
78 temperatures: Rc<Vec<Dyadic<i64>>>,
79 start: usize,
80 fingerprint: u64,
82}
83
84impl GameValue {
85 pub fn nimber(value: u64) -> Self {
86 Self::number_up_nimber(0.into(), 0, value)
87 }
88
89 pub fn number_up_nimber(number: Dyadic<i64>, up: i64, nimber: u64) -> Self {
91 Self(GameValueNode::NumberUpNimber(number, up, nimber))
92 }
93
94 pub fn as_number(&self) -> Option<Dyadic<i64>> {
95 match self.0 {
96 GameValueNode::NumberUpNimber(n, 0, 0) => Some(n),
97 _ => None,
98 }
99 }
100
101 pub fn as_nimber(&self) -> Option<u64> {
102 match self.0 {
103 GameValueNode::NumberUpNimber(q, 0, n) if q.numerator() == 0 => Some(n),
104 _ => None,
105 }
106 }
107
108 pub fn as_number_nimber(&self) -> Option<(Dyadic<i64>, u64)> {
110 match self.0 {
111 GameValueNode::NumberUpNimber(q, 0, n) => Some((q, n)),
112 _ => None,
113 }
114 }
115
116 pub fn as_number_up_nimber(&self) -> Option<(Dyadic<i64>, i64, u64)> {
118 match self.0 {
119 GameValueNode::NumberUpNimber(q, k, n) => Some((q, k, n)),
120 _ => None,
121 }
122 }
123
124 #[allow(clippy::type_complexity)] pub fn as_switch_sum(&self) -> Option<(Dyadic<i64>, i64, u64, &[Dyadic<i64>])> {
128 match &self.0 {
129 GameValueNode::NumberUpNimber(q, k, n) => Some((*q, *k, *n, &[])),
130 GameValueNode::Switches(value) => Some((
131 value.number,
132 value.up,
133 value.nimber,
134 &value.temperatures[value.start..],
135 )),
136 _ => None,
137 }
138 }
139
140 pub fn outcome(&self) -> PartisanOutcome {
141 match self.partial_cmp(&Self::from(0)) {
142 Some(Ordering::Greater) => PartisanOutcome::Left,
143 Some(Ordering::Less) => PartisanOutcome::Right,
144 Some(Ordering::Equal) => PartisanOutcome::Previous,
145 None => PartisanOutcome::Next,
146 }
147 }
148
149 pub fn wins(&self, player: Player) -> bool {
151 match player {
152 Player::Left => !self.leq(&Self::from(0)),
153 Player::Right => !Self::from(0).leq(self),
154 }
155 }
156
157 pub fn from_options(
159 left: impl IntoIterator<Item = Self>,
160 right: impl IntoIterator<Item = Self>,
161 ) -> Self {
162 Self::from_options_memoized(
163 left.into_iter().collect(),
164 right.into_iter().collect(),
165 &mut HashMap::new(),
166 )
167 }
168
169 pub fn options(&self, player: Player) -> impl Iterator<Item = Self> + '_ {
170 let mut offset = Dyadic::from(0);
171 let (single, nimbers, general) = match &self.0 {
172 GameValueNode::NumberUpNimber(n, 0, 0) => {
173 let option = if n.exponent() == 0 {
174 match player {
175 Player::Left if n.numerator() > 0 => Some(Dyadic::from(n.numerator() - 1)),
176 Player::Right if n.numerator() < 0 => Some(Dyadic::from(n.numerator() + 1)),
177 _ => None,
178 }
179 } else {
180 let numerator = (n.numerator() >> 1) + i64::from(player == Player::Right);
181 Some(Dyadic::new(numerator, n.exponent() - 1))
182 };
183 (option.map(Self::from), None, &[][..])
184 }
185 GameValueNode::NumberUpNimber(q, 0, n) => (None, Some((*q, *n)), &[][..]),
186 GameValueNode::NumberUpNimber(q, k, 1)
187 if matches!((*k, player), (1, Player::Left) | (-1, Player::Right)) =>
188 {
189 (None, Some((*q, 2)), &[][..])
190 }
191 GameValueNode::NumberUpNimber(q, k, n) => {
192 let sign = if *k > 0 { 1 } else { -1 };
193 let moves_to_number = (*k > 0) == (player == Player::Left);
194 let option = if moves_to_number {
195 Self::from(*q)
196 } else {
197 Self::number_up_nimber(*q, k - sign, n ^ 1)
198 };
199 (Some(option), None, &[][..])
200 }
201 GameValueNode::Switches(value) => {
202 let temperatures = &value.temperatures[value.start..];
203 let t = if player == Player::Left {
204 temperatures[0]
205 } else {
206 -temperatures[0]
207 };
208 let option = if temperatures.len() == 1 {
209 Self::number_up_nimber(value.number + t, value.up, value.nimber)
210 } else {
211 Self(GameValueNode::Switches(Rc::new(GameValueSwitches {
212 number: value.number + t,
213 start: value.start + 1,
214 fingerprint: value.fingerprint ^ Self::temperature_hash(temperatures[0]),
215 ..(**value).clone()
216 })))
217 };
218 (Some(option), None, &[][..])
219 }
220 GameValueNode::Options(q, options) => {
221 offset = *q;
222 let (left, right) = &**options;
223 let moves = if player == Player::Left { left } else { right };
224 (None, None, moves.as_slice())
225 }
226 };
227 single
228 .into_iter()
229 .chain(
230 nimbers
231 .into_iter()
232 .flat_map(|(q, n)| (0..n).map(move |m| Self::number_up_nimber(q, 0, m))),
233 )
234 .chain(general.iter().map(move |g| g.translated(offset)))
235 }
236
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 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 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 }
378
379 fn leq(&self, rhs: &Self) -> bool {
380 self.leq_memoized(rhs, &mut HashMap::new())
381 }
382
383 fn leq_memoized(
384 &self,
385 rhs: &Self,
386 cache: &mut HashMap<GameValueNode, HashMap<GameValueNode, bool>>,
387 ) -> bool {
388 if let (Some((a, k, n, ts)), Some((b, l, m, us))) =
389 (self.as_switch_sum(), rhs.as_switch_sum())
390 {
391 let mut number = a;
394 let mut odd = false;
395 for t in Self::switch_difference(ts, us) {
396 if !odd && number >= b || odd && number <= b {
398 return odd;
399 }
400 number = if odd { number - t } else { number + t };
401 odd = !odd;
402 }
403 return match number.cmp(&b) {
404 Ordering::Less => true,
405 Ordering::Greater => false,
406 Ordering::Equal => {
407 let up = if odd { l - k } else { k - l };
408 let nimber = n ^ m;
409 let wins = up > 0 || up == 0 && nimber != 0 || up == -1 && nimber == 1;
410 wins == odd
411 }
412 };
413 }
414 if let (GameValueNode::Options(a, g), GameValueNode::Options(b, h)) = (&self.0, &rhs.0)
415 && Rc::ptr_eq(g, h)
416 {
417 return a <= b;
418 }
419 let offset = -self.number_offset();
421 let left = self.translated(offset).0;
422 let right = rhs.translated(offset).0;
423 if let Some(&value) = cache.get(&left).and_then(|row| row.get(&right)) {
424 return value;
425 }
426 let mut sides = [(self, rhs, Player::Left), (rhs, self, Player::Right)];
428 if self.as_switch_sum().is_some() {
429 sides.swap(0, 1);
430 }
431 let value = sides.into_iter().all(|(game, other, player)| {
432 let mut preferred = |a: &Self, b: &Self| match player {
433 Player::Left => b.leq_memoized(a, cache),
434 Player::Right => a.leq_memoized(b, cache),
435 };
436 if let GameValueNode::NumberUpNimber(q, 0, n) = game.0
437 && n != 0
438 {
439 let number = Self::from(q);
440 !preferred(&number, other)
441 && (preferred(other, &number)
442 || !game.options(player).skip(1).any(|g| preferred(&g, other)))
443 } else {
444 !game.options(player).any(|g| preferred(&g, other))
445 }
446 });
447 cache.entry(left).or_default().insert(right, value);
448 value
449 }
450
451 fn negated(&self, cache: &mut HashMap<usize, Self>) -> Self {
452 match &self.0 {
453 GameValueNode::NumberUpNimber(q, k, n) => Self::number_up_nimber(-*q, -*k, *n),
454 GameValueNode::Switches(value) => {
455 Self(GameValueNode::Switches(Rc::new(GameValueSwitches {
456 number: -value.number,
457 up: -value.up,
458 ..(**value).clone()
459 })))
460 }
461 GameValueNode::Options(q, options) => {
462 let key = Rc::as_ptr(options) as usize;
463 if let Some(value) = cache.get(&key) {
464 return value.translated(-*q);
465 }
466 let (left, right) = &**options;
467 let value = Self(GameValueNode::Options(
468 0.into(),
469 Rc::new((
470 right.iter().map(|g| g.negated(cache)).collect(),
471 left.iter().map(|g| g.negated(cache)).collect(),
472 )),
473 ));
474 cache.insert(key, value.clone());
475 value.translated(-*q)
476 }
477 }
478 }
479
480 fn sum(
481 &self,
482 rhs: &Self,
483 cache: &mut HashMap<(GameValueNode, GameValueNode), Self>,
484 comparisons: &mut HashMap<GameValueNode, HashMap<GameValueNode, bool>>,
485 ) -> Self {
486 if let (GameValueNode::NumberUpNimber(a, k, n), GameValueNode::NumberUpNimber(b, l, m)) =
487 (&self.0, &rhs.0)
488 {
489 return Self::number_up_nimber(*a + *b, k + l, n ^ m);
490 }
491 if let Some(q) = self.as_number() {
492 return rhs.translated(q);
493 }
494 if let Some(q) = rhs.as_number() {
495 return self.translated(q);
496 }
497 if let (Some((a, k, n, ts)), Some((b, l, m, us))) =
498 (self.as_switch_sum(), rhs.as_switch_sum())
499 {
500 return Self::from_switches(
501 a + b,
502 k + l,
503 n ^ m,
504 Self::switch_difference(ts, us).collect(),
505 self.switch_hash() ^ rhs.switch_hash(),
506 );
507 }
508 let (a, b) = (self.number_offset(), rhs.number_offset());
510 if a.numerator() != 0 || b.numerator() != 0 {
511 return self
512 .translated(-a)
513 .sum(&rhs.translated(-b), cache, comparisons)
514 .translated(a + b);
515 }
516 let mut key = (self.0.clone(), rhs.0.clone());
517 let swap = match (&key.0, &key.1) {
518 (GameValueNode::Options(_, a), GameValueNode::Options(_, b)) => {
519 Rc::as_ptr(a) > Rc::as_ptr(b)
520 }
521 (GameValueNode::Options(..), _) => true,
522 _ => false,
523 };
524 if swap {
525 std::mem::swap(&mut key.0, &mut key.1);
526 }
527 if let Some(value) = cache.get(&key) {
528 return value.clone();
529 }
530 let mut options = |player| {
531 let mut moves = Vec::new();
532 for g in self.options(player) {
533 moves.push(g.sum(rhs, cache, comparisons));
534 }
535 for g in rhs.options(player) {
536 moves.push(self.sum(&g, cache, comparisons));
537 }
538 moves
539 };
540 let value =
541 Self::from_options_memoized(options(Player::Left), options(Player::Right), comparisons);
542 cache.insert(key, value.clone());
543 value
544 }
545
546 fn number_offset(&self) -> Dyadic<i64> {
547 match &self.0 {
548 GameValueNode::NumberUpNimber(q, ..) | GameValueNode::Options(q, _) => *q,
549 GameValueNode::Switches(v) => v.number,
550 }
551 }
552
553 fn translated(&self, number: Dyadic<i64>) -> Self {
554 if number.numerator() == 0 {
555 return self.clone();
556 }
557 match &self.0 {
558 GameValueNode::NumberUpNimber(q, k, n) => Self::number_up_nimber(*q + number, *k, *n),
559 GameValueNode::Switches(v) => {
560 Self(GameValueNode::Switches(Rc::new(GameValueSwitches {
561 number: v.number + number,
562 ..(**v).clone()
563 })))
564 }
565 GameValueNode::Options(q, options) => {
566 Self(GameValueNode::Options(*q + number, options.clone()))
567 }
568 }
569 }
570
571 fn temperature_hash(t: Dyadic<i64>) -> u64 {
572 let mut hasher = DefaultHasher::new();
573 t.hash(&mut hasher);
574 hasher.finish()
575 }
576
577 fn switch_hash(&self) -> u64 {
578 match &self.0 {
579 GameValueNode::NumberUpNimber(..) => 0,
580 GameValueNode::Switches(v) => v.fingerprint,
581 GameValueNode::Options(..) => unreachable!(),
582 }
583 }
584
585 fn from_switches(
586 q: Dyadic<i64>,
587 k: i64,
588 n: u64,
589 temperatures: Vec<Dyadic<i64>>,
590 fingerprint: u64,
591 ) -> Self {
592 if temperatures.is_empty() {
593 Self::number_up_nimber(q, k, n)
594 } else {
595 Self(GameValueNode::Switches(Rc::new(GameValueSwitches {
596 number: q,
597 up: k,
598 nimber: n,
599 fingerprint,
600 temperatures: Rc::new(temperatures),
601 start: 0,
602 })))
603 }
604 }
605
606 fn switch_difference<'a>(
607 mut left: &'a [Dyadic<i64>],
608 mut right: &'a [Dyadic<i64>],
609 ) -> impl Iterator<Item = Dyadic<i64>> + 'a {
610 std::iter::from_fn(move || {
611 while let (Some(a), Some(b)) = (left.first(), right.first()) {
612 if a != b {
613 break;
614 }
615 left = &left[1..];
616 right = &right[1..];
617 }
618 if left.first() < right.first() {
619 std::mem::swap(&mut left, &mut right);
620 }
621 let (&value, tail) = left.split_first()?;
622 left = tail;
623 Some(value)
624 })
625 }
626
627 fn remove_dominated(
628 options: &mut Vec<Self>,
629 player: Player,
630 cache: &mut HashMap<GameValueNode, HashMap<GameValueNode, bool>>,
631 ) {
632 let mut retained = vec![true; options.len()];
633 for i in 0..options.len() {
634 let dominated = (0..options.len()).any(|j| {
635 i != j
636 && retained[j]
637 && match player {
638 Player::Left => options[i].leq_memoized(&options[j], cache),
639 Player::Right => options[j].leq_memoized(&options[i], cache),
640 }
641 });
642 retained[i] = !dominated;
643 }
644 let mut retained = retained.into_iter();
645 options.retain(|_| retained.next().unwrap());
646 }
647}
648
649impl From<i64> for GameValue {
650 fn from(value: i64) -> Self {
651 Dyadic::from(value).into()
652 }
653}
654impl From<Dyadic<i64>> for GameValue {
655 fn from(value: Dyadic<i64>) -> Self {
656 Self::number_up_nimber(value, 0, 0)
657 }
658}
659impl Neg for GameValue {
660 type Output = Self;
661 fn neg(self) -> Self {
662 self.negated(&mut HashMap::new())
663 }
664}
665impl Add for GameValue {
666 type Output = Self;
667 fn add(self, rhs: Self) -> Self {
668 self.sum(&rhs, &mut HashMap::new(), &mut HashMap::new())
669 }
670}
671impl Sub for GameValue {
672 type Output = Self;
673 fn sub(self, rhs: Self) -> Self {
674 if self.0 == rhs.0 {
675 Self::from(0)
676 } else {
677 self + -rhs
678 }
679 }
680}
681impl Sum for GameValue {
682 fn sum<I>(iter: I) -> Self
683 where
684 I: Iterator<Item = Self>,
685 {
686 let (mut number, mut up, mut nimber) = (Dyadic::from(0), 0, 0);
687 let mut temperatures = Vec::new();
688 let mut fingerprint = 0;
689 let mut general = Self::from(0);
690 for value in iter {
691 if let Some((q, k, n, ts)) = value.as_switch_sum() {
692 number = number + q;
693 up += k;
694 nimber ^= n;
695 temperatures.extend_from_slice(ts);
696 fingerprint ^= value.switch_hash();
697 } else {
698 general = general + value;
699 }
700 }
701 temperatures.sort_unstable_by(|a, b| b.cmp(a));
702 let mut retained = 0;
703 for i in 0..temperatures.len() {
704 if retained > 0 && temperatures[retained - 1] == temperatures[i] {
705 retained -= 1;
706 } else {
707 temperatures[retained] = temperatures[i];
708 retained += 1;
709 }
710 }
711 temperatures.truncate(retained);
712 let value = Self::from_switches(number, up, nimber, temperatures, fingerprint);
713 if general.as_nimber() == Some(0) {
714 value
715 } else {
716 value + general
717 }
718 }
719}
720impl PartialEq for GameValue {
721 fn eq(&self, rhs: &Self) -> bool {
722 match (&self.0, &rhs.0) {
723 (GameValueNode::NumberUpNimber(a, k, n), GameValueNode::NumberUpNimber(b, l, m)) => {
724 return a == b && k == l && n == m;
725 }
726 (GameValueNode::Switches(_), GameValueNode::Switches(_)) => {
727 return self.0 == rhs.0;
728 }
729 _ => {}
730 }
731 let mut cache = HashMap::new();
732 self.leq_memoized(rhs, &mut cache) && rhs.leq_memoized(self, &mut cache)
733 }
734}
735impl Eq for GameValue {}
736impl PartialOrd for GameValue {
737 fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
738 let mut cache = HashMap::new();
739 match (
740 self.leq_memoized(rhs, &mut cache),
741 rhs.leq_memoized(self, &mut cache),
742 ) {
743 (true, true) => Some(Ordering::Equal),
744 (true, false) => Some(Ordering::Less),
745 (false, true) => Some(Ordering::Greater),
746 (false, false) => None,
747 }
748 }
749}
750impl fmt::Display for GameValue {
751 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
752 if let Some((q, k, n, temperatures)) = self.as_switch_sum() {
753 let mut separator = "";
754 if q.numerator() != 0 || k == 0 && n == 0 && temperatures.is_empty() {
755 write!(f, "{q}")?;
756 separator = "+";
757 }
758 if k != 0 {
759 f.write_str(separator)?;
760 if k.unsigned_abs() != 1 {
761 write!(f, "{}", k.unsigned_abs())?;
762 }
763 f.write_str(if k > 0 { "↑" } else { "↓" })?;
764 separator = "+";
765 }
766 if n != 0 {
767 write!(f, "{separator}*")?;
768 if n != 1 {
769 write!(f, "{n}")?;
770 }
771 separator = "+";
772 }
773 for t in temperatures {
774 write!(f, "{separator}{{{t}|{}}}", -*t)?;
775 separator = "+";
776 }
777 return Ok(());
778 }
779 f.write_str("{")?;
780 for (separator, player) in [("", Player::Left), ("|", Player::Right)] {
781 f.write_str(separator)?;
782 for (i, g) in self.options(player).enumerate() {
783 if i > 0 {
784 f.write_str(",")?;
785 }
786 write!(f, "{g}")?;
787 }
788 }
789 f.write_str("}")
790 }
791}
792impl fmt::Debug for GameValue {
793 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
794 fmt::Display::fmt(self, f)
795 }
796}
797
798#[cfg(test)]
799mod tests {
800 use super::*;
801 use crate::tools::{
802 Xorshift,
803 testutil::{integer_boundary_values, sequence_cases},
804 };
805
806 #[test]
807 fn number_nimbers() {
808 let mut rng = Xorshift::default();
809 let nimbers: Vec<_> = integer_boundary_values!(u64)
810 .into_iter()
811 .chain(rng.random_iter(..).take(1024))
812 .collect();
813 for n in nimbers {
814 let random = Dyadic::new(rng.random(-8192..=8192), rng.random(0..=8));
815 for q in [
816 0.into(),
817 (-8192).into(),
818 8192.into(),
819 Dyadic::new(-1, 1),
820 Dyadic::new(1, 1),
821 random,
822 ] {
823 let value = GameValue::from(q) + GameValue::nimber(n);
824 assert_eq!(
825 GameValue::from_options([q.into()], [q.into()]).as_number_nimber(),
826 Some((q, 1))
827 );
828 assert_eq!(value.as_number_nimber(), Some((q, n)));
829 assert_eq!(value.as_number(), (n == 0).then_some(q));
830 assert_eq!(value.as_nimber(), (q.numerator() == 0).then_some(n));
831 let prefix = if n != 0 && q.numerator() != 0 {
832 format!("{q}+")
833 } else {
834 String::new()
835 };
836 let suffix = match n {
837 0 => q.to_string(),
838 1 => "*".to_owned(),
839 _ => format!("*{n}"),
840 };
841 assert_eq!(value.to_string(), prefix + &suffix);
842 assert_eq!((-value.clone()).as_number_nimber(), Some((-q, n)));
843 assert_eq!(value.clone() - value.clone(), 0.into());
844 let m = rng.rand64();
845 let r = Dyadic::new(rng.random(-8192..=8192), rng.random(0..=8));
846 let other = GameValue::from(r) + GameValue::nimber(m);
847 assert_eq!(
848 (value.clone() + other.clone()).as_number_nimber(),
849 Some((q + r, n ^ m))
850 );
851 assert_eq!(
852 (value.clone() - other).as_number_nimber(),
853 Some((q - r, n ^ m))
854 );
855 if n != 0 {
856 for player in [Player::Left, Player::Right] {
857 assert!(
858 value
859 .options(player)
860 .take(3)
861 .enumerate()
862 .all(|(m, g)| { g.as_number_nimber() == Some((q, m as u64)) })
863 );
864 assert_eq!(value.options(player).take(3).count(), n.min(3) as usize);
865 }
866 }
867 for k in [-8192, -2, -1, 0, 1, 2, 8192, rng.random(-8192..=8192)] {
868 let value = GameValue::number_up_nimber(q, k, n);
869 let other = GameValue::number_up_nimber(r, -k, m);
870 assert_eq!((-value.clone()).as_number_up_nimber(), Some((-q, -k, n)));
871 assert_eq!(
872 (value.clone() + other).as_number_nimber(),
873 Some((q + r, n ^ m))
874 );
875 if k != 0 {
876 assert_eq!(
877 GameValue::from_options(
878 value.options(Player::Left),
879 value.options(Player::Right)
880 )
881 .as_number_up_nimber(),
882 Some((q, k, n)),
883 );
884 }
885 }
886 }
887 if n != 0 {
888 let star = GameValue::nimber(n);
889 let switch = GameValue::from_options([1.into()], [(-1).into()]);
890 assert_eq!(star.partial_cmp(&switch), None);
891 assert_eq!(switch.partial_cmp(&star), None);
892 let up = GameValue::from_options([0.into()], [star]);
893 assert!(up > GameValue::from(0));
894 assert_eq!(up.clone() - up, 0.into());
895 }
896 }
897 }
898
899 #[test]
900 fn order_and_sum_against_play() {
901 let mut rng = Xorshift::default();
902 let mut games = vec![
903 (vec![], vec![]),
904 (vec![0], vec![]),
905 (vec![], vec![0]),
906 (vec![0], vec![0]),
907 ];
908 for l in 0..16 {
910 for r in 0..16 {
911 games.push((
912 (0..4).filter(|&i| l >> i & 1 != 0).collect(),
913 (0..4).filter(|&i| r >> i & 1 != 0).collect(),
914 ));
915 }
916 }
917 for n in games.len()..games.len() + 32 {
918 games.push((
919 (0..3).map(|_| rng.random(0..n)).collect(),
920 (0..3).map(|_| rng.random(0..n)).collect(),
921 ));
922 }
923 let mut numbers = vec![(0i64, Vec::new(), Vec::new())];
925 let mut indices = HashMap::from([(0, 0)]);
926 for step in [2, 1] {
927 for q in (step..=4).step_by(step as usize).flat_map(|q| [-q, q]) {
928 if indices.contains_key(&q) {
929 continue;
930 }
931 let left = (step == 1 || q > 0)
932 .then(|| indices[&(q - step)])
933 .into_iter()
934 .collect();
935 let right = (step == 1 || q < 0)
936 .then(|| indices[&(q + step)])
937 .into_iter()
938 .collect();
939 indices.insert(q, numbers.len());
940 numbers.push((q, left, right));
941 }
942 }
943 let start = games.len();
944 let mut compact = Vec::new();
945 for (i, &(q, ref left, ref right)) in numbers.iter().enumerate() {
946 for n in 0..8 {
947 games.push((
948 left.iter()
949 .map(|j| start + j * 8 + n)
950 .chain((0..n).map(|m| start + i * 8 + m))
951 .collect(),
952 right
953 .iter()
954 .map(|j| start + j * 8 + n)
955 .chain((0..n).map(|m| start + i * 8 + m))
956 .collect(),
957 ));
958 compact.push((games.len() - 1, (Dyadic::new(q, 1), n as u64)));
959 }
960 }
961 let symmetric = games.len();
962 for (i, &(q, _, _)) in numbers.iter().enumerate() {
963 for mask in 1..16 {
964 let left: Vec<_> = (0..4)
965 .filter(|n| mask >> n & 1 != 0)
966 .flat_map(|n| [start + i * 8 + n; 2])
967 .collect();
968 games.push((left.clone(), left.into_iter().rev().collect()));
969 let mex = (0..).find(|n| mask >> n & 1 == 0).unwrap();
970 compact.push((games.len() - 1, (Dyadic::new(q, 1), mex)));
971 }
972 }
973 for i in 0..numbers.len() {
974 for n in 0..8 {
975 for m in 0..8 {
976 games.push((vec![start + i * 8 + n], vec![start + i * 8 + m]));
977 }
978 }
979 }
980 let up = games.len();
981 games.push((vec![0], vec![3]));
982 let down = games.len();
983 games.push((vec![3], vec![0]));
984 let inner = games.len();
985 games.push((vec![0], vec![2]));
986 let tiny = games.len();
987 games.push((vec![0], vec![inner]));
988 let inner = games.len();
989 games.push((vec![1], vec![0]));
990 let miny = games.len();
991 games.push((vec![inner], vec![0]));
992 let products_start = games.len();
993 let mut components = vec![0, 1, 2, 3, up, down, start + 2];
994 let mut switches = vec![0];
995 for t in 1..=3 {
997 let a = start + indices[&t] * 8;
998 let b = start + indices[&-t] * 8;
999 let switch = games.len();
1000 games.push((vec![a], vec![b]));
1001 components.extend([a, b, switch]);
1002 switches.push(switch);
1003 }
1004 let mut states: Vec<Vec<_>> = sequence_cases(&mut rng, 0..=components.len() - 1, 0..=5)
1005 .map(|s| s.into_iter().map(|i| components[i]).collect())
1006 .collect();
1007 for (a, &first) in switches.iter().enumerate() {
1009 for (b, &second) in switches[..=a].iter().enumerate() {
1010 for q in [a as i64 - b as i64, b as i64 - a as i64] {
1011 for u in [0, up, down] {
1012 for n in [0, 3] {
1013 states.push(vec![start + indices[&q] * 8, u, n, first, second]);
1014 }
1015 }
1016 }
1017 }
1018 }
1019 for &component in &components {
1020 let parts = [tiny, component, miny, component, tiny];
1021 states.extend((3..=5).map(|len| parts[..len].to_vec()));
1022 }
1023 states.extend(
1025 (260..start)
1026 .chain([tiny, miny])
1027 .flat_map(|g| (0..numbers.len()).map(move |i| vec![g, start + i * 8])),
1028 );
1029 let switch_products_start = states.len();
1030 states.extend(
1031 (0usize..1 << (switches.len() - 1))
1032 .filter(|mask| mask.count_ones() > 1)
1033 .map(|mask| {
1034 switches[1..]
1035 .iter()
1036 .enumerate()
1037 .filter_map(|(i, &s)| (mask >> i & 1 != 0).then_some(s))
1038 .collect()
1039 }),
1040 );
1041 let mut products = Vec::new();
1042 let mut product_cache = HashMap::new();
1043 for parts in states {
1044 let mut ordered = parts.clone();
1045 ordered.sort_unstable();
1046 let root = ordered
1047 .iter()
1048 .fold(0, |a, &b| sum_options(&mut games, a, b, &mut product_cache));
1049 products.push((root, parts));
1050 }
1051 let mut values: Vec<GameValue> = Vec::new();
1052 for (left, right) in &games {
1053 values.push(GameValue::from_options(
1054 left.iter().map(|&i| values[i].clone()),
1055 right.iter().map(|&i| values[i].clone()),
1056 ));
1057 }
1058 for (i, expected) in compact {
1059 assert_eq!(values[i].as_number_nimber(), Some(expected));
1060 }
1061 assert!(values[tiny].as_switch_sum().is_none());
1062 for (root, parts) in &products {
1063 let sum: GameValue = parts.iter().map(|&i| values[i].clone()).sum();
1064 assert_eq!(sum, values[*root]);
1065 if parts.iter().all(|&i| values[i].as_switch_sum().is_some()) {
1066 assert!(sum.as_switch_sum().is_some());
1067 assert!(values[*root].as_switch_sum().is_some(), "{}", values[*root]);
1068 }
1069 let reversed: GameValue = parts.iter().rev().map(|&i| values[i].clone()).sum();
1070 assert_eq!(sum, reversed);
1071 let folded = parts
1072 .iter()
1073 .fold(GameValue::from(0), |s, &i| s + values[i].clone());
1074 assert_eq!(sum, folded);
1075 values[*root] = sum;
1077 }
1078 let mut oracle_games = Vec::new();
1080 let mut oracle_ids = Vec::with_capacity(games.len());
1081 let mut oracle_nodes = HashMap::new();
1082 for (left, right) in &games {
1083 let [left, right] = [left, right].map(|moves| {
1084 let mut moves: Vec<_> = moves.iter().map(|&i| oracle_ids[i]).collect();
1085 moves.sort_unstable();
1086 moves.dedup();
1087 moves
1088 });
1089 let id = *oracle_nodes
1090 .entry((left, right))
1091 .or_insert_with_key(|options| {
1092 let id = oracle_games.len();
1093 oracle_games.push(options.clone());
1094 id
1095 });
1096 oracle_ids.push(id);
1097 }
1098 drop(oracle_nodes);
1099 let zero = GameValue::from(0);
1100 let mut differences = HashMap::new();
1101 let mut sums = HashMap::new();
1102 let mut roots: Vec<_> = (0..products_start)
1103 .chain(products.iter().map(|&(i, _)| i))
1104 .collect();
1105 roots.sort_unstable();
1107 roots.dedup();
1108 for i in roots {
1109 let g = &values[i];
1110 let (left, right) = &games[i];
1111 let candidate = GameValue(GameValueNode::Options(
1112 0.into(),
1113 Rc::new((
1114 left.iter().map(|&j| values[j].clone()).collect(),
1115 right.iter().map(|&j| values[j].clone()).collect(),
1116 )),
1117 ));
1118 let winning = [false, true].map(|right| {
1119 wins(
1120 &oracle_games,
1121 oracle_ids[i],
1122 oracle_ids[0],
1123 right,
1124 false,
1125 &mut sums,
1126 )
1127 });
1128 for (player, win) in [Player::Left, Player::Right].into_iter().zip(winning) {
1129 assert_eq!(g.wins(player), win);
1130 let options: Vec<_> = g.options(player).collect();
1131 for (j, option) in options.iter().enumerate() {
1132 if let Some((q, k, n, ts)) = option.as_switch_sum() {
1133 let rebuilt: GameValue =
1134 std::iter::once(GameValue::number_up_nimber(q, k, n))
1135 .chain(
1136 ts.iter().map(|&t| {
1137 GameValue::from_options([t.into()], [(-t).into()])
1138 }),
1139 )
1140 .sum();
1141 let memo = HashMap::from([(option.0.clone(), ())]);
1142 assert!(memo.contains_key(&rebuilt.0));
1143 }
1144 for other in options.iter().skip(j + 1) {
1145 assert_eq!(option.partial_cmp(other), None, "dominated options in {g}");
1146 }
1147 for reply in option.options(player.opponent()) {
1148 let order = reply.partial_cmp(g);
1149 assert_ne!(order, Some(Ordering::Equal), "reversible option in {g}");
1150 assert_ne!(
1151 order,
1152 Some(if player == Player::Left {
1153 Ordering::Less
1154 } else {
1155 Ordering::Greater
1156 }),
1157 "reversible option in {g}"
1158 );
1159 }
1160 }
1161 }
1162 let outcome = match winning {
1163 [true, true] => PartisanOutcome::Next,
1164 [false, false] => PartisanOutcome::Previous,
1165 [true, false] => PartisanOutcome::Left,
1166 [false, true] => PartisanOutcome::Right,
1167 };
1168 assert_eq!(g.outcome(), outcome);
1169 assert_eq!(g.clone() - g.clone(), zero, "G-G for {i}: {g}");
1170 assert_eq!(g.clone() + -g.clone(), zero);
1171 assert_eq!(-(-g.clone()), *g);
1172 let mut partners: Vec<_> = if i < 260 {
1173 (0..260).collect()
1174 } else if i >= products_start {
1175 components
1176 .iter()
1177 .copied()
1178 .chain([tiny])
1179 .chain(products[switch_products_start..].iter().map(|&(i, _)| i))
1180 .collect()
1181 } else if i >= start {
1182 (start..symmetric).collect()
1183 } else {
1184 (0..32).map(|_| rng.random(0..products_start)).collect()
1185 };
1186 if (260..start).contains(&i) {
1187 partners.push(i);
1188 }
1189 for j in partners {
1190 let expected = match (
1191 wins(
1192 &oracle_games,
1193 oracle_ids[i],
1194 oracle_ids[j],
1195 false,
1196 true,
1197 &mut differences,
1198 ),
1199 wins(
1200 &oracle_games,
1201 oracle_ids[i],
1202 oracle_ids[j],
1203 true,
1204 true,
1205 &mut differences,
1206 ),
1207 ) {
1208 (false, false) => Some(Ordering::Equal),
1209 (true, false) => Some(Ordering::Greater),
1210 (false, true) => Some(Ordering::Less),
1211 (true, true) => None,
1212 };
1213 assert_eq!(
1214 g.partial_cmp(&values[j]),
1215 expected,
1216 "{i} vs {j}: {g}, {}",
1217 values[j]
1218 );
1219 assert_eq!(
1220 candidate.partial_cmp(&values[j]),
1221 expected,
1222 "raw options {i} vs {j}"
1223 );
1224 assert_eq!(g == &values[j], expected == Some(Ordering::Equal));
1225 if (i < 260 && j >= 4) || (symmetric..products_start).contains(&i) {
1226 continue;
1227 }
1228 assert_eq!((g.clone() - values[j].clone()).partial_cmp(&zero), expected);
1229 let sum = g.clone() + values[j].clone();
1230 assert_eq!(sum, values[j].clone() + g.clone());
1231 let collected: GameValue = [g.clone(), values[j].clone()].into_iter().sum();
1232 assert_eq!(sum, collected);
1233 for player in [Player::Left, Player::Right] {
1234 assert_eq!(
1235 sum.wins(player),
1236 wins(
1237 &oracle_games,
1238 oracle_ids[i],
1239 oracle_ids[j],
1240 player == Player::Right,
1241 false,
1242 &mut sums
1243 ),
1244 "{i} + {j}, {player:?}"
1245 );
1246 }
1247 }
1248 }
1249 }
1250
1251 fn sum_options(
1253 games: &mut Vec<(Vec<usize>, Vec<usize>)>,
1254 a: usize,
1255 b: usize,
1256 cache: &mut HashMap<(usize, usize), usize>,
1257 ) -> usize {
1258 if a == 0 {
1259 return b;
1260 }
1261 if b == 0 {
1262 return a;
1263 }
1264 let key = (a.min(b), a.max(b));
1265 if let Some(&i) = cache.get(&key) {
1266 return i;
1267 }
1268 let (al, ar) = games[a].clone();
1269 let (bl, br) = games[b].clone();
1270 let mut options = [Vec::new(), Vec::new()];
1271 for (moves, (left, right)) in options.iter_mut().zip([(al, bl), (ar, br)]) {
1272 moves.extend(left.into_iter().map(|c| sum_options(games, c, b, cache)));
1273 moves.extend(right.into_iter().map(|c| sum_options(games, a, c, cache)));
1274 }
1275 let i = games.len();
1276 let [left, right] = options;
1277 games.push((left, right));
1278 cache.insert(key, i);
1279 i
1280 }
1281
1282 fn wins(
1284 games: &[(Vec<usize>, Vec<usize>)],
1285 a: usize,
1286 b: usize,
1287 right: bool,
1288 subtract: bool,
1289 cache: &mut HashMap<(usize, usize, bool), bool>,
1290 ) -> bool {
1291 let key = (a.min(b), a.max(b), right ^ (subtract && a > b));
1293 if let Some(&result) = cache.get(&key) {
1294 return result;
1295 }
1296 let first = if right { &games[a].1 } else { &games[a].0 };
1297 let second = if right ^ subtract {
1298 &games[b].1
1299 } else {
1300 &games[b].0
1301 };
1302 let result = first
1303 .iter()
1304 .any(|&c| !wins(games, c, b, !right, subtract, cache))
1305 || second
1306 .iter()
1307 .any(|&c| !wins(games, a, c, !right, subtract, cache));
1308 cache.insert(key, result);
1309 result
1310 }
1311}