1use super::board::Board;
2use super::board_map::BoardMap;
3use super::castlinginfo;
4use super::enpassant::Enpassant;
5use super::king::{self, KingState};
6use super::movement::{self, PieceMove};
7use super::play;
8use super::state::State;
9use super::{AvailableTurn, Side};
10use chess_notation_parser::{
11 turn_castling, turn_move, Castling, CastlingType, Flag, FlagCheck, Move,
12 Piece, Square, Turn,
13};
14
15struct Sps {
17 square: Square,
18 piece: Piece,
19 side: Side,
20}
21
22pub fn get_available_turns(board: &mut Board) -> Vec<AvailableTurn> {
24 let mut available_turns = Vec::<AvailableTurn>::with_capacity(128);
25 let side = board.active_player;
26
27 let player: Vec<Sps> = scan_for_pieces(&board.map, side);
29
30 for sps in player.iter() {
31 let moves = &mut get_turns(sps, board);
32 available_turns.append(moves);
33 }
34
35 available_turns
36}
37
38fn scan_for_pieces(map: &BoardMap, side: Side) -> Vec<Sps> {
40 map.into_iter()
41 .filter(|(_, (_, s))| side == *s)
42 .map(|(sq, (p, _))| Sps {
43 square: sq,
44 piece: p,
45 side,
46 })
47 .collect::<Vec<Sps>>()
48}
49
50struct TurnInfo {
52 captured: Option<Piece>,
53 turn: Turn,
54}
55
56fn get_turns(sps: &Sps, board: &mut Board) -> Vec<AvailableTurn> {
58 let unchecked_turns = get_unchecked_turns(sps, board);
59 let mut turns = get_check_checkmate_flags(unchecked_turns, sps, board);
60
61 set_correct_src(&mut turns, &board.map, sps);
62 gen_available_turns(turns, sps)
63}
64
65fn gen_available_turns(turns: Vec<TurnInfo>, sps: &Sps) -> Vec<AvailableTurn> {
68 turns
69 .iter()
70 .map(|turn_info| {
71 let dst = match turn_info.turn {
72 Turn::Move(ref turn) => turn.dst,
73 Turn::Castling(castling) => {
74 castlinginfo::get_path_king(sps.side, castling.r#type).dst
75 }
76 };
77
78 AvailableTurn::new(
79 sps.square.to_string(),
80 dst.to_string(),
81 sps.piece.to_string(),
82 turn_info.captured.map(|piece| piece.to_string()),
83 turn_info.turn.to_string(),
84 )
85 })
86 .collect::<Vec<AvailableTurn>>()
87}
88
89fn set_correct_src(turns: &mut [TurnInfo], map: &BoardMap, sps: &Sps) {
91 for turn_info in turns.iter_mut() {
92 if let Turn::Move(ref mut move_turn) = turn_info.turn {
93 move_turn.src = match sps.piece {
94 Piece::Pawn => get_correct_pawn_src(move_turn, sps),
95 _ => get_correct_non_pawn_src(move_turn, map, sps),
96 };
97 }
98 }
99}
100
101fn get_correct_pawn_src(t_move: &Move, sps: &Sps) -> Option<Vec<Square>> {
103 match t_move.check_flag(Flag::CAPTURE) {
104 false => None,
106 _ => Some(Square::get_file(sps.square.get_file_char()).unwrap()),
107 }
108}
109
110fn get_correct_non_pawn_src(
112 t_move: &Move,
113 map: &BoardMap,
114 sps: &Sps,
115) -> Option<Vec<Square>> {
116 let mut possible_src = movement::possible_squares_for_dst(
117 map,
118 t_move.dst,
119 sps.side,
120 PieceMove::from(sps.piece),
121 );
122
123 if possible_src.len() == 1 {
125 return None;
126 }
127
128 possible_src.retain(|s| *s != sps.square);
130
131 let src_file = sps.square.get_file_char();
133 let mut use_file = true;
134 for square in possible_src.iter() {
135 if src_file == square.get_file_char() {
136 use_file = false;
137 break;
138 }
139 }
140
141 if use_file {
142 return Some(Square::get_file(src_file).unwrap());
143 }
144
145 let src_rank = sps.square.get_rank_char();
146 let mut use_rank = true;
147 for square in possible_src.iter() {
148 if src_rank == square.get_rank_char() {
149 use_rank = false;
150 break;
151 }
152 }
153
154 if use_rank {
155 return Some(Square::get_rank(src_rank).unwrap());
156 }
157
158 Some(vec![sps.square])
159}
160
161fn get_unchecked_turns(sps: &Sps, board: &mut Board) -> Vec<Turn> {
163 let get_castling = |turn: &Turn| -> CastlingType {
164 match *turn {
165 Turn::Castling(castling) => castling.r#type,
166 _ => panic!("Castling expected"),
167 }
168 };
169
170 let active_side = board.active_player;
171 let mut unchecked_turns = match sps.piece {
172 Piece::Pawn => return get_unchecked_pawn_turns(sps, board),
174
175 Piece::King => board
177 .castling_rights
178 .get()
179 .into_iter()
180 .filter(|(side, _)| *side == active_side)
181 .map(|(_, castling)| turn_castling!(castling))
182 .filter(|turn| {
183 play::verify_castling(board, get_castling(turn)).is_ok()
184 })
185 .collect::<Vec<Turn>>(),
186 _ => vec![],
187 };
188
189 unchecked_turns.append(&mut get_unchecked_non_pawn_turns(sps, board));
190 unchecked_turns
191}
192
193fn get_unchecked_non_pawn_turns(sps: &Sps, board: &Board) -> Vec<Turn> {
195 movement::possible_squares_for_src(
196 &board.map,
197 sps.square,
198 sps.side,
199 PieceMove::from(sps.piece),
200 )
201 .iter()
202 .map(|dst| {
203 turn_move!(
204 sps.piece,
205 *dst,
206 match board.map.get(dst).is_some() {
207 true => Flag::CAPTURE,
208 false => 0,
209 }
210 )
211 })
212 .collect::<Vec<Turn>>()
213}
214
215fn get_unchecked_pawn_turns(sps: &Sps, board: &Board) -> Vec<Turn> {
217 let mut turns = movement::possible_squares_for_src(
219 &board.map,
220 sps.square,
221 sps.side,
222 PieceMove::PawnNormal,
223 )
224 .into_iter()
225 .filter(|square| board.map.get(square).is_none())
226 .map(|dst| turn_move!(Piece::Pawn, dst, Flag::NONE))
227 .collect::<Vec<Turn>>();
228
229 let mut capture_turns = movement::possible_squares_for_src(
231 &board.map,
232 sps.square,
233 sps.side,
234 PieceMove::PawnCapture,
235 )
236 .into_iter()
237 .filter(|square| match board.map.get(square) {
238 None => match board.enpassant {
239 None => false,
240 Some(enpassant) => *square == enpassant.capture_pos,
241 },
242 Some((p, s)) => s != board.active_player && p != Piece::King,
243 })
244 .map(|dst| turn_move!(Piece::Pawn, dst, Flag::CAPTURE))
245 .collect::<Vec<Turn>>();
246
247 turns.append(&mut capture_turns);
248 turns = get_promotion_for_unchecked_pawn_turns(turns, board.active_player);
249 turns
250}
251
252fn get_promotion_for_unchecked_pawn_turns(
254 mut turns: Vec<Turn>,
255 side: Side,
256) -> Vec<Turn> {
257 let mut promotion_moves: Vec<Move> = vec![];
258 let mut turns_iter = turns.iter_mut();
259
260 while let Some(Turn::Move(turn)) = turns_iter.next() {
261 if (turn.dst.get_rank_char() != '1' || side != Side::Black)
262 && (turn.dst.get_rank_char() != '8' || side != Side::White)
263 {
264 continue;
265 }
266
267 turn.promotion = Some(Piece::Queen);
269
270 let mut gen_promoted_turn = |piece| {
271 let mut new_move: Move = turn.clone();
272 new_move.promotion = Some(piece);
273 promotion_moves.push(new_move);
274 };
275
276 gen_promoted_turn(Piece::Rook);
278 gen_promoted_turn(Piece::Bishop);
279 gen_promoted_turn(Piece::Knight);
280 }
281
282 let mut promotion_turns = promotion_moves
283 .into_iter()
284 .map(Turn::Move)
285 .collect::<Vec<Turn>>();
286
287 turns.append(&mut promotion_turns);
288 turns
289}
290
291fn simulate_turn(
293 board: &mut Board,
294 sps: &Sps,
295 turn: &Turn,
296) -> Result<State, ()> {
297 let ret_ok = match turn {
298 Turn::Move(r#move) if sps.piece == Piece::Pawn => {
299 simulate_pawn_move(board, sps, r#move)
300 }
301 Turn::Move(r#move) => simulate_move(board, sps, r#move),
302 Turn::Castling(castling) => simulate_castling(board, castling),
303 }?;
304
305 board.hash_state_push();
306 board.active_player.switch_side();
307 Ok(ret_ok)
308}
309
310fn simulate_castling(board: &mut Board, turn: &Castling) -> Result<State, ()> {
312 let state = State::new(board, turn.to_string());
313
314 let side = board.active_player;
315 let castling = (side, turn.r#type);
316
317 assert!(
319 board.castling_rights.remove(&castling),
320 "Castling right not found"
321 );
322
323 let rook_path = castlinginfo::get_path_rook(side, turn.r#type);
324 board.move_piece(rook_path.dst, rook_path.src);
325
326 let king_path = castlinginfo::get_path_king(side, turn.r#type);
327 board.move_piece(king_path.dst, king_path.src);
328
329 assert!(
332 king::is_safe(&board.map, board.get_king_pos(side), side),
333 "Castling: King is not safe"
334 );
335
336 let castling_opposite = (side, turn.r#type.opposite());
338 board.castling_rights.remove(&castling_opposite);
339
340 board.enpassant = None;
341
342 Ok(state)
343}
344
345fn simulate_pawn_move(
347 board: &mut Board,
348 sps: &Sps,
349 turn: &Move,
350) -> Result<State, ()> {
351 let mut state = State::new(board, turn.to_string());
352 let side = board.active_player;
353
354 state.captured = match board.move_piece(turn.dst, sps.square) {
358 None => match board.enpassant {
359 None => None,
360 Some(enpassant) => match turn.dst == enpassant.capture_pos {
361 true => Some((
362 enpassant.pawn_src,
363 board.map.remove(&enpassant.pawn_src).unwrap(),
364 )),
365 false => None,
366 },
367 },
368 Some(captured) => Some((turn.dst, captured)),
369 };
370
371 assert!(!turn.check_flag(Flag::CAPTURE) ^ state.captured.is_some());
373
374 state.moving_piece_src = Some(sps.square);
375
376 if !king::is_safe(&board.map, board.get_king_pos(side), side) {
378 board.undo(state);
380 return Err(());
381 }
382
383 if let Some(promotion) = turn.promotion {
384 board.map.insert(turn.dst, (promotion, sps.side));
385 }
386
387 board.fifty_move_rule = 0;
388
389 board.enpassant = match turn.check_flag(Flag::CAPTURE) {
391 true => None,
392 _ => Enpassant::try_from(sps.square, turn.dst, side),
393 };
394
395 Ok(state)
396}
397
398fn simulate_move(
400 board: &mut Board,
401 sps: &Sps,
402 turn: &Move,
403) -> Result<State, ()> {
404 let mut state = State::new(board, turn.to_string());
405 let side = board.active_player;
406
407 let captured = board.move_piece(turn.dst, sps.square);
408 state.captured = captured.map(|captured| (turn.dst, captured));
409
410 state.moving_piece_src = Some(sps.square);
411
412 if !king::is_safe(&board.map, board.get_king_pos(side), side) {
414 board.undo(state);
416 return Err(());
417 }
418
419 board.fifty_move_rule = 0;
421 board.enpassant = None;
422 play::handle_castling_status(board, sps.square, turn, &captured);
423
424 Ok(state)
425}
426
427fn get_check_checkmate_flags(
429 mut turns: Vec<Turn>,
430 sps: &Sps,
431 board: &mut Board,
432) -> Vec<TurnInfo> {
433 let mut to_be_removed = Vec::<usize>::new();
434 let mut captured = Vec::<Option<Piece>>::with_capacity(turns.len());
435
436 for (i, turn) in turns.iter_mut().enumerate() {
437 let simulated_state = simulate_turn(board, sps, turn);
439 if simulated_state.is_err() {
440 captured.push(None);
441 to_be_removed.push(i);
442 continue;
443 }
444
445 match king::get_state(board, board.active_player) {
446 KingState::Safe => (),
447 KingState::Check => add_turn_flag(turn, Flag::CHECK),
448 _ => add_turn_flag(
450 turn,
451 match confirm_checkmate(board) {
452 true => Flag::CHECKMATE,
453 _ => Flag::CHECK,
454 },
455 ),
456 }
457
458 let simulated_state = simulated_state.unwrap();
459 captured
460 .push(simulated_state.captured.map(|(_, (captured, _))| captured));
461
462 board.active_player.switch_side();
463 board.hash_state_pop();
464 board.undo(simulated_state);
465 }
466
467 while let Some(i) = to_be_removed.pop() {
469 turns.remove(i);
470 captured.remove(i);
471 }
472
473 turns
474 .into_iter()
475 .zip(captured.into_iter())
476 .map(|(turn, captured)| TurnInfo { turn, captured })
477 .collect::<Vec<TurnInfo>>()
478}
479
480fn confirm_checkmate(board: &mut Board) -> bool {
483 let opponent: Vec<Sps> = scan_for_pieces(&board.map, board.active_player);
485
486 for sps in opponent {
487 for turn in get_unchecked_turns(&sps, board) {
488 match simulate_turn(board, &sps, &turn) {
489 Ok(state) => {
493 board.active_player.switch_side();
494 board.hash_state_pop();
495 board.undo(state);
496 return false;
497 }
498 _ => continue,
499 }
500 }
501 }
502
503 true
504}
505
506fn add_turn_flag(turn: &mut Turn, flag: u8) {
508 match turn {
509 Turn::Castling(ref mut castling) => castling.flags |= flag,
510 Turn::Move(ref mut r#move) => r#move.flags |= flag,
511 }
512}