1use super::*;
2use alloc::{
3 string::{String, ToString},
4 vec::Vec,
5};
6use core::cmp::Ordering;
7
8pub struct BoardBuilder {
9 board: Board,
10}
11
12impl From<Board> for BoardBuilder {
13 fn from(board: Board) -> Self {
14 Self { board }
15 }
16}
17
18impl Default for BoardBuilder {
19 fn default() -> Self {
20 let mut board = Board::empty();
21 board.white_castling_rights.disable_all();
22 board.black_castling_rights.disable_all();
23 Self { board }
24 }
25}
26
27impl BoardBuilder {
28 pub fn row(mut self, piece: Piece) -> Self {
29 let mut pos = piece.get_pos();
30 while pos.get_col() > 0 {
31 pos = pos.next_left()
32 }
33
34 for _ in 0..8 {
35 *self.board.get_square(pos) = Square::from(piece.move_to(pos));
36 pos = pos.next_right();
37 }
38
39 self
40 }
41
42 pub fn column(mut self, piece: Piece) -> Self {
43 let mut pos = piece.get_pos();
44 while pos.get_row() > 0 {
45 pos = pos.next_below()
46 }
47
48 for _ in 0..8 {
49 *self.board.get_square(pos) = Square::from(piece.move_to(pos));
50 pos = pos.next_above();
51 }
52
53 self
54 }
55
56 pub fn piece(mut self, piece: Piece) -> Self {
57 let pos = piece.get_pos();
58 *self.board.get_square(pos) = Square::from(piece);
59 self
60 }
61
62 pub fn enable_castling(mut self) -> Self {
63 self.board.black_castling_rights.enable_all();
64 self.board.white_castling_rights.enable_all();
65 self
66 }
67
68 pub fn disable_castling(mut self) -> Self {
69 self.board.black_castling_rights.disable_all();
70 self.board.white_castling_rights.disable_all();
71 self
72 }
73
74 pub fn enable_queenside_castle(mut self, color: Color) -> Self {
75 match color {
76 WHITE => self.board.white_castling_rights.enable_queenside(),
77 BLACK => self.board.black_castling_rights.enable_queenside(),
78 }
79 self
80 }
81
82 pub fn disable_queenside_castle(mut self, color: Color) -> Self {
83 match color {
84 WHITE => self.board.white_castling_rights.disable_queenside(),
85 BLACK => self.board.black_castling_rights.disable_queenside(),
86 }
87 self
88 }
89
90 pub fn enable_kingside_castle(mut self, color: Color) -> Self {
91 match color {
92 WHITE => self.board.white_castling_rights.enable_kingside(),
93 BLACK => self.board.black_castling_rights.enable_kingside(),
94 }
95 self
96 }
97
98 pub fn disable_kingside_castle(mut self, color: Color) -> Self {
99 match color {
100 WHITE => self.board.white_castling_rights.disable_kingside(),
101 BLACK => self.board.black_castling_rights.disable_kingside(),
102 }
103 self
104 }
105
106 pub fn build(self) -> Board {
107 self.board
108 }
109}
110
111#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
112pub struct CastlingRights {
113 kingside: bool,
114 queenside: bool,
115}
116
117impl Default for CastlingRights {
118 fn default() -> Self {
119 Self {
120 kingside: true,
121 queenside: true,
122 }
123 }
124}
125
126impl CastlingRights {
127 fn can_kingside_castle(&self) -> bool {
128 self.kingside
129 }
130
131 fn can_queenside_castle(&self) -> bool {
132 self.queenside
133 }
134
135 fn disable_kingside(&mut self) {
136 self.kingside = false
137 }
138
139 fn disable_queenside(&mut self) {
140 self.queenside = false
141 }
142
143 fn disable_all(&mut self) {
144 self.disable_kingside();
145 self.disable_queenside()
146 }
147
148 fn enable_kingside(&mut self) {
149 self.kingside = true
150 }
151
152 fn enable_queenside(&mut self) {
153 self.queenside = true
154 }
155
156 fn enable_all(&mut self) {
157 self.enable_kingside();
158 self.enable_queenside()
159 }
160}
161
162impl Default for Board {
163 fn default() -> Self {
164 BoardBuilder::default()
165 .piece(Piece::Rook(BLACK, A8))
166 .piece(Piece::Knight(BLACK, B8))
167 .piece(Piece::Bishop(BLACK, C8))
168 .piece(Piece::Queen(BLACK, D8))
169 .piece(Piece::King(BLACK, E8))
170 .piece(Piece::Bishop(BLACK, F8))
171 .piece(Piece::Knight(BLACK, G8))
172 .piece(Piece::Rook(BLACK, H8))
173 .row(Piece::Pawn(BLACK, A7))
174 .row(Piece::Pawn(WHITE, A2))
175 .piece(Piece::Rook(WHITE, A1))
176 .piece(Piece::Knight(WHITE, B1))
177 .piece(Piece::Bishop(WHITE, C1))
178 .piece(Piece::Queen(WHITE, D1))
179 .piece(Piece::King(WHITE, E1))
180 .piece(Piece::Bishop(WHITE, F1))
181 .piece(Piece::Knight(WHITE, G1))
182 .piece(Piece::Rook(WHITE, H1))
183 .enable_castling()
184 .build()
185 }
186}
187
188#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
189pub struct Board {
190 squares: [Square; 64],
191
192 en_passant: Option<Position>,
193
194 white_castling_rights: CastlingRights,
195 black_castling_rights: CastlingRights,
196
197 turn: Color,
198}
199
200impl Evaluate for Board {
201 #[inline]
202 fn value_for(&self, ally_color: Color) -> f64 {
203 self.squares
204 .iter()
205 .map(|square| match square.get_piece() {
206 Some(piece) => {
207 if piece.get_color() == ally_color {
208 piece.get_weighted_value()
209 } else {
210 -piece.get_weighted_value()
211 }
212 }
213 None => 0.0,
214 })
215 .sum()
216 }
217
218 #[inline]
219 fn get_current_player_color(&self) -> Color {
220 self.turn
221 }
222
223 #[inline]
224 fn apply_eval_move(&self, m: Move) -> Self {
225 self.apply_move(m).change_turn()
226 }
227
228 #[inline]
229 fn get_legal_moves(&self) -> Vec<Move> {
230 let mut result = vec![];
231 let color = self.get_current_player_color();
232 for square in &self.squares {
233 if let Some(piece) = square.get_piece() {
234 if piece.get_color() == color {
235 result.extend(piece.get_legal_moves(self))
236 }
237 }
238 }
239
240 result
241 }
242}
243
244impl core::fmt::Display for Board {
245 fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
246 let rating_bar = self.rating_bar(16);
247 let abc = if self.turn == WHITE {
248 "abcdefgh"
249 } else {
250 "hgfedcba"
251 };
252
253 write!(f, " {}\n ╔════════╗", abc)?;
254 let mut square_color = !self.turn;
255 let height = 8;
256 let width = 8;
257
258 for row in 0..height {
259 writeln!(f)?;
260
261 let print_row = match self.turn {
262 WHITE => height - row - 1,
263 BLACK => row,
264 };
265 write!(f, "{} ║", print_row + 1)?;
266
267 for col in 0..width {
268 let print_col = match self.turn {
269 BLACK => width - col - 1,
270 WHITE => col,
271 };
272
273 let pos = Position::new(print_row, print_col);
274
275 let s = if let Some(piece) = self.get_piece(pos) {
276 piece.to_string()
277 } else {
278 String::from(match square_color {
279 WHITE => "░",
280 BLACK => "▓",
281 })
282 };
283 if Some(pos) == self.en_passant {
284 write!(f, "\x1b[34m{}\x1b[m\x1b[0m", s)?;
285 } else if self.is_threatened(pos, self.turn) {
286 write!(f, "\x1b[31m{}\x1b[m\x1b[0m", s)?;
287 } else if self.is_threatened(pos, !self.turn) {
288 write!(f, "\x1b[32m{}\x1b[m\x1b[0m", s)?;
289 } else {
290 write!(f, "{}", s)?;
291 }
292
293 square_color = !square_color;
294 }
295 write!(f, "║")?;
296
297 if row == 2 {
298 let white_adv = self.get_material_advantage(WHITE);
299 let black_adv = self.get_material_advantage(BLACK);
300
301 match white_adv.cmp(&black_adv) {
302 Ordering::Equal => write!(f, " Both sides have equal material")?,
303 Ordering::Greater => write!(f, " White +{} points", white_adv)?,
304 Ordering::Less => write!(f, " Black +{} points", black_adv)?,
305 }
306 } else if row == 3 {
307 write!(f, " {} to move", self.turn)?;
308 } else if row == 4 {
309 write!(f, " [{}]", rating_bar)?;
310 }
311 square_color = !square_color;
312 }
313
314 write!(f, "\n ╚════════╝\n {}\n", abc)
315 }
316}
317
318impl Board {
319 pub fn horde() -> Self {
321 BoardBuilder::from(Board::default())
322 .row(Piece::Pawn(WHITE, A1))
323 .row(Piece::Pawn(WHITE, A2))
324 .row(Piece::Pawn(WHITE, A3))
325 .row(Piece::Pawn(WHITE, A4))
326 .piece(Piece::Pawn(WHITE, F5))
327 .piece(Piece::Pawn(WHITE, G5))
328 .piece(Piece::Pawn(WHITE, B5))
329 .piece(Piece::Pawn(WHITE, C5))
330 .build()
331 }
332
333 pub fn empty() -> Self {
334 Self {
335 squares: [EMPTY_SQUARE; 64],
336 en_passant: None,
337
338 white_castling_rights: CastlingRights::default(),
339 black_castling_rights: CastlingRights::default(),
340
341 turn: WHITE,
342 }
343 }
344
345 pub fn rating_bar(&self, len: usize) -> String {
346 let (best_m, _, your_best_val) = self.get_best_next_move(2);
347 let (_, _, your_lowest_val) = self.get_worst_next_move(2);
348 let mut your_val = your_best_val + your_lowest_val;
349 let (_, _, their_best_val) = self.apply_move(best_m).change_turn().get_best_next_move(2);
350 let (_, _, their_lowest_val) = self.apply_move(best_m).change_turn().get_worst_next_move(2);
351 let mut their_val = their_best_val + their_lowest_val;
352
353 if your_val < 0.0 {
354 your_val = -your_val;
355 their_val += your_val * 2.0;
356 }
357
358 if their_val < 0.0 {
359 their_val = -their_val;
360 your_val += their_val * 2.0;
361 }
362
363 let your_percentage = your_val / (your_val + their_val);
364 let their_percentage = their_val / (your_val + their_val);
365
366 let (your_color, their_color) = match self.turn {
367 WHITE => ("▓", "░"),
368 BLACK => ("░", "▓"),
369 };
370
371 let white = match self.turn {
372 WHITE => your_color.repeat((your_percentage * len as f64) as usize),
373 BLACK => their_color.repeat((their_percentage * len as f64) as usize),
374 };
375
376 let black = match self.turn {
377 BLACK => your_color.repeat((your_percentage * len as f64) as usize),
378 WHITE => their_color.repeat((their_percentage * len as f64) as usize),
379 };
380
381 white + &black
382 }
383
384 #[inline]
386 pub fn get_turn_color(&self) -> Color {
387 self.turn
388 }
389
390 pub fn get_en_passant(&self) -> Option<Position> {
392 self.en_passant
393 }
394
395 pub fn remove_all(&self, color: Color) -> Self {
397 let mut result = *self;
398 for square in &mut result.squares {
399 if let Some(piece) = square.get_piece() {
400 if piece.get_color() == color {
401 *square = EMPTY_SQUARE
402 }
403 }
404 }
405
406 result
407 }
408
409 pub fn queen_all(&self, color: Color) -> Self {
411 let mut result = *self;
412 for square in &mut result.squares {
413 if let Some(piece) = square.get_piece() {
414 if !piece.is_king() && piece.get_color() == color {
415 *square = Square::from(Piece::Queen(color, piece.get_pos()))
416 }
417 }
418 }
419
420 result
421 }
422
423 #[inline]
425 pub fn set_turn(&self, color: Color) -> Self {
426 let mut result = *self;
427 result.turn = color;
428 result
429 }
430
431 #[inline]
433 pub fn get_material_advantage(&self, color: Color) -> i32 {
434 self.squares
435 .iter()
436 .map(|square| match square.get_piece() {
437 Some(piece) => {
438 if piece.get_color() == color {
439 piece.get_material_value()
440 } else {
441 -piece.get_material_value()
442 }
443 }
444 None => 0,
445 })
446 .sum()
447 }
448
449 #[inline]
450 fn get_square(&mut self, pos: Position) -> &mut Square {
451 &mut self.squares[((7 - pos.get_row()) * 8 + pos.get_col()) as usize]
452 }
453
454 #[inline]
455 fn add_piece(&mut self, piece: Piece) {
456 let pos = piece.get_pos();
457 *self.get_square(pos) = Square::from(piece);
458 }
459
460 #[inline]
462 pub fn get_piece(&self, pos: Position) -> Option<Piece> {
463 if pos.is_off_board() {
464 return None;
465 }
466 self.squares[((7 - pos.get_row()) * 8 + pos.get_col()) as usize].get_piece()
467 }
468
469 #[inline]
471 pub fn has_ally_piece(&self, pos: Position, ally_color: Color) -> bool {
472 if let Some(piece) = self.get_piece(pos) {
473 piece.get_color() == ally_color
474 } else {
475 false
476 }
477 }
478
479 #[inline]
486 pub fn has_enemy_piece(&self, pos: Position, ally_color: Color) -> bool {
487 if let Some(piece) = self.get_piece(pos) {
488 piece.get_color() == !ally_color
489 } else {
490 false
491 }
492 }
493
494 #[inline]
497 pub fn has_piece(&self, pos: Position) -> bool {
498 self.get_piece(pos) != None
499 }
500
501 #[inline]
504 pub fn has_no_piece(&self, pos: Position) -> bool {
505 self.get_piece(pos) == None
506 }
507
508 pub fn get_king_pos(&self, color: Color) -> Option<Position> {
510 let mut king_pos = None;
511 for square in &self.squares {
512 if let Some(Piece::King(c, pos)) = square.get_piece() {
513 if c == color {
514 king_pos = Some(pos);
515 }
516 }
517 }
518 king_pos
519 }
520
521 pub fn is_threatened(&self, pos: Position, ally_color: Color) -> bool {
523 for (i, square) in self.squares.iter().enumerate() {
524 let row = 7 - i / 8;
525 let col = i % 8;
526 let square_pos = Position::new(row as i32, col as i32);
527 if !square_pos.is_orthogonal_to(pos) && !square_pos.is_diagonal_to(pos) && !square_pos.is_knight_move(pos) {
528 continue;
529 }
530
531 if let Some(piece) = square.get_piece() {
532 if piece.get_color() == ally_color {
533 continue;
534 }
535
536 if piece.is_legal_attack(pos, self) {
537 return true;
538 }
539 }
540 }
541
542 false
543 }
544
545 #[inline]
547 pub fn is_in_check(&self, color: Color) -> bool {
548 if let Some(king_pos) = self.get_king_pos(color) {
549 self.is_threatened(king_pos, color)
550 } else {
551 false
552 }
553 }
554
555 fn move_piece(&self, from: Position, to: Position) -> Self {
556 let mut result = *self;
557 result.en_passant = None;
558
559 if from.is_off_board() || to.is_off_board() {
560 return result;
561 }
562
563 let from_square = result.get_square(from);
564 if let Some(mut piece) = from_square.get_piece() {
565 *from_square = EMPTY_SQUARE;
566
567 if piece.is_pawn() && (to.get_row() == 0 || to.get_row() == 7) {
568 piece = Piece::Queen(piece.get_color(), piece.get_pos());
569 }
570
571 if piece.is_starting_pawn() && (from.get_row() - to.get_row()).abs() == 2 {
572 result.en_passant = Some(to.pawn_back(piece.get_color()))
573 }
574
575 result.add_piece(piece.move_to(to));
576
577 let castling_rights = match piece.get_color() {
578 WHITE => &mut result.white_castling_rights,
579 BLACK => &mut result.black_castling_rights,
580 };
581
582 if piece.is_king() {
583 castling_rights.disable_all();
584 } else if piece.is_queenside_rook() {
585 castling_rights.disable_queenside();
586 } else if piece.is_kingside_rook() {
587 castling_rights.disable_kingside();
588 }
589 }
590
591 result
592 }
593
594 pub fn can_kingside_castle(&self, color: Color) -> bool {
596 let right_of_king = Position::king_pos(color).next_right();
597 match color {
598 WHITE => {
599 self.has_no_piece(Position::new(0, 5))
600 && self.has_no_piece(Position::new(0, 6))
601 && self.get_piece(Position::new(0, 7)) == Some(Piece::Rook(color, Position::new(0, 7)))
602 && self.white_castling_rights.can_kingside_castle()
603 && !self.is_in_check(color)
604 && !self.is_threatened(right_of_king, color)
605 && !self.is_threatened(right_of_king.next_right(), color)
606 }
607 BLACK => {
608 self.has_no_piece(Position::new(7, 5))
609 && self.has_no_piece(Position::new(7, 6))
610 && self.get_piece(Position::new(7, 7)) == Some(Piece::Rook(color, Position::new(7, 7)))
611 && self.black_castling_rights.can_kingside_castle()
612 && !self.is_in_check(color)
613 && !self.is_threatened(right_of_king, color)
614 && !self.is_threatened(right_of_king.next_right(), color)
615 }
616 }
617 }
618
619 pub fn can_queenside_castle(&self, color: Color) -> bool {
621 match color {
622 WHITE => {
623 self.has_no_piece(Position::new(0, 1))
624 && self.has_no_piece(Position::new(0, 2))
625 && self.has_no_piece(Position::new(0, 3))
626 && self.get_piece(Position::new(0, 0)) == Some(Piece::Rook(color, Position::new(0, 0)))
627 && self.white_castling_rights.can_queenside_castle()
628 && !self.is_in_check(color)
629 && !self.is_threatened(Position::queen_pos(color), color)
630 }
631 BLACK => {
632 self.has_no_piece(Position::new(7, 1))
633 && self.has_no_piece(Position::new(7, 2))
634 && self.has_no_piece(Position::new(7, 3))
635 && self.get_piece(Position::new(7, 0)) == Some(Piece::Rook(color, Position::new(7, 0)))
636 && self.black_castling_rights.can_queenside_castle()
637 && !self.is_in_check(color)
638 && !self.is_threatened(Position::queen_pos(color), color)
639 }
640 }
641 }
642
643 pub(crate) fn is_legal_move(&self, m: Move, player_color: Color) -> bool {
644 match m {
645 Move::KingSideCastle => self.can_kingside_castle(player_color),
646 Move::QueenSideCastle => self.can_queenside_castle(player_color),
647 Move::Piece(from, to) => match self.get_piece(from) {
648 Some(Piece::Pawn(c, pos)) => {
649 let piece = Piece::Pawn(c, pos);
650 ((if let Some(en_passant) = self.en_passant {
651 (en_passant == from.pawn_up(player_color).next_left()
652 || en_passant == from.pawn_up(player_color).next_right()
653 && en_passant == to)
654 && c == player_color
655 } else {
656 false
657 }) || piece.is_legal_move(to, self)
658 && piece.get_color() == player_color)
659 && !self.apply_move(m).is_in_check(player_color)
660 }
661 Some(piece) => {
662 piece.is_legal_move(to, self)
663 && piece.get_color() == player_color
664 && !self.apply_move(m).is_in_check(player_color)
665 }
666 _ => false,
667 },
668 Move::Resign => true,
669 }
670 }
671
672 pub fn has_sufficient_material(&self, color: Color) -> bool {
674 let mut pieces = vec![];
675 for square in &self.squares {
676 if let Some(piece) = square.get_piece() {
677 if piece.get_color() == color {
678 pieces.push(piece);
679 }
680 }
681 }
682
683 pieces.sort();
684
685 if pieces.len() == 0 {
686 false
687 } else if pieces.len() == 1 && pieces[0].is_king() {
688 false
689 } else if pieces.len() == 2 && pieces[0].is_king() && pieces[1].is_knight() {
690 false
691 } else if pieces.len() == 2 && pieces[0].is_king() && pieces[1].is_bishop() {
692 false
693 } else if pieces.len() == 3
694 && pieces[0].is_king()
695 && pieces[1].is_knight()
696 && pieces[2].is_knight()
697 {
698 false
699 } else if pieces.len() == 3
700 && pieces[0].is_king()
701 && pieces[1].is_bishop()
702 && pieces[2].is_bishop()
703 {
704 false
705 } else {
706 true
707 }
708 }
709
710 #[inline]
712 pub fn has_insufficient_material(&self, color: Color) -> bool {
713 !self.has_sufficient_material(color)
714 }
715
716 pub fn is_stalemate(&self) -> bool {
718 (self.get_legal_moves().is_empty() && !self.is_in_check(self.get_current_player_color()))
719 || (self.has_insufficient_material(self.turn)
720 && self.has_insufficient_material(!self.turn))
721 }
722
723 pub fn is_checkmate(&self) -> bool {
725 self.is_in_check(self.get_current_player_color()) && self.get_legal_moves().is_empty()
726 }
727
728 #[inline]
730 pub fn change_turn(mut self) -> Self {
731 self.turn = !self.turn;
732 self
733 }
734
735 fn apply_move(&self, m: Move) -> Self {
736 match m {
737 Move::KingSideCastle => {
738 if let Some(king_pos) = self.get_king_pos(self.turn) {
739 let rook_pos = match self.turn {
740 WHITE => Position::new(0, 7),
741 BLACK => Position::new(7, 7),
742 };
743 self.move_piece(king_pos, rook_pos.next_left())
744 .move_piece(rook_pos, king_pos.next_right())
745 } else {
746 *self
747 }
748 }
749 Move::QueenSideCastle => {
750 if let Some(king_pos) = self.get_king_pos(self.turn) {
751 let rook_pos = match self.turn {
752 WHITE => Position::new(0, 0),
753 BLACK => Position::new(7, 0),
754 };
755 self.move_piece(king_pos, king_pos.next_left().next_left())
756 .move_piece(rook_pos, king_pos.next_left())
757 } else {
758 *self
759 }
760 }
761
762 Move::Piece(from, to) => {
763 let mut result = self.move_piece(from, to);
764
765 if let (Some(en_passant), Some(Piece::Pawn(player_color, _))) =
766 (self.en_passant, self.get_piece(from))
767 {
768 if (en_passant == from.pawn_up(player_color).next_left()
769 || en_passant == from.pawn_up(player_color).next_right())
770 && en_passant == to
771 {
772 result.squares[((7 - en_passant.pawn_back(player_color).get_row()) * 8
773 + en_passant.get_col())
774 as usize] = EMPTY_SQUARE;
775 }
776 }
777
778 result
779 }
780 Move::Resign => self.remove_all(self.turn).queen_all(!self.turn),
781 }
782 }
783
784 pub fn play_move(&self, m: Move) -> GameResult {
786 let current_color = self.get_turn_color();
787
788 if m == Move::Resign {
789 GameResult::Victory(!current_color)
790 } else if self.is_legal_move(m, current_color) {
791 let next_turn = self.apply_move(m).change_turn();
792 if next_turn.is_checkmate() {
793 GameResult::Victory(current_color)
794 } else if next_turn.is_stalemate() {
795 GameResult::Stalemate
796 } else {
797 GameResult::Continuing(next_turn)
798 }
799 } else {
800 GameResult::IllegalMove(m)
801 }
802 }
803}