1use core::{cmp::Ordering, fmt, iter::FusedIterator, ops::Deref, slice};
2
3use crate::position::Evaluation;
4
5use super::*;
6
7#[derive(Debug, thiserror::Error)]
9pub enum Error {
10 #[error("position slot {0} not found")]
11 Position(PositionSlot),
12 #[error("move slot {0} not found")]
13 Move(MoveSlot),
14 #[error(transparent)]
15 Option(#[from] Missing),
16}
17
18pub type Result<T, E = Error> = core::result::Result<T, E>;
20
21pub trait Choose: Into<Choice> {
23 fn choose(self, position: PositionRef<'_>) -> Result<usize, Missing>;
24}
25
26#[derive(Clone, Copy, Eq, PartialEq)]
30#[non_exhaustive]
31pub enum Choice {
32 Index(usize),
33 Move(chess::Move),
34 Slot(MoveSlot),
35}
36
37#[derive(Debug, thiserror::Error)]
39#[error("option {choice:?} not found in position slot {position}")]
40pub struct Missing {
41 pub position: PositionSlot,
42 pub choice: Choice,
43}
44
45impl fmt::Debug for Choice {
46 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47 match self {
48 Self::Index(index) => write!(f, "Index({index})"),
49 Self::Move(play) => write!(f, "Move({play})"),
50 Self::Slot(slot) => write!(f, "Slot({slot})"),
51 }
52 }
53}
54
55impl From<usize> for Choice {
56 fn from(index: usize) -> Self {
57 Self::Index(index)
58 }
59}
60
61impl From<MoveSlot> for Choice {
62 fn from(slot: MoveSlot) -> Self {
63 Self::Slot(slot)
64 }
65}
66
67impl From<chess::Move> for Choice {
68 fn from(play: chess::Move) -> Self {
69 Self::Move(play)
70 }
71}
72
73#[derive(Clone, Copy)]
75pub struct Order<'g> {
76 game: &'g Game,
77 pub slot: PositionSlot,
78}
79
80#[derive(Clone)]
82pub struct Moves<'g> {
83 game: &'g Game,
84 slots: slice::Iter<'g, MoveSlot>,
85}
86
87pub struct MovesMut<'g> {
91 game: &'g mut Game,
92 position: PositionSlot,
93 index: usize,
94 visited: Set<MoveSlot>,
95 protected: Protected,
96}
97
98impl Game {
99 pub fn play(&self, slot: MoveSlot) -> Result<MoveRef<'_>> {
100 self.tree
101 .contains(slot.into())
102 .then_some(MoveRef { game: self, slot })
103 .ok_or(Error::Move(slot))
104 }
105
106 pub fn play_mut(&mut self, slot: MoveSlot) -> Result<MoveMut<'_>> {
107 if !self.tree.contains(slot.into()) {
108 return Err(Error::Move(slot));
109 }
110 Ok(MoveMut::new(self, slot))
111 }
112
113 pub fn position(&self, slot: PositionSlot) -> Result<PositionRef<'_>> {
114 self.tree
115 .contains(slot)
116 .then_some(PositionRef { game: self, slot })
117 .ok_or(Error::Position(slot))
118 }
119
120 pub fn position_mut(&mut self, slot: PositionSlot) -> Result<PositionMut<'_>> {
121 if !self.tree.contains(slot) {
122 return Err(Error::Position(slot));
123 }
124 Ok(PositionMut::new(self, slot))
125 }
126
127 pub const fn start(&self) -> PositionRef<'_> {
128 PositionRef { game: self, slot: START }
129 }
130
131 pub const fn start_mut(&mut self) -> PositionMut<'_> {
132 PositionMut::new(self, START)
133 }
134
135 pub const fn into_cursor(self) -> Cursor {
136 Cursor::new(self)
137 }
138
139 pub const fn cursor(&self) -> CursorRef<'_> {
140 Cursor { game: self, slot: START, preserve: Set::new() }
141 }
142
143 pub const fn cursor_mut(&mut self) -> CursorMut<'_> {
144 Cursor { game: self, slot: START, preserve: Set::new() }
145 }
146
147 pub const fn order(&self, slot: PositionSlot) -> Order<'_> {
148 Order { game: self, slot }
149 }
150}
151
152impl<'g> PositionRef<'g> {
154 pub const fn slot(&self) -> PositionSlot {
155 self.slot
156 }
157
158 pub const fn game(&self) -> &'g Game {
159 self.game
160 }
161
162 pub fn into_game(self) -> Game {
163 Game::unchecked_with_mode_next(
164 self.position().position(),
165 self.game.mode(),
166 self.game.tree.next(),
167 )
168 }
169
170 pub fn index(&self, option: impl Choose) -> Result<usize> {
171 Ok(option.choose(*self)?)
172 }
173
174 pub fn has_option(&self, option: impl Choose) -> bool {
175 self.index(option).is_ok()
176 }
177
178 pub(in crate::game) fn position(&self) -> &'g Position {
179 self.game.tree.position(self.slot)
180 }
181}
182
183impl<'g> PositionRef<'g> {
185 pub const fn cursor(self) -> CursorRef<'g> {
186 Cursor { game: self.game, slot: self.slot, preserve: Set::new() }
187 }
188
189 pub const fn play(&self) -> start::Result<MoveRef<'g>> {
190 match self.slot {
191 PositionSlot::Move(slot) => Ok(MoveRef { game: self.game, slot }),
192 PositionSlot::Start => Err(start::Error),
193 }
194 }
195
196 pub fn main(&self) -> Result<MoveRef<'g>> {
197 self.option(0)
198 }
199
200 pub fn option(&self, option: impl Choose) -> Result<MoveRef<'g>> {
201 let index = self.index(option)?;
202 let slot = self.position().options[index];
203 Ok(MoveRef { game: self.game, slot })
204 }
205
206 pub fn has_options(&self) -> bool {
207 !self.position().options.is_empty()
208 }
209
210 pub fn has_alternatives(&self) -> bool {
211 self.position().options.len() >= 2
212 }
213
214 pub fn alternatives(&self) -> Moves<'g> {
215 let slots = self.position().options.get(1..).unwrap_or_default().iter();
216 Moves { game: self.game, slots }
217 }
218
219 pub fn iter(&self) -> Moves<'g> {
220 Moves { game: self.game, slots: self.position().options.iter() }
221 }
222}
223
224impl Deref for PositionRef<'_> {
225 type Target = Position;
226
227 fn deref(&self) -> &Position {
228 self.position()
229 }
230}
231
232impl<'g> PositionMut<'g> {
234 const fn new(game: &'g mut Game, slot: PositionSlot) -> Self {
235 Self { game, slot, protected: Protected::new() }
236 }
237
238 pub const fn slot(&self) -> PositionSlot {
239 self.slot
240 }
241
242 pub const fn game(&self) -> &Game {
243 self.game
244 }
245
246 pub const fn as_ref(&self) -> PositionRef<'_> {
247 PositionRef { game: self.game, slot: self.slot }
248 }
249
250 pub fn into_ref(self) -> PositionRef<'g> {
251 PositionRef { game: self.game, slot: self.slot }
252 }
253
254 pub fn set_comment(&mut self, comment: Option<Text>) {
255 self.update_public(|public| public.comment = comment);
256 }
257
258 pub fn set_evaluation(&mut self, evaluation: Option<Evaluation>) {
259 self.position_mut().evaluation = evaluation;
260 }
261
262 pub fn set_expanded(&mut self, expanded: bool) {
263 self.update_public(|public| public.expanded = expanded);
264 }
265
266 pub fn update_public(&mut self, update: impl FnOnce(&mut PositionPublic)) {
267 let before = self.game.tracks_changes().then(|| self.public.clone());
268 update(&mut self.position_mut().public);
269 if let Some(before) = before
270 && before != self.public
271 {
272 self.game.track(change::Patch::Position { slot: self.slot, public: Box::new(before) });
273 }
274 }
275
276 pub(in crate::game) fn position(&self) -> &Position {
277 self.game.tree.position(self.slot)
278 }
279
280 pub(in crate::game) fn position_mut(&mut self) -> &mut Position {
281 self.game.tree.position_mut(self.slot)
282 }
283}
284
285impl<'g> PositionMut<'g> {
287 pub fn cursor_mut(&mut self) -> CursorMut<'_> {
288 let mut preserve = self.protected.unremovable.clone();
289 preserve.insert(self.slot);
290 Cursor { game: self.game, slot: self.slot, preserve }
291 }
292
293 pub fn into_cursor_mut(self) -> CursorMut<'g> {
294 Cursor { game: self.game, slot: self.slot, preserve: self.protected.unremovable }
295 }
296
297 pub const fn play(&self) -> start::Result<MoveRef<'_>> {
298 match self.slot {
299 PositionSlot::Move(slot) => Ok(MoveRef { game: self.game, slot }),
300 PositionSlot::Start => Err(start::Error),
301 }
302 }
303
304 pub fn play_mut(&mut self) -> start::Result<MoveMut<'_>> {
305 match self.slot {
306 PositionSlot::Move(slot) => {
307 Ok(MoveMut { game: self.game, slot, protected: self.protected.clone() })
308 }
309 PositionSlot::Start => Err(start::Error),
310 }
311 }
312
313 pub fn into_play(self) -> start::Result<MoveMut<'g>> {
314 match self.slot {
315 PositionSlot::Move(slot) => {
316 Ok(MoveMut { game: self.game, slot, protected: self.protected })
317 }
318 PositionSlot::Start => Err(start::Error),
319 }
320 }
321
322 pub fn main_mut(&mut self) -> Result<MoveMut<'_>> {
323 self.option_mut(0)
324 }
325
326 pub fn into_main(self) -> Result<MoveMut<'g>> {
327 self.into_option(0)
328 }
329
330 pub fn option_mut(&mut self, option: impl Choose) -> Result<MoveMut<'_>> {
331 let slot = self.as_ref().option(option)?.slot();
332 Ok(MoveMut { game: self.game, slot, protected: self.protected.clone() })
333 }
334
335 pub fn into_option(self, option: impl Choose) -> Result<MoveMut<'g>> {
336 let slot = self.as_ref().option(option)?.slot();
337 Ok(MoveMut { game: self.game, slot, protected: self.protected })
338 }
339
340 pub fn alternatives_mut(&mut self) -> MovesMut<'_> {
341 MovesMut {
342 game: self.game,
343 position: self.slot,
344 index: 1,
345 visited: Set::new(),
346 protected: self.protected.clone(),
347 }
348 }
349
350 pub fn into_alternatives(self) -> MovesMut<'g> {
351 MovesMut {
352 game: self.game,
353 position: self.slot,
354 index: 1,
355 visited: Set::new(),
356 protected: self.protected,
357 }
358 }
359
360 pub fn iter_mut(&mut self) -> MovesMut<'_> {
361 MovesMut {
362 game: self.game,
363 position: self.slot,
364 index: 0,
365 visited: Set::new(),
366 protected: self.protected.clone(),
367 }
368 }
369
370 pub fn into_iter_mut(self) -> MovesMut<'g> {
371 MovesMut {
372 game: self.game,
373 position: self.slot,
374 index: 0,
375 visited: Set::new(),
376 protected: self.protected,
377 }
378 }
379}
380
381impl Deref for PositionMut<'_> {
382 type Target = Position;
383
384 fn deref(&self) -> &Position {
385 self.position()
386 }
387}
388
389impl<'g> MoveRef<'g> {
391 pub const fn slot(&self) -> MoveSlot {
392 self.slot
393 }
394
395 pub const fn game(&self) -> &'g Game {
396 self.game
397 }
398
399 pub fn into_game(self) -> Game {
400 let mut game = self.previous().into_game();
401 game.start_mut()
402 .insert_at_untracked(Some(self.slot), 0, self.play().play)
403 .expect("move is legal in its previous position");
404 game
405 }
406
407 pub fn comment(&self) -> Option<&'g Text> {
408 self.game.tree.position(self.slot.into()).comment()
409 }
410
411 pub fn evaluation(&self) -> Option<&'g Evaluation> {
412 self.game.tree.position(self.slot.into()).evaluation()
413 }
414
415 pub fn expanded(&self) -> bool {
416 self.game.tree.position(self.slot.into()).expanded()
417 }
418
419 pub(in crate::game) fn play(&self) -> &Move {
420 self.game.tree.play(self.slot)
421 }
422}
423
424impl<'g> MoveRef<'g> {
426 pub fn previous(&self) -> PositionRef<'g> {
427 PositionRef { game: self.game, slot: self.play().previous }
428 }
429
430 pub const fn position(&self) -> PositionRef<'g> {
431 PositionRef { game: self.game, slot: PositionSlot::Move(self.slot) }
432 }
433}
434
435impl Deref for MoveRef<'_> {
436 type Target = Move;
437
438 fn deref(&self) -> &Move {
439 self.play()
440 }
441}
442
443impl<'g> MoveMut<'g> {
445 const fn new(game: &'g mut Game, slot: MoveSlot) -> Self {
446 Self { game, slot, protected: Protected::new() }
447 }
448
449 pub const fn slot(&self) -> MoveSlot {
450 self.slot
451 }
452
453 pub const fn game(&self) -> &Game {
454 self.game
455 }
456
457 pub const fn as_ref(&self) -> MoveRef<'_> {
458 MoveRef { game: self.game, slot: self.slot }
459 }
460
461 pub fn into_ref(self) -> MoveRef<'g> {
462 MoveRef { game: self.game, slot: self.slot }
463 }
464
465 pub fn set_intro(&mut self, intro: Option<Text>) {
466 self.update_public(|public| public.affixes.intro = intro);
467 }
468
469 pub fn set_outro(&mut self, outro: Option<Text>) {
470 self.update_public(|public| public.affixes.outro = outro);
471 }
472
473 pub fn update_commands(&mut self, update: impl FnOnce(&mut Vec<Command>)) {
474 self.update_public(|public| update(&mut public.commands));
475 }
476
477 pub fn update_nags(&mut self, update: impl FnOnce(&mut Vec<Nag>)) {
478 self.update_public(|public| update(&mut public.nags));
479 }
480
481 pub fn update_public(&mut self, update: impl FnOnce(&mut MovePublic)) {
482 let before = self.game.tracks_changes().then(|| self.public.clone());
483 update(&mut self.play_mut().public);
484 if let Some(before) = before
485 && before != self.public
486 {
487 self.game.track(change::Patch::Move { slot: self.slot, public: Box::new(before) });
488 }
489 }
490
491 pub fn comment(&self) -> Option<&Text> {
492 self.game.tree.position(self.slot.into()).comment()
493 }
494
495 pub fn evaluation(&self) -> Option<&Evaluation> {
496 self.as_ref().evaluation()
497 }
498
499 pub fn expanded(&self) -> bool {
500 self.as_ref().expanded()
501 }
502
503 pub fn set_comment(&mut self, comment: Option<Text>) {
504 self.position_mut().set_comment(comment);
505 }
506
507 pub fn set_evaluation(&mut self, evaluation: Option<Evaluation>) {
508 self.position_mut().set_evaluation(evaluation);
509 }
510
511 pub fn set_expanded(&mut self, expanded: bool) {
512 self.position_mut().set_expanded(expanded);
513 }
514
515 pub(in crate::game) fn play(&self) -> &Move {
516 self.game.tree.play(self.slot)
517 }
518
519 pub(in crate::game) fn play_mut(&mut self) -> &mut Move {
520 self.game.tree.play_mut(self.slot)
521 }
522}
523
524impl<'g> MoveMut<'g> {
526 pub fn previous(&self) -> PositionRef<'_> {
527 self.as_ref().previous()
528 }
529
530 pub fn previous_mut(&mut self) -> PositionMut<'_> {
531 let slot = self.play().previous;
532 let protected = self.protected.with_unremovable(self.slot.into());
533 PositionMut { game: self.game, slot, protected }
534 }
535
536 pub fn into_previous(self) -> PositionMut<'g> {
537 let slot = self.play().previous;
538 PositionMut { game: self.game, slot, protected: self.protected }
539 }
540
541 pub const fn position(&self) -> PositionRef<'_> {
542 PositionRef { game: self.game, slot: PositionSlot::Move(self.slot) }
543 }
544
545 pub fn position_mut(&mut self) -> PositionMut<'_> {
546 PositionMut {
547 game: self.game,
548 slot: PositionSlot::Move(self.slot),
549 protected: self.protected.clone(),
550 }
551 }
552
553 pub fn into_position(self) -> PositionMut<'g> {
554 PositionMut {
555 game: self.game,
556 slot: PositionSlot::Move(self.slot),
557 protected: self.protected,
558 }
559 }
560}
561
562impl Deref for MoveMut<'_> {
563 type Target = Move;
564
565 fn deref(&self) -> &Move {
566 self.play()
567 }
568}
569
570impl PartialEq<PositionSlot> for Order<'_> {
571 fn eq(&self, other: &PositionSlot) -> bool {
572 self.slot == *other
573 }
574}
575
576impl PartialOrd<PositionSlot> for Order<'_> {
577 fn partial_cmp(&self, other: &PositionSlot) -> Option<Ordering> {
578 if self.slot == *other {
579 return Some(Ordering::Equal);
580 }
581 if !self.game.tree.contains(self.slot) || !self.game.tree.contains(*other) {
582 return None;
583 }
584 if self.game.tree.less_equal(self.slot, *other) {
585 Some(Ordering::Less)
586 } else if self.game.tree.less_equal(*other, self.slot) {
587 Some(Ordering::Greater)
588 } else {
589 None
590 }
591 }
592}
593
594impl Choose for usize {
595 fn choose(self, position: PositionRef<'_>) -> Result<usize, Missing> {
596 (self < position.options().len())
597 .then_some(self)
598 .ok_or(Missing { position: position.slot(), choice: self.into() })
599 }
600}
601
602impl Choose for MoveSlot {
603 fn choose(self, position: PositionRef<'_>) -> Result<usize, Missing> {
604 position
605 .options()
606 .iter()
607 .position(|option| *option == self)
608 .ok_or(Missing { position: position.slot(), choice: self.into() })
609 }
610}
611
612impl Choose for chess::Move {
613 fn choose(self, position: PositionRef<'_>) -> Result<usize, Missing> {
614 position
615 .iter()
616 .position(|option| **option == self)
617 .ok_or(Missing { position: position.slot(), choice: self.into() })
618 }
619}
620
621impl<'g> IntoIterator for PositionRef<'g> {
622 type Item = MoveRef<'g>;
623 type IntoIter = Moves<'g>;
624
625 fn into_iter(self) -> Self::IntoIter {
626 self.iter()
627 }
628}
629
630impl<'g> Iterator for Moves<'g> {
631 type Item = MoveRef<'g>;
632
633 fn next(&mut self) -> Option<Self::Item> {
634 let slot = self.slots.next().copied()?;
635 Some(MoveRef { game: self.game, slot })
636 }
637
638 fn size_hint(&self) -> (usize, Option<usize>) {
639 self.slots.size_hint()
640 }
641}
642
643impl DoubleEndedIterator for Moves<'_> {
644 fn next_back(&mut self) -> Option<Self::Item> {
645 let slot = self.slots.next_back().copied()?;
646 Some(MoveRef { game: self.game, slot })
647 }
648}
649
650impl ExactSizeIterator for Moves<'_> {}
651impl FusedIterator for Moves<'_> {}
652
653impl MovesMut<'_> {
654 #[allow(clippy::should_implement_trait)]
655 pub fn next(&mut self) -> Option<MoveMut<'_>> {
656 loop {
657 let slot = self.game.tree.position(self.position).options.get(self.index).copied()?;
658 self.index += 1;
659
660 if self.visited.insert(slot) && self.game.tree.contains(slot.into()) {
661 return Some(MoveMut { game: self.game, slot, protected: self.protected.clone() });
662 }
663 }
664 }
665}
666
667#[cfg(test)]
668mod tests {
669 use crate::{Role::Pawn, Square::*};
670
671 use super::*;
672
673 #[test]
674 fn choice_debug() {
675 let slot = MoveSlot::from_index(21).unwrap();
676 let play = chess::Move::normal(Pawn, E2, E4);
677
678 assert_eq!(format!("{:?}", Choice::Index(37)), "Index(37)");
679 assert_eq!(format!("{:?}", Choice::Move(play)), "Move(e2-e4)");
680 assert_eq!(format!("{:?}", Choice::Slot(slot)), "Slot(22)");
681 }
682
683 #[test]
684 fn missing_display() {
685 let position = PositionSlot::from(12);
686 let slot = MoveSlot::from_index(21).unwrap();
687 let play = chess::Move::normal(Pawn, E2, E4);
688
689 assert_eq!(Error::Position(position).to_string(), "position slot 12 not found");
690 assert_eq!(Error::Move(slot).to_string(), "move slot 22 not found");
691 assert_eq!(
692 Missing { position, choice: Choice::Index(37) }.to_string(),
693 "option Index(37) not found in position slot 12"
694 );
695 assert_eq!(
696 Missing { position, choice: Choice::Slot(slot) }.to_string(),
697 "option Slot(22) not found in position slot 12"
698 );
699 assert_eq!(
700 Missing { position, choice: Choice::Move(play) }.to_string(),
701 "option Move(e2-e4) not found in position slot 12"
702 );
703 }
704}