1use std::ops::Deref;
2use std::ops::Index;
3use std::ops::IndexMut;
4
5use crate::fen::FEN;
6use crate::mailbox;
7use crate::movegen::*;
8use crate::zobrist;
9use crate::zobrist::PositionHash;
10
11pub(crate) const ABOVE_BELOW: usize = 8; #[derive(Debug, PartialEq, Clone, Copy)]
14pub struct Pos64([Square; 64]);
15impl Index<usize> for Pos64 {
16 type Output = Square;
17
18 fn index(&self, index: usize) -> &Self::Output {
19 &self.0[index]
20 }
21}
22impl IndexMut<usize> for Pos64 {
23 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
24 &mut self.0[index]
25 }
26}
27impl Deref for Pos64 {
28 type Target = [Square; 64];
29
30 fn deref(&self) -> &Self::Target {
31 &self.0
32 }
33}
34impl Default for Pos64 {
35 fn default() -> Self {
36 Self([Square::Empty; 64])
37 }
38}
39impl Pos64 {
40 #[inline(always)]
42 pub fn polyglot_is_pawn_beside(&self, i: usize, pawn_colour: PieceColour) -> bool {
43 let piece = Piece {
44 pcolour: pawn_colour,
45 ptype: PieceType::Pawn,
46 };
47 let left = mailbox::next_mailbox_number(i, -1);
48 if left >= 0 {
50 if let Square::Piece(p) = &self[left as usize] {
51 if p == &piece {
52 return true;
53 }
54 }
55 }
56 let right = mailbox::next_mailbox_number(i, 1);
57 if right >= 0 {
59 if let Square::Piece(p) = &self[right as usize] {
60 if p == &piece {
61 return true;
62 }
63 }
64 }
65 false
66 }
67}
68
69#[derive(Debug, PartialEq, Clone)]
70pub struct AttackMap(Vec<Move>);
71impl AttackMap {
72 const fn new() -> Self {
73 Self(Vec::new())
74 }
75
76 fn new_no_alloc() -> Self {
77 Self(Vec::with_capacity(0))
78 }
79
80 fn clear(&mut self) {
81 self.0.clear();
82 }
83}
84
85#[derive(Debug, PartialEq, Eq, Clone, Copy)]
86pub struct DefendMap([bool; 64]);
87impl DefendMap {
88 const fn new() -> Self {
89 Self([false; 64])
90 }
91
92 fn clear(&mut self) {
93 self.0 = [false; 64];
94 }
95}
96
97impl MoveMap for DefendMap {
98 fn add_move(&mut self, mv: &Move) {
99 self.0[mv.to] = true;
100 }
101}
102
103impl MoveMap for AttackMap {
104 fn add_move(&mut self, mv: &Move) {
105 self.0.push(*mv);
106 }
107}
108
109#[derive(Debug, Clone)]
110pub struct Position {
111 pub pos64: Pos64,
112 pub side: PieceColour,
113 pub movegen_flags: MovegenFlags,
114 defend_map: DefendMap, attack_map: AttackMap, wking_idx: usize,
117 bking_idx: usize,
118}
119impl Position {
120 pub fn new_starting() -> Self {
122 let mut pos: Pos64 = Pos64::default();
123
124 let movegen_flags = MovegenFlags {
125 white_castle_short: true,
126 white_castle_long: true,
127 black_castle_short: true,
128 black_castle_long: true,
129 en_passant: None,
130 polyglot_en_passant: None,
131 };
132
133 pos[0] = Square::Piece(Piece {
134 pcolour: PieceColour::Black,
135 ptype: PieceType::Rook,
136 });
137 pos[1] = Square::Piece(Piece {
138 pcolour: PieceColour::Black,
139 ptype: PieceType::Knight,
140 });
141 pos[2] = Square::Piece(Piece {
142 pcolour: PieceColour::Black,
143 ptype: PieceType::Bishop,
144 });
145 pos[3] = Square::Piece(Piece {
146 pcolour: PieceColour::Black,
147 ptype: PieceType::Queen,
148 });
149 pos[4] = Square::Piece(Piece {
150 pcolour: PieceColour::Black,
151 ptype: PieceType::King,
152 });
153 pos[5] = Square::Piece(Piece {
154 pcolour: PieceColour::Black,
155 ptype: PieceType::Bishop,
156 });
157 pos[6] = Square::Piece(Piece {
158 pcolour: PieceColour::Black,
159 ptype: PieceType::Knight,
160 });
161 pos[7] = Square::Piece(Piece {
162 pcolour: PieceColour::Black,
163 ptype: PieceType::Rook,
164 });
165 for i in 8..16 {
166 pos[i] = Square::Piece(Piece {
167 pcolour: PieceColour::Black,
168 ptype: PieceType::Pawn,
169 });
170 }
171 for i in 16..48 {
172 pos[i] = Square::Empty;
173 }
174 for i in 48..56 {
175 pos[i] = Square::Piece(Piece {
176 pcolour: PieceColour::White,
177 ptype: PieceType::Pawn,
178 });
179 }
180 pos[56] = Square::Piece(Piece {
181 pcolour: PieceColour::White,
182 ptype: PieceType::Rook,
183 });
184 pos[57] = Square::Piece(Piece {
185 pcolour: PieceColour::White,
186 ptype: PieceType::Knight,
187 });
188 pos[58] = Square::Piece(Piece {
189 pcolour: PieceColour::White,
190 ptype: PieceType::Bishop,
191 });
192 pos[59] = Square::Piece(Piece {
193 pcolour: PieceColour::White,
194 ptype: PieceType::Queen,
195 });
196 pos[60] = Square::Piece(Piece {
197 pcolour: PieceColour::White,
198 ptype: PieceType::King,
199 });
200 pos[61] = Square::Piece(Piece {
201 pcolour: PieceColour::White,
202 ptype: PieceType::Bishop,
203 });
204 pos[62] = Square::Piece(Piece {
205 pcolour: PieceColour::White,
206 ptype: PieceType::Knight,
207 });
208 pos[63] = Square::Piece(Piece {
209 pcolour: PieceColour::White,
210 ptype: PieceType::Rook,
211 });
212
213 let side = PieceColour::White;
214
215 let mut new = Self {
216 pos64: pos,
217 side,
218 movegen_flags,
219 defend_map: DefendMap::new(),
220 attack_map: AttackMap::new(),
221 wking_idx: 60,
222 bking_idx: 4,
223 };
224 new.gen_maps();
225 new
226 }
227
228 pub(crate) fn new_from_pub_parts(
229 pos64: Pos64,
230 side: PieceColour,
231 movegen_flags: MovegenFlags,
232 ) -> Self {
233 let mut new = Self {
234 pos64,
235 side,
236 movegen_flags,
237 defend_map: DefendMap::new(),
238 attack_map: AttackMap::new(),
239 wking_idx: 0,
240 bking_idx: 0,
241 };
242 new.update_king_idx();
243 new.gen_maps();
244 new
245 }
246
247 pub fn new_position(&self, mv: &Move) -> Self {
249 let mut new_pos = self.clone();
250 new_pos.set_en_passant_flag(mv);
251 new_pos.set_castle_flags(mv);
252 new_pos.set_king_position(mv);
253
254 match mv.move_type {
255 MoveType::EnPassant(ep_capture) => {
256 new_pos.pos64[ep_capture] = Square::Empty;
258 }
259 MoveType::Castle(castle_mv) => {
260 new_pos.pos64[castle_mv.rook_to] = new_pos.pos64[castle_mv.rook_from];
261 new_pos.pos64[castle_mv.rook_from] = Square::Empty;
262 }
263 MoveType::Promotion(ptype, _) => match &mut new_pos.pos64[mv.from] {
264 Square::Piece(p) => {
265 p.ptype = ptype;
266 }
267 Square::Empty => {
268 unreachable!();
269 }
270 },
271 _ => {}
272 }
273
274 new_pos.pos64[mv.to] = new_pos.pos64[mv.from];
275 new_pos.pos64[mv.from] = Square::Empty;
276
277 new_pos.toggle_side();
278 new_pos.gen_maps();
279 new_pos
280 }
281
282 #[inline(always)]
283 pub fn pos_hash(&self) -> PositionHash {
284 zobrist::pos_hash(self)
285 }
286
287 #[inline(always)]
288 fn update_king_idx(&mut self) {
289 for (i, s) in self.pos64.iter().enumerate() {
290 if let Square::Piece(p) = s {
291 if p.ptype == PieceType::King {
292 if p.pcolour == PieceColour::White {
293 self.wking_idx = i;
294 } else {
295 self.bking_idx = i;
296 }
297 }
298 }
299 }
300 }
301
302 #[inline(always)]
304 fn set_king_position(&mut self, mv: &Move) {
305 if mv.piece.ptype == PieceType::King {
306 if mv.piece.pcolour == PieceColour::White {
307 self.wking_idx = mv.to;
308 } else {
309 self.bking_idx = mv.to;
310 }
311 }
312 }
313 #[inline(always)]
315 fn test_clone(&self) -> Self {
316 Self {
317 pos64: self.pos64,
318 side: self.side,
319 movegen_flags: self.movegen_flags,
320 defend_map: self.defend_map,
321 attack_map: AttackMap::new_no_alloc(),
323 wking_idx: self.wking_idx,
324 bking_idx: self.bking_idx,
325 }
326 }
327
328 pub fn is_move_legal(&self, mv: &Move) -> bool {
329 if mv.piece.ptype == PieceType::King {
333 if self.is_defended(mv.to) {
334 return false;
335 }
336 if let MoveType::Castle(castle_mv) = mv.move_type {
337 return !(self.is_defended(castle_mv.king_squares.0)
339 || self.is_defended(castle_mv.king_squares.1)
340 || self.is_defended(castle_mv.king_squares.2));
341 }
342 }
343
344 let mut test_pos = self.test_clone();
345 test_pos.set_king_position(mv);
346
347 if let MoveType::EnPassant(ep_capture) = mv.move_type {
348 test_pos.pos64[ep_capture] = Square::Empty;
349 }
350
351 test_pos.pos64[mv.to] = test_pos.pos64[mv.from];
352 test_pos.pos64[mv.from] = Square::Empty;
353
354 !movegen_in_check(&test_pos.pos64, test_pos.get_king_idx())
355 }
356
357 #[inline(always)]
358 fn toggle_side(&mut self) {
359 self.side = if self.side == PieceColour::White {
360 PieceColour::Black
361 } else {
362 PieceColour::White
363 };
364 }
365
366 #[inline(always)]
367 const fn is_defended(&self, i: usize) -> bool {
368 self.defend_map.0[i]
369 }
370
371 #[inline(always)]
372 fn get_king_idx(&self) -> usize {
373 if self.side == PieceColour::White {
374 self.wking_idx
375 } else {
376 self.bking_idx
377 }
378 }
379
380 #[inline(always)]
381 pub fn is_in_check(&self) -> bool {
382 self.is_defended(self.get_king_idx())
383 }
384
385 pub fn get_pseudo_legal_moves(&self) -> &Vec<Move> {
386 &self.attack_map.0
387 }
388
389 pub fn get_legal_moves(&self) -> Vec<&Move> {
390 let mut legal_moves = Vec::with_capacity(self.attack_map.0.len());
391 for mv in &self.attack_map.0 {
392 if self.is_move_legal(mv) {
393 legal_moves.push(mv);
394 }
395 }
396 legal_moves
397 }
398
399 #[inline(always)]
401 fn set_en_passant_flag(&mut self, mv: &Move) {
402 if mv.move_type == MoveType::DoublePawnPush {
403 if self.pos64.polyglot_is_pawn_beside(mv.to, !self.side) {
405 self.movegen_flags.polyglot_en_passant = Some(mv.to);
406 } else {
407 self.movegen_flags.polyglot_en_passant = None;
408 }
409 self.movegen_flags.en_passant = Some(mv.to);
410 } else {
411 self.movegen_flags.en_passant = None;
412 }
413 }
414
415 #[inline(always)]
416 fn set_castle_flags(&mut self, mv: &Move) {
417 if mv.piece.ptype == PieceType::King {
418 if mv.piece.pcolour == PieceColour::White {
419 self.movegen_flags.white_castle_long = false;
420 self.movegen_flags.white_castle_short = false;
421 } else {
422 self.movegen_flags.black_castle_long = false;
423 self.movegen_flags.black_castle_short = false;
424 }
425 }
426 match mv.from {
427 LONG_BLACK_ROOK_START => {
428 self.movegen_flags.black_castle_long = false;
429 }
430 LONG_WHITE_ROOK_START => {
431 self.movegen_flags.white_castle_long = false;
432 }
433 SHORT_BLACK_ROOK_START => {
434 self.movegen_flags.black_castle_short = false;
435 }
436 SHORT_WHITE_ROOK_START => {
437 self.movegen_flags.white_castle_short = false;
438 }
439 _ => {}
440 }
441 match mv.to {
443 LONG_BLACK_ROOK_START => {
444 self.movegen_flags.black_castle_long = false;
445 }
446 LONG_WHITE_ROOK_START => {
447 self.movegen_flags.white_castle_long = false;
448 }
449 SHORT_BLACK_ROOK_START => {
450 self.movegen_flags.black_castle_short = false;
451 }
452 SHORT_WHITE_ROOK_START => {
453 self.movegen_flags.white_castle_short = false;
454 }
455 _ => {}
456 }
457 }
458
459 pub(crate) fn gen_maps(&mut self) {
460 self.defend_map.clear();
461 self.attack_map.clear();
462
463 let pos64 = &self.pos64;
464 let movegen_flags = &self.movegen_flags;
465 let side = self.side;
466 for (i, s) in pos64.iter().enumerate() {
467 if let Square::Piece(p) = s {
468 let is_defending = p.pcolour != side;
469 let map: &mut dyn MoveMap = if is_defending {
470 &mut self.defend_map
471 } else {
472 &mut self.attack_map
473 };
474 movegen(pos64, movegen_flags, *p, i, is_defending, map);
475 }
476 }
477 }
478}
479
480impl From<FEN> for Position {
481 fn from(fen: FEN) -> Self {
482 Self::new_from_pub_parts(fen.pos64(), fen.side(), fen.movegen_flags())
483 }
484}