chess_move_gen/generation/
mod.rs1mod attacks;
2mod castle;
3mod lookup;
4mod pawn;
5mod pinned;
6pub mod slider;
7mod util;
8
9use self::attacks::*;
10use self::castle::*;
11use self::lookup::*;
12use self::pawn::*;
13use self::pinned::*;
14use self::slider::consts::squares_between;
15use self::slider::*;
16use std::cmp::Ordering;
17
18use self::attacks::king_danger_squares;
19use crate::bb::{BB, EMPTY};
20use crate::mv_list::MoveAdder;
21use crate::piece::*;
22use crate::position::Position;
23
24pub struct MoveGenPreprocessing((BB, BB, BB));
38
39impl MoveGenPreprocessing {
40 #[allow(dead_code)]
41 pub fn in_check(&self) -> bool {
42 self.0.0 != EMPTY
43 }
44}
45
46pub fn legal_moves<L: MoveAdder>(position: &Position, list: &mut L) -> bool {
48 legal_moves_with_preprocessing(position, list, movegen_preprocessing(position))
49}
50
51pub fn movegen_preprocessing(position: &Position) -> MoveGenPreprocessing {
53 let stm = position.state().stm;
54 let kings = position.bb_pc(KING.pc(stm));
55
56 MoveGenPreprocessing(checkers_and_pinned(kings, stm.flip(), position))
57}
58
59pub fn legal_moves_with_preprocessing<L: MoveAdder>(
61 position: &Position,
62 list: &mut L,
63 preprocessed_data: MoveGenPreprocessing,
64) -> bool {
65 let stm = position.state().stm;
66 let kings = position.bb_pc(KING.pc(stm));
67
68 let attacked_squares = king_danger_squares(kings, stm.flip(), position);
70
71 let king_sq = kings.bitscan();
72
73 let (checkers, pinned, pinners) = preprocessed_data.0;
74 let king_attacks_count = checkers.pop_count();
75
76 let enemy = position.bb_side(stm.flip());
80 let empty_squares = position.bb_empty();
81
82 let mut capture_mask = enemy;
83 let king_capture_mask = enemy & !attacked_squares;
84 let mut push_mask = empty_squares;
85 let king_push_mask = empty_squares & !attacked_squares;
86
87 match king_attacks_count.cmp(&1) {
88 Ordering::Greater => {
89 king_moves(position, king_capture_mask, king_push_mask, list);
91 return true;
92 }
93 Ordering::Less => (),
94 Ordering::Equal => {
95 capture_mask = checkers;
98 let checker_sq = checkers.bitscan();
99 let checker = position.at(checker_sq);
100 debug_assert!(checker.is_some());
101 if checker.is_slider() {
102 push_mask = squares_between(king_sq, checker_sq);
105 } else {
106 push_mask = EMPTY;
109 }
110 }
111 }
112
113 slider_moves(position, capture_mask, push_mask, pinned, king_sq, list);
115
116 knight_moves(position, capture_mask, push_mask, !pinned, list);
118
119 pawn_moves(position, capture_mask, push_mask, !pinned, list);
121
122 pawn_pin_ray_moves(
125 position,
126 capture_mask & pinners,
127 push_mask,
128 king_sq,
129 pinned,
130 stm,
131 list,
132 );
133
134 if king_attacks_count == 0 {
135 castles(position, attacked_squares, list);
139 }
140
141 king_moves(position, king_capture_mask, king_push_mask, list);
142
143 king_attacks_count > 0
144}
145
146#[allow(dead_code)]
152pub fn loud_legal_moves<L: MoveAdder>(position: &Position, list: &mut L) -> bool {
153 loud_legal_moves_with_preprocessing(position, list, movegen_preprocessing(position))
154}
155
156#[allow(dead_code)]
157pub fn loud_legal_moves_with_preprocessing<L: MoveAdder>(
158 position: &Position,
159 list: &mut L,
160 preprocessed_data: MoveGenPreprocessing,
161) -> bool {
162 let stm = position.state().stm;
163 let kings = position.bb_pc(KING.pc(stm));
164
165 let attacked_squares = king_danger_squares(kings, stm.flip(), position);
167
168 let king_sq = kings.bitscan();
169
170 let (checkers, pinned, pinners) = preprocessed_data.0;
171 let king_attacks_count = checkers.pop_count();
172
173 let enemy = position.bb_side(stm.flip());
177 let empty_squares = position.bb_empty();
178
179 let mut capture_mask = enemy;
180 let king_capture_mask = enemy & !attacked_squares;
181 let king_push_mask = empty_squares & !attacked_squares;
182
183 match king_attacks_count.cmp(&1) {
184 Ordering::Greater => {
185 king_moves(position, king_capture_mask, king_push_mask, list);
187 return true;
188 }
189 Ordering::Equal => {
190 capture_mask = checkers;
193 let checker_sq = checkers.bitscan();
194 let checker = position.at(checker_sq);
195 debug_assert!(checker.is_some());
196 let push_mask = if checker.is_slider() {
197 squares_between(king_sq, checker_sq)
200 } else {
201 EMPTY
204 };
205 king_moves(position, king_capture_mask, king_push_mask, list);
212 knight_moves(position, capture_mask, push_mask, !pinned, list);
213 slider_moves(position, capture_mask, push_mask, pinned, king_sq, list);
214 pawn_moves(position, capture_mask, push_mask, !pinned, list);
215 }
216 Ordering::Less => {
217 let push_mask = EMPTY;
218
219 pawn_captures(position, capture_mask, push_mask, !pinned, list);
225 pawn_pin_ray_captures(position, capture_mask & pinners, king_sq, pinned, stm, list);
226 knight_captures(position, capture_mask, !pinned, list);
227 slider_captures(position, capture_mask, pinned, king_sq, list);
228 king_captures(position, king_capture_mask, list);
229 }
230 }
231
232 king_attacks_count > 0
233}
234
235#[cfg(test)]
236mod test {
237 use super::*;
238 use crate::mv_list::MoveVec;
239 use crate::position::STARTING_POSITION_FEN;
240
241 macro_rules! test_legal_moves {
242 ($name:ident, $moves:expr, $fen:expr) => {
243 #[test]
244 fn $name() {
245 let mut list = MoveVec::new();
246 let position = &Position::from_fen($fen).unwrap();
247 legal_moves::<MoveVec>(position, &mut list);
248 if list.len() != $moves {
249 println!("Found {} moves, expected {}", list.len(), $moves);
250 println!("Moves: {}", list);
251 println!("{}", position);
252 }
253 assert_eq!(list.len(), $moves);
254 }
255 };
256 }
257
258 macro_rules! test_loud_legal_moves {
259 ($name:ident, $moves:expr, $fen:expr) => {
260 #[test]
261 fn $name() {
262 let mut list = MoveVec::new();
263 let position = &Position::from_fen($fen).unwrap();
264 loud_legal_moves::<MoveVec>(position, &mut list);
265 if list.len() != $moves {
266 println!("Found {} moves, expected {}", list.len(), $moves);
267 println!("Moves: {}", list);
268 println!("{}", position);
269 }
270 assert_eq!(list.len(), $moves);
271 }
272 };
273 }
274
275 test_legal_moves!(legal_moves_starting_position, 20, STARTING_POSITION_FEN);
276 test_legal_moves!(legal_moves_1, 8, "r6r/1b2k1bq/8/8/7B/8/8/R3K2R b QK - 3 2");
277 test_legal_moves!(legal_moves_2, 8, "8/8/8/2k5/2pP4/8/B7/4K3 b - d3 5 3");
278 test_legal_moves!(
279 legal_moves_3,
280 19,
281 "r1bqkbnr/pppppppp/n7/8/8/P7/1PPPPPPP/RNBQKBNR w QqKk - 2 2"
282 );
283 test_legal_moves!(
284 legal_moves_4,
285 5,
286 "r3k2r/p1pp1pb1/bn2Qnp1/2qPN3/1p2P3/2N5/PPPBBPPP/R3K2R b QqKk - 3 2"
287 );
288 test_legal_moves!(
289 legal_moves_5,
290 44,
291 "2kr3r/p1ppqpb1/bn2Qnp1/3PN3/1p2P3/2N5/PPPBBPPP/R3K2R b QK - 3 2"
292 );
293 test_legal_moves!(
294 legal_moves_6,
295 39,
296 "rnb2k1r/pp1Pbppp/2p5/q7/2B5/8/PPPQNnPP/RNB1K2R w QK - 3 9"
297 );
298
299 test_legal_moves!(legal_moves_7, 8, "5k2/8/8/q7/8/2Q5/8/4K3 w - -");
300
301 test_legal_moves!(legal_moves_8, 9, "2r5/3pk3/8/2P5/8/2K5/8/8 w - - 5 4");
302
303 test_legal_moves!(legal_moves_9, 3, "5k2/5pb1/5Q1B/8/8/8/8/4K3 b - - 1 1");
304
305 test_legal_moves!(legal_moves_10, 3, "4k3/8/5r2/8/8/8/4PPpP/5K2 w - - 0 1");
306
307 test_legal_moves!(
308 legal_moves_11,
309 5,
310 "4k3/3pq3/4Q3/1B2N3/1p2P3/2N5/PPPB1PP1/R3K2R b KQ - 0 1"
311 );
312
313 test_legal_moves!(legal_moves_12, 7, "8/8/8/6k1/4Pp2/8/8/K1B5 b - e3 0 1");
315
316 test_legal_moves!(legal_moves_13, 7, "8/8/8/8/R3Ppk1/8/8/K7 b - e3 0 1");
318
319 test_legal_moves!(legal_moves_14, 4, "4k3/3NqQ2/8/8/8/8/4p3/4K3 b - - 0 1");
320
321 test_legal_moves!(legal_moves_15, 8, "8/8/5k2/8/4Pp2/8/8/4KR2 b - e3 0 1");
322
323 test_loud_legal_moves!(loud_legal_moves_1, 1, "8/8/8/6k1/4Pp2/8/8/K1B5 b - e3 0 1");
324
325 test_loud_legal_moves!(
326 loud_legal_moves_2,
327 3,
328 "rnbqkbnr/pppppppp/8/2N5/8/8/PPP1PPPP/R1BQKBNR w KQkq - 0 1"
329 );
330
331 test_loud_legal_moves!(loud_legal_moves_3, 0, "8/8/8/8/R3Ppk1/8/8/K7 b - e3 0 1");
332}