1use core::{
2 borrow::{Borrow, BorrowMut},
3 ops::Deref,
4};
5
6use super::{options, traversal::Choose, *};
7
8impl Cursor<Game> {
16 pub const fn new(game: Game) -> Self {
17 Self { game, slot: START, preserve: Set::new() }
18 }
19
20 pub fn into_game(self) -> Game {
21 self.game
22 }
23}
24
25impl<G> Cursor<G> {
27 pub const fn slot(&self) -> PositionSlot {
28 self.slot
29 }
30}
31
32impl<G: Borrow<Game>> Cursor<G> {
34 pub fn game(&self) -> &Game {
35 self.game.borrow()
36 }
37
38 pub fn position(&self) -> PositionRef<'_> {
39 PositionRef { game: self.game(), slot: self.slot }
40 }
41
42 pub fn play(&self) -> start::Result<MoveRef<'_>> {
43 self.position().play()
44 }
45
46 pub fn at(&self, slot: PositionSlot) -> traversal::Result<CursorRef<'_>> {
47 let game = self.game();
48 game.position(slot)?;
49 Ok(Cursor { game, slot, preserve: self.preserve.clone() })
50 }
51}
52
53impl<G: Borrow<Game>> Deref for Cursor<G> {
54 type Target = Game;
55
56 fn deref(&self) -> &Game {
57 self.game()
58 }
59}
60
61impl<G: BorrowMut<Game>> Cursor<G> {
63 pub fn game_mut(&mut self) -> &mut Game {
64 self.game.borrow_mut()
65 }
66
67 pub fn set_orientation(&mut self, orientation: Player) {
68 self.game.borrow_mut().set_orientation(orientation);
69 }
70
71 pub fn set_outcome(&mut self, outcome: Outcome) {
72 self.game.borrow_mut().set_outcome(outcome);
73 }
74
75 pub fn update_roster(&mut self, update: impl FnOnce(&mut Roster)) {
76 self.game.borrow_mut().update_roster(update);
77 }
78
79 pub fn update_tags(&mut self, update: impl FnOnce(&mut Vec<Tag>)) {
80 self.game.borrow_mut().update_tags(update);
81 }
82
83 pub fn cursor_mut(&mut self) -> CursorMut<'_> {
85 let slot = self.slot;
86 let preserve = self.protected().unremovable;
87 Cursor { game: self.game.borrow_mut(), slot, preserve }
88 }
89
90 pub fn position_mut(&mut self) -> PositionMut<'_> {
91 let slot = self.slot;
92 let protected = self.protected();
93 PositionMut { game: self.game.borrow_mut(), slot, protected }
94 }
95
96 pub fn play_mut(&mut self) -> start::Result<MoveMut<'_>> {
97 self.position_mut().into_play()
98 }
99
100 pub fn at_mut(&mut self, slot: PositionSlot) -> traversal::Result<CursorMut<'_>> {
101 if !self.game.borrow().tree.contains(slot) {
102 return Err(traversal::Error::Position(slot));
103 }
104
105 let preserve = self.protected().unremovable;
106 Ok(Cursor { game: self.game.borrow_mut(), slot, preserve })
107 }
108
109 fn protected(&self) -> Protected {
110 let mut unremovable = self.preserve.clone();
111 unremovable.insert(self.slot);
112 Protected { unremovable }
113 }
114}
115
116impl<'g> CursorRef<'g> {
118 pub fn into_game(self) -> &'g Game {
119 self.game
120 }
121
122 pub fn into_position(self) -> PositionRef<'g> {
123 PositionRef { game: self.game, slot: self.slot }
124 }
125
126 pub fn into_play(self) -> start::Result<MoveRef<'g>> {
127 self.into_position().play()
128 }
129}
130
131impl<'g> CursorMut<'g> {
133 pub fn into_position_mut(self) -> PositionMut<'g> {
134 let slot = self.slot;
135 let mut unremovable = self.preserve;
136 unremovable.insert(slot);
137 PositionMut { game: self.game, slot, protected: Protected { unremovable } }
138 }
139
140 pub fn into_play_mut(self) -> start::Result<MoveMut<'g>> {
141 self.into_position_mut().into_play()
142 }
143}
144
145impl<G: Borrow<Game>> Cursor<G> {
147 pub fn set(&mut self, slot: PositionSlot) -> traversal::Result<bool> {
148 if !self.game.borrow().tree.contains(slot) {
149 return Err(traversal::Error::Position(slot));
150 }
151
152 let changed = self.slot != slot;
153 self.slot = slot;
154 Ok(changed)
155 }
156
157 pub fn prev(&mut self) -> start::Result<PositionSlot> {
158 let slot = self.position().play()?.previous().slot();
159 self.slot = slot;
160 Ok(slot)
161 }
162
163 pub fn mainline_end(&mut self) -> PositionSlot {
164 while self.main().is_ok() {}
165 self.slot
166 }
167
168 pub fn main(&mut self) -> traversal::Result<PositionSlot> {
169 self.option(0)
170 }
171
172 pub fn option(&mut self, option: impl Choose) -> traversal::Result<PositionSlot> {
173 let slot = self.position().option(option)?.position().slot();
174 self.slot = slot;
175 Ok(slot)
176 }
177}
178
179impl<G: BorrowMut<Game>> Cursor<G> {
181 pub fn option_mut_or_push(&mut self, play: chess::Move) -> options::Result<PositionSlot> {
182 let slot = self.position_mut().option_mut_or_push(play)?.slot().into();
183 self.slot = slot;
184 Ok(slot)
185 }
186
187 pub fn option_mut_or_insert_before(
188 &mut self,
189 option: impl Choose,
190 play: chess::Move,
191 ) -> Result<PositionSlot> {
192 let slot = self.position_mut().option_mut_or_insert_before(option, play)?.slot().into();
193 self.slot = slot;
194 Ok(slot)
195 }
196
197 pub fn option_mut_or_insert_after(
198 &mut self,
199 option: impl Choose,
200 play: chess::Move,
201 ) -> Result<PositionSlot> {
202 let slot = self.position_mut().option_mut_or_insert_after(option, play)?.slot().into();
203 self.slot = slot;
204 Ok(slot)
205 }
206
207 pub fn remove_play(&mut self) -> Result<()> {
210 let play = self.play()?;
211 let removed = self.slot;
212 let slot = play.slot();
213 self.slot = play.previous().slot();
214
215 if let Err(error) = self.position_mut().remove(slot) {
216 self.slot = removed;
217 return Err(error);
218 }
219
220 Ok(())
221 }
222}
223
224#[cfg(test)]
225mod tests {
226 use crate::{Role::Pawn, Square::*};
227
228 use super::*;
229
230 #[test]
231 fn cursor_traversal() {
232 let mut cursor = Cursor::new(Game::new(chess::Position::start()));
233 let e4 = chess::Move::normal(Pawn, E2, E4);
234 let e5 = chess::Move::normal(Pawn, E7, E5);
235
236 assert_eq!(cursor.slot(), START);
237 assert!(cursor.prev().is_err());
238 assert!(cursor.main().is_err());
239
240 cursor.option_mut_or_push(e4).unwrap();
241 let after_e4 = cursor.slot();
242 cursor.option_mut_or_push(e5).unwrap();
243 let after_e5 = cursor.slot();
244
245 assert_eq!(cursor.prev().unwrap(), after_e4);
246 assert_eq!(cursor.main().unwrap(), after_e5);
247 assert!(!cursor.set(after_e5).unwrap());
248 assert_eq!(**cursor.play().unwrap(), e5);
249 }
250
251 #[test]
252 fn cursor_removal_protection() {
253 let mut game = Game::new(chess::Position::start());
254 let e4 = game.start_mut().into_push(chess::Move::normal(Pawn, E2, E4)).unwrap().slot();
255 let e5 = game
256 .play_mut(e4)
257 .unwrap()
258 .into_position()
259 .into_push(chess::Move::normal(Pawn, E7, E5))
260 .unwrap()
261 .slot();
262 let d4 = game.start_mut().into_push(chess::Move::normal(Pawn, D2, D4)).unwrap().slot();
263
264 let mut cursor = Cursor::new(game);
265 assert!(cursor.set(e5.into()).unwrap());
266
267 let mut root = cursor.at_mut(START).unwrap();
268 let mut start = root.position_mut();
269 assert!(matches!(
270 start.remove(e4),
271 Err(Error::Options(options::Error::Remove(options::Remove::Unremovable(slot))))
272 if slot == e5.into()
273 ));
274 assert!(start.remove(d4).is_ok());
275
276 assert_eq!(cursor.slot(), e5.into());
277 assert_eq!(cursor.position().slot(), e5.into());
278 assert!(cursor.game().play(e4).is_ok());
279 assert!(cursor.game().play(e5).is_ok());
280 assert!(cursor.game().play(d4).is_err());
281 }
282
283 #[test]
284 fn cursor_remove_play() {
285 let mut cursor = Cursor::new(Game::new(chess::Position::start()));
286 let e4 = cursor.option_mut_or_push(chess::Move::normal(Pawn, E2, E4)).unwrap();
287 let e5 = cursor.option_mut_or_push(chess::Move::normal(Pawn, E7, E5)).unwrap();
288
289 assert!(cursor.position_mut().into_remove_play().is_err());
290 assert_eq!(cursor.slot(), e5);
291 assert!(cursor.remove_play().is_ok());
292 assert_eq!(cursor.slot(), e4);
293 assert!(cursor.game().position(e5).is_err());
294 assert!(cursor.remove_play().is_ok());
295 assert_eq!(cursor.slot(), START);
296 assert!(matches!(cursor.remove_play(), Err(Error::Start(start::Error))));
297 }
298}