pub struct Dyadic<T> {
numerator: T,
exponent: u32,
}Expand description
A reduced numerator / 2^exponent.
Fields§
§numerator: T§exponent: u32Implementations§
Source§impl<T> Dyadic<T>
impl<T> Dyadic<T>
Sourcepub fn numerator(self) -> T
pub fn numerator(self) -> T
Examples found in repository?
crates/competitive/src/algorithm/game_analyzer/value.rs (line 65)
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 // XOR of the remaining temperatures' hashes; equality still checks the contents.
81 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 /// Constructs `number + up * ↑ + *nimber` without expanding options.
90 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 /// Returns `(q, n)` when the value is `q + *n`.
109 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 /// Returns `(q, k, n)` when the value is `q + k↑ + *n`.
117 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 /// Returns `(q, k, n, temperatures)` for `q + k↑ + *n + Σ{t|-t}`.
125 /// Temperatures are positive and strictly decreasing; the slice may be empty.
126 #[allow(clippy::type_complexity)] // Borrows the components without a separate public type.
127 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 /// Whether the selected starting player can force a win.
150 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 /// Removes dominated and reversible options.
158 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 // 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 }
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 // Both players take the hottest remaining switch. At zero, the tail
392 // decides the winner with the turn reversed once per switch.
393 let mut number = a;
394 let mut odd = false;
395 for t in Self::switch_difference(ts, us) {
396 // The unplayed alternating tail has the sign of the next term.
397 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 // Normalize only memo keys: from_options also compares unreduced candidates.
420 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 // Try explicit options before expanding a compact value.
427 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 // Cache the sum of the untranslated parts, sharing it across numeric offsets.
509 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 }Sourcepub fn exponent(self) -> u32
pub fn exponent(self) -> u32
Examples found in repository?
crates/competitive/src/algorithm/game_analyzer/value.rs (line 173)
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 // 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 }Source§impl<T> Dyadic<T>where
T: Signed,
impl<T> Dyadic<T>where
T: Signed,
Sourcepub fn new(numerator: T, exponent: u32) -> Self
pub fn new(numerator: T, exponent: u32) -> Self
Examples found in repository?
crates/competitive/src/num/dyadic.rs (line 68)
46 pub fn simplest_between(left: Option<Self>, right: Option<Self>) -> Option<Self> {
47 if left.zip(right).is_some_and(|(l, r)| l >= r) {
48 return None;
49 }
50 if left.is_none_or(|l| l.numerator < T::zero())
51 && right.is_none_or(|r| r.numerator > T::zero())
52 {
53 return Some(Self::zero());
54 }
55 let negative = right.is_some_and(|r| r.numerator <= T::zero());
56 let (lower, upper) = if negative {
57 (-right.unwrap(), left.map(Neg::neg))
58 } else {
59 (left.unwrap(), right)
60 };
61 let two = T::one() + T::one();
62 for exponent in 0.. {
63 let floor = if exponent < lower.exponent {
64 lower.numerator / two.pow(lower.exponent - exponent)
65 } else {
66 lower.numerator * two.pow(exponent - lower.exponent)
67 };
68 let value = Self::new(floor + T::one(), exponent);
69 if upper.is_none_or(|u| value < u) {
70 return Some(if negative { -value } else { value });
71 }
72 }
73 unreachable!()
74 }
75}
76
77impl<T> From<T> for Dyadic<T>
78where
79 T: Signed,
80{
81 fn from(numerator: T) -> Self {
82 Self::new(numerator, 0)
83 }
84}
85impl<T> Zero for Dyadic<T>
86where
87 T: Signed,
88{
89 fn zero() -> Self {
90 T::zero().into()
91 }
92}
93impl<T> One for Dyadic<T>
94where
95 T: Signed,
96{
97 fn one() -> Self {
98 T::one().into()
99 }
100}
101impl<T> Default for Dyadic<T>
102where
103 T: Signed,
104{
105 fn default() -> Self {
106 Self::zero()
107 }
108}
109impl<T> Neg for Dyadic<T>
110where
111 T: Signed,
112{
113 type Output = Self;
114 fn neg(self) -> Self {
115 Self {
116 numerator: -self.numerator,
117 exponent: self.exponent,
118 }
119 }
120}
121impl<T> Add for Dyadic<T>
122where
123 T: Signed,
124{
125 type Output = Self;
126 fn add(self, rhs: Self) -> Self {
127 let exponent = self.exponent.max(rhs.exponent);
128 let two = T::one() + T::one();
129 let left = self.numerator * two.pow(exponent - self.exponent);
130 let right = rhs.numerator * two.pow(exponent - rhs.exponent);
131 Self::new(left + right, exponent)
132 }More examples
crates/competitive/src/algorithm/game_analyzer/value.rs (line 181)
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 // 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 }Sourcepub fn simplest_between(left: Option<Self>, right: Option<Self>) -> Option<Self>
pub fn simplest_between(left: Option<Self>, right: Option<Self>) -> Option<Self>
The simplest number strictly between the bounds; absent bounds are unbounded.
Returns None for an empty interval.
Examples found in repository?
crates/competitive/src/algorithm/game_analyzer/value.rs (line 246)
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 }Trait Implementations§
impl<T: Copy> Copy for Dyadic<T>
impl<T: Eq> Eq for Dyadic<T>
Source§impl<T> Ord for Dyadic<T>where
T: Signed,
impl<T> Ord for Dyadic<T>where
T: Signed,
1.21.0 (const: unstable) · Source§fn max(self, other: Self) -> Selfwhere
Self: Sized,
fn max(self, other: Self) -> Selfwhere
Self: Sized,
Compares and returns the maximum of two values. Read more
1.21.0 (const: unstable) · Source§fn min(self, other: Self) -> Selfwhere
Self: Sized,
fn min(self, other: Self) -> Selfwhere
Self: Sized,
Compares and returns the minimum of two values. Read more
Source§impl<T> PartialOrd for Dyadic<T>where
T: Signed,
impl<T> PartialOrd for Dyadic<T>where
T: Signed,
impl<T: PartialEq> StructuralPartialEq for Dyadic<T>
Auto Trait Implementations§
impl<T> Forget for Dyadic<T>
impl<T> Freeze for Dyadic<T>where
T: Freeze,
impl<T> Move for Dyadic<T>
impl<T> RefUnwindSafe for Dyadic<T>where
T: RefUnwindSafe,
impl<T> Send for Dyadic<T>where
T: Send,
impl<T> Sync for Dyadic<T>where
T: Sync,
impl<T> Unpin for Dyadic<T>where
T: Unpin,
impl<T> UnsafeUnpin for Dyadic<T>where
T: UnsafeUnpin,
impl<T> UnwindSafe for Dyadic<T>where
T: UnwindSafe,
Blanket Implementations§
Source§impl<T> AsTotalOrd for Twhere
T: PartialOrd,
impl<T> AsTotalOrd for Twhere
T: PartialOrd,
fn as_total_ord(&self) -> TotalOrd<&T>
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