1use crate::{Role, Square, board::Bitboard};
2
3use super::{Move, Position, Side};
4
5use Role::*;
6
7type Moves = Vec<Move>;
8
9impl Position {
11 pub fn legal_moves(&self) -> Moves {
12 if self.is_check() {
13 return self.evasion_moves();
14 }
15
16 let shields = self.board().king_shields(self.turn());
17
18 let mut moves = self.pseudo_piece_moves();
19 moves.retain(|m| self.piece_move_is_safe(*m, shields));
20 moves.extend(self.legal_king_moves());
21 moves.extend(self.legal_en_passant_moves());
22 moves.extend(self.legal_castle_moves());
23 moves
24 }
25
26 pub fn legal_castle_moves(&self) -> Moves {
27 let mut moves = Moves::new();
28
29 for side in Side::ALL {
30 if let Some(play) = self.can_castle(side) {
31 moves.push(play);
32 }
33 }
34
35 moves
36 }
37 fn evasion_moves(&self) -> Moves {
38 let checkers = self.checkers();
39 let Some(king) = self.board().king_of(self.turn()) else {
40 return Moves::new();
41 };
42
43 let mut moves = self.legal_king_evasion_moves(king, checkers);
44 if checkers.more_than_one() {
45 return moves;
46 }
47
48 let Some(checker) = checkers.first() else {
49 return moves;
50 };
51
52 let shields = self.board().king_shields(self.turn());
53 let target = king.between(checker).with(checker);
54 let mut piece_moves = self.pseudo_piece_moves_to(target);
55 piece_moves.retain(|m| self.piece_move_is_safe(*m, shields));
56 moves.extend(piece_moves);
57 moves.extend(self.legal_en_passant_moves());
58
59 moves
60 }
61
62 pub fn pseudo_piece_moves(&self) -> Moves {
63 let target = !self.board().player(self.turn());
64 self.pseudo_piece_moves_to(target)
65 }
66
67 fn pseudo_piece_moves_to(&self, target: Bitboard) -> Moves {
73 let mut moves = Moves::new();
74
75 self.pseudo_pawn_moves(target, &mut moves);
76 for role in [Knight, Bishop, Rook, Queen] {
77 self.pseudo_role_moves(role, target, &mut moves);
78 }
79
80 moves
81 }
82
83 fn legal_king_evasion_moves(&self, king: Square, checkers: Bitboard) -> Moves {
84 let sliders = checkers.intersection(self.board().sliders());
85 let mut attacked = Bitboard::EMPTY;
86 let mut sliders = sliders;
87 while let Some(checker) = sliders.pop_first() {
88 attacked.append(checker.full_ray(king).difference(Bitboard::from_square(checker)));
92 }
93
94 let mut moves = Moves::new();
95 let target = self.board().player(self.turn()).union(attacked);
96
97 self.pseudo_role_moves(King, !target, &mut moves);
98 moves.retain(|m| self.king_move_is_safe(*m));
99
100 moves
101 }
102
103 pub fn legal_king_moves(&self) -> Moves {
104 let mut moves = Moves::new();
105 let target = !self.board().player(self.turn());
106
107 self.pseudo_role_moves(King, target, &mut moves);
108 moves.retain(|m| self.king_move_is_safe(*m));
109
110 moves
111 }
112
113 fn king_move_is_safe(&self, play: Move) -> bool {
114 let occupied = self.board().occupied().difference(Bitboard::from_square(play.from));
115 self.board().attacks_on(play.to, self.turn().other(), occupied).is_empty()
116 }
117
118 fn king_square_is_safe(&self, square: Square) -> bool {
119 self.board().attacks_on(square, self.turn().other(), self.board().occupied()).is_empty()
120 }
121
122 fn pseudo_role_moves(&self, role: Role, target: Bitboard, moves: &mut Moves) {
123 let occupied = self.board().occupied();
124 let mut pieces = self.board().role(role).intersection(self.board().player(self.turn()));
125
126 while let Some(from) = pieces.pop_first() {
127 let piece = role.of(self.turn());
128 let mut targets = from.attacks(piece, occupied).intersection(target);
129 while let Some(to) = targets.pop_first() {
130 moves.push(Move::capture(role, from, to, self.board().role_at(to)));
131 }
132 }
133 }
134
135 fn pseudo_pawn_moves(&self, target: Bitboard, moves: &mut Moves) {
136 let occupied = self.board().occupied();
137 let them = self.board().player(self.turn().other());
138 let pawns = self.board().pawns().intersection(self.board().player(self.turn()));
139 let empty = !occupied;
140 let push = self.turn().pawn_push();
141 let double_push = self.turn().pawn_double_push();
142 let [left, right] = self.turn().pawn_capture_directions();
143 let double_rank = Bitboard::from_rank(self.turn().pawn_double_push_rank());
144
145 let single = pawns.checked_shift(push).intersection(empty);
146 let double = single.intersection(double_rank).checked_shift(push).intersection(empty);
147 let captures_left = pawns.checked_shift(left).intersection(them);
148 let captures_right = pawns.checked_shift(right).intersection(them);
149
150 let mut targets = single.intersection(target);
151 while let Some(to) = targets.pop_first() {
152 let from = to.checked_add(push.reverse()).expect("valid pawn source");
153 moves.extend(Move::pawn(self.turn(), from, to, None));
154 }
155
156 let mut targets = double.intersection(target);
157 while let Some(to) = targets.pop_first() {
158 let from = to.checked_add(double_push.reverse()).expect("valid pawn source");
159 moves.push(Move::normal(Pawn, from, to));
160 }
161
162 let mut targets = captures_left.intersection(target);
163 while let Some(to) = targets.pop_first() {
164 let from = to.checked_add(left.reverse()).expect("valid pawn source");
165 moves.extend(Move::pawn(self.turn(), from, to, self.board().role_at(to)));
166 }
167
168 let mut targets = captures_right.intersection(target);
169 while let Some(to) = targets.pop_first() {
170 let from = to.checked_add(right.reverse()).expect("valid pawn source");
171 moves.extend(Move::pawn(self.turn(), from, to, self.board().role_at(to)));
172 }
173 }
174
175 fn legal_en_passant_moves(&self) -> Moves {
176 let mut moves = Moves::new();
177
178 if let Some(to) = self.en_passant() {
179 let to = to.square();
180
181 let mut pawns = self
182 .board()
183 .pawns()
184 .intersection(self.board().player(self.turn()))
185 .intersection(to.pawn_attack_moves(self.turn().other()));
186
187 while let Some(from) = pawns.pop_first() {
188 let m = Move::en_passant(from, to);
189 if self.en_passant_move_is_safe(m) {
190 moves.push(m);
191 }
192 }
193 }
194
195 moves
196 }
197
198 fn piece_move_is_safe(&self, play: Move, shields: Bitboard) -> bool {
199 if !shields.contains(play.from) {
202 return true;
203 }
204
205 match self.board().king_of(self.turn()) {
206 Some(king) => king.full_ray(play.from).contains(play.to),
212 None => false,
214 }
215 }
216
217 fn en_passant_move_is_safe(&self, play: Move) -> bool {
218 let Some(king) = self.board().king_of(self.turn()) else {
219 return false;
220 };
221
222 let captured = Square::new(play.to.file(), play.from.rank());
223 let occupied = self
224 .board()
225 .occupied()
226 .difference(Bitboard::from_square(play.from))
227 .difference(Bitboard::from_square(captured))
228 .union(Bitboard::from_square(play.to));
229
230 self.board().attacks_on(king, self.turn().other(), occupied).is_empty()
231 }
232
233 pub fn can_castle(&self, side: Side) -> Option<Move> {
234 let rook_file = self.castles().get(self.turn(), side)?;
235 let king_from = self.board().king_of(self.turn())?;
236 let empty_path = self.turn().castle_empty_path(king_from, rook_file);
237 if !self.board().occupied().intersection(empty_path).is_empty() {
238 return None;
239 }
240
241 if self
242 .turn()
243 .castle_king_path(king_from, side)
244 .iter()
245 .any(|square| !self.king_square_is_safe(square))
246 {
247 return None;
248 }
249
250 Some(Move::castle(self.turn(), king_from, rook_file))
251 }
252}
253
254#[cfg(test)]
255mod tests {
256 use crate::{
257 Player::*,
258 position::Parts,
259 square::{File::*, Square::*},
260 };
261
262 use super::{Move, Position, Side};
263
264 fn freestyle_position() -> Position {
265 let mut parts = Parts::empty();
266 let board = &mut parts.board;
267 board.insert(C1, White.king());
268 board.insert(A1, White.rook());
269 board.insert(H1, White.rook());
270 board.insert(C8, Black.king());
271 board.insert(A8, Black.rook());
272 board.insert(H8, Black.rook());
273
274 let castles = &mut parts.castles;
275 castles.set(White, Side::Queen, A);
276 castles.set(White, Side::King, H);
277 castles.set(Black, Side::Queen, A);
278 castles.set(Black, Side::King, H);
279 parts.validate().unwrap()
280 }
281
282 #[test]
283 fn generates_freestyle_castle_moves() {
284 let moves = freestyle_position().legal_castle_moves();
285
286 assert!(moves.contains(&Move::castle(White, C1, A)));
287 assert!(moves.contains(&Move::castle(White, C1, H)));
288 }
289
290 #[test]
291 fn blocks_freestyle_castle_move() {
292 let mut parts = freestyle_position().parts();
293 parts.board.insert(E1, White.knight());
294 let position = parts.validate().unwrap();
295
296 assert!(!position.legal_castle_moves().contains(&Move::castle(White, C1, H)));
297 }
298}