checke_rs/
board.rs

1use thiserror::Error;
2
3use crate::bitboard::BitBoard;
4use crate::position::{MoveIter, Square};
5use crate::turn::Turn;
6
7const INITIAL_RED_PIECES: BitBoard = BitBoard::new(0x2AD5AA);
8const INITIAL_BLACK_PIECES: BitBoard = BitBoard::new(0x156AD50000000000);
9const INITIAL_KINGS: BitBoard = BitBoard::new(0x0);
10
11#[derive(Debug, Error)]
12pub enum MoveError {}
13
14/// Represents the current status of a board instance.
15#[derive(PartialEq, Debug)]
16pub enum BoardStatus {
17    /// The player to move still has valid moves that can be played and is therefor the game is
18    /// still ongoing.
19    OnGoing,
20
21    /// The player to move no longer has any valid moves and therefore the game has been
22    /// completed.
23    Complete,
24}
25
26/// Represents the player disc color
27#[derive(PartialEq, Debug, Copy, Clone)]
28pub enum Player {
29    Red,
30    Black,
31}
32
33impl Default for Board {
34    /// Creates a fresh board with pieces in starting positions.
35    fn default() -> Self {
36        Board {
37            current_player: Player::Black,
38            red_pieces: INITIAL_RED_PIECES,
39            black_pieces: INITIAL_BLACK_PIECES,
40            kings: INITIAL_KINGS,
41        }
42    }
43}
44
45#[derive(Debug)]
46pub struct Board {
47    current_player: Player,
48    red_pieces: BitBoard,
49    black_pieces: BitBoard,
50    kings: BitBoard,
51}
52
53impl Board {
54    /// Creates an empty [Board] instance.
55    pub fn empty() -> Self {
56        Board {
57            red_pieces: BitBoard::new(0),
58            black_pieces: BitBoard::new(0),
59            kings: BitBoard::new(0),
60            ..Board::default()
61        }
62    }
63
64    /// Retrieves the player that is able to take a turn on this board instance.
65    pub fn current_player(&self) -> Player { self.current_player }
66
67    /// Calculates the current status of the game based on if the [Board::current_player] currently
68    /// has any available moves to make.
69    pub fn status(&self) -> BoardStatus {
70        let mut player_moves = MoveIter::new(self, self.current_player);
71        match player_moves.next() {
72            Some(_) => BoardStatus::OnGoing,
73            None => BoardStatus::Complete
74        }
75    }
76
77    /// Applies a turn to the game board and changes the state of the board and
78    /// the position of its pieces.
79    pub fn push_turn(&mut self, turn: Turn) -> Result<(), MoveError> {
80        todo!()
81    }
82
83    /// Pops the most recent turn off the turn stack
84    pub fn pop_turn(&mut self) {
85        todo!()
86    }
87
88    /// Retrieves a bitboard representing where all red pieces are on the board.
89    pub fn red_pieces(&self) -> BitBoard {
90        self.red_pieces
91    }
92
93    /// Retrieves a bitboard representing where all black pieces are on the board.
94    pub fn black_pieces(&self) -> BitBoard {
95        self.black_pieces
96    }
97
98    /// Retrieves a bitboard representing where all pieces of a specified player are on the board.
99    pub fn pieces_by_player(&self, player: Player) -> BitBoard {
100        match player {
101            Player::Red => self.red_pieces,
102            Player::Black => self.black_pieces
103        }
104    }
105
106    /// Retrieves a bitboard representing where all pieces are on the board.
107    pub fn all_pieces(&self) -> BitBoard {
108        self.red_pieces | self.black_pieces
109    }
110
111    /// Retrieves a bitboard representing where the red king pieces are on the board.
112    pub fn red_kings(&self) -> BitBoard {
113        self.red_pieces & self.kings
114    }
115
116    /// Retrieves a bitboard representing where the black king pieces are on the board.
117    pub fn black_kings(&self) -> BitBoard {
118        self.black_pieces & self.kings
119    }
120
121    /// Retrieves a bitboard representing where all kings of a specified player are on the board.
122    pub fn kings_by_player(&self, player: Player) -> BitBoard {
123        match player {
124            Player::Red => self.red_kings(),
125            Player::Black => self.black_kings()
126        }
127    }
128
129    /// Retrieves a bitboard representing all king pieces are on the board.
130    pub fn all_kings(&self) -> BitBoard { self.kings }
131
132    /// Provides either a positive or negative 1 denoting which direction the current player
133    /// should be progressing on the board. A positive 1 represents the current player should be
134    /// moving down the board or towards the larger square values. Vice versa with -1.
135    pub fn direction(&self) -> i8 {
136        match self.current_player {
137            Player::Red => 1,
138            Player::Black => -1
139        }
140    }
141}
142
143#[derive(Debug, Error, PartialEq)]
144pub enum BoardCreationError {
145    #[error("Only a single piece can be placed per square.")]
146    DuplicateSquareAssignments
147}
148
149#[derive(Debug)]
150struct Placement {
151    player: Player,
152    square: Square,
153    is_king: bool,
154}
155
156/// Used to create complex board configurations with reasonable safety.
157#[derive(Debug)]
158pub struct BoardBuilder {
159    current_player: Player,
160    placements: Vec<Placement>,
161}
162
163impl BoardBuilder {
164    /// Sets the current player the board will be built with.
165    pub fn current_player(&mut self, player: Player) -> &mut Self {
166        self.current_player = player;
167        self
168    }
169
170    /// Adds a normal piece on the board.
171    pub fn place_piece(&mut self, player: Player, square: Square) -> &mut Self {
172        let placement = Placement { player, square, is_king: false };
173        self.placements.push(placement);
174        self
175    }
176
177    /// Adds a king piece on the board.
178    pub fn place_king(&mut self, player: Player, square: Square) -> &mut Self {
179        let placement = Placement { player, square, is_king: true };
180        self.placements.push(placement);
181        self
182    }
183
184    /// Attempts to construct a new [Board] instance given the previous details.
185    /// An error will be returned if during the build process multiple pieces were placed
186    /// on the same square.
187    pub fn build(&self) -> Result<Board, BoardCreationError> {
188        let current_player = self.current_player;
189        let mut red_pieces = BitBoard::new(0);
190        let mut black_pieces = BitBoard::new(0);
191        let mut kings = BitBoard::new(0);
192        for placement in &self.placements {
193            let piece = placement.square.to_bitboard();
194            if (red_pieces | black_pieces).contains(piece) {
195                return Err(BoardCreationError::DuplicateSquareAssignments);
196            }
197
198            match placement.player {
199                Player::Red => { red_pieces = red_pieces | piece }
200                Player::Black => { black_pieces = black_pieces | piece }
201            }
202
203            if placement.is_king {
204                kings = kings | piece
205            }
206        }
207
208        let board = Board { current_player, red_pieces, black_pieces, kings };
209        Ok(board)
210    }
211}
212
213impl Default for BoardBuilder {
214    fn default() -> Self {
215        BoardBuilder {
216            current_player: Player::Black,
217            placements: vec![],
218        }
219    }
220}