myopic_board/lib.rs
1#[macro_use]
2extern crate lazy_static;
3
4use anyhow::Result;
5
6pub use mv::Move;
7pub use myopic_core::*;
8pub use parse::uci::UciMove;
9
10use crate::enumset::EnumSet;
11pub use crate::imp::Board;
12
13mod imp;
14mod mv;
15mod parse;
16
17/// The start position of a chess game encoded in FEN format
18pub const STARTPOS_FEN: &'static str = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
19
20/// Return the start position of a standard game
21pub fn start() -> Board {
22 STARTPOS_FEN.parse().unwrap()
23}
24
25#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
26pub enum MoveComputeType {
27 All,
28 Attacks,
29 /// If a promoting move causes check then all promoting moves for
30 /// the four different target pieces will be included for that pawn.
31 AttacksChecks,
32}
33
34/// Represents the possible ways a game can be terminated, we only
35/// consider a game to be terminated when a side has no legal moves
36/// to make or if a special draw condition is met like position
37/// repetition. If a side has no legal moves and is currently in check
38/// then the game is lost, if it is not in check then the game is
39/// drawn.
40#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
41pub enum Termination {
42 Draw,
43 Loss,
44}
45
46/// Represents the individual components which make up a board position
47/// encoded as a FEN string.
48#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
49pub enum FenComponent {
50 Board,
51 Active,
52 CastlingRights,
53 Enpassant,
54 HalfMoveCount,
55 MoveCount,
56}
57
58/// Trait representing a mutable state of play of a chess game
59/// which can be evolved/devolved via (applicable) Move instances,
60/// compute the set of legal moves and queried for a variety of
61/// properties.
62pub trait ChessBoard: Clone + Send {
63 /// Evolves the position by making the given move. If the source hash
64 /// of the move does not match the hash of this position (prior to making
65 /// the move) then an error will be returned. If the hash matches but
66 /// the move is illegal in this position (e.g if you manually start
67 /// creating moves) then the results are undefined.
68 fn make(&mut self, action: Move) -> Result<()>;
69
70 /// Reverses and returns the move which was made last. If no move has
71 /// been made yet then an error is returned.
72 fn unmake(&mut self) -> Result<Move>;
73
74 /// Compute a vector of all the legal moves in this position for the
75 /// given computation type. Note there is no particular ordering to the
76 /// move vector.
77 fn compute_moves(&mut self, computation_type: MoveComputeType) -> Vec<Move>;
78
79 /// Compute the termination state of this node. If it is not terminal
80 /// nothing is returned, if it is then the manner of termination is
81 /// returned wrapped inside an Option. The termination can be only a
82 /// draw or a loss since a side only loses when it runs out of moves,
83 /// i.e. you don't play a winning move, you just fail to have a legal
84 /// move.
85 fn termination_status(&mut self) -> Option<Termination>;
86
87 /// Determines whether the active side is in a state of check.
88 fn in_check(&mut self) -> bool;
89
90 /// Return the locations of all pieces on the given side.
91 fn side(&self, side: Side) -> BitBoard;
92
93 /// Return the locations of all white and black pieces.
94 fn sides(&self) -> (BitBoard, BitBoard);
95
96 /// Returns the Zobrist hash of this position.
97 fn hash(&self) -> u64;
98
99 /// Return the active side in this position, i.e. the one whose turn it is.
100 fn active(&self) -> Side;
101
102 /// Return the enpassant target square in this position.
103 fn enpassant(&self) -> Option<Square>;
104
105 /// Return the locations of the given pieces.
106 fn locs(&self, pieces: &[Piece]) -> BitBoard;
107
108 /// Return the location of the king for the given side.
109 fn king(&self, side: Side) -> Square;
110
111 /// Return the piece occupying the given location.
112 fn piece(&self, location: Square) -> Option<Piece>;
113
114 /// Return the half move clock value at this position.
115 fn half_move_clock(&self) -> usize;
116
117 /// Return the number of previous positions for this board.
118 fn position_count(&self) -> usize;
119
120 /// Return the remaining castling rights from this position.
121 fn remaining_rights(&self) -> EnumSet<CastleZone>;
122
123 /// Parse the given string as a sequence of pgn encoded moves
124 /// starting from the current position. The moves are then
125 /// made one by one. The sequence of moves which were made
126 /// are returned in a Vec.
127 fn play_pgn(&mut self, moves: &str) -> Result<Vec<Move>>;
128
129 /// Parse the given string as a sequence of uci encoded moves
130 /// starting from the current position. The moves are then
131 /// made one by one.The sequence of moves which were made
132 /// are returned in a Vec.
133 fn play_uci(&mut self, moves: &str) -> Result<Vec<Move>>;
134
135 /// Given a uci encoded move this method will attempt to match
136 /// it to the unique matching legal move in this position if it
137 /// exist. An error is returned if no matching move exists in
138 /// this position.
139 fn parse_uci(&mut self, uci_move: &str) -> Result<Move>;
140
141 /// Return the specified components of the FEN encoding of this position
142 /// in the given order with components separated by a space.
143 fn to_partial_fen(&self, cmps: &[FenComponent]) -> String;
144
145 /// Return the complete FEN representation of this position.
146 fn to_fen(&self) -> String {
147 self.to_partial_fen(&[
148 FenComponent::Board,
149 FenComponent::Active,
150 FenComponent::CastlingRights,
151 FenComponent::Enpassant,
152 FenComponent::HalfMoveCount,
153 FenComponent::MoveCount,
154 ])
155 }
156
157 /// Returns the locations of all pieces on the board.
158 fn all_pieces(&self) -> BitBoard {
159 let (w, b) = self.sides();
160 w | b
161 }
162}
163
164#[cfg(test)]
165mod uci_conversion_test {
166 use myopic_core::*;
167
168 use crate::mv::Move;
169
170 #[test]
171 fn test_pawn_standard_conversion() {
172 assert_eq!(
173 "e2e4",
174 Move::Standard {
175 source: 0u64,
176 moving: Piece::WP,
177 from: Square::E2,
178 dest: Square::E4,
179 capture: None,
180 }
181 .uci_format()
182 );
183 }
184
185 #[test]
186 fn test_rook_standard_conversion() {
187 assert_eq!(
188 "h1h7",
189 Move::Standard {
190 source: 0u64,
191 moving: Piece::BR,
192 from: Square::H1,
193 dest: Square::H7,
194 capture: Some(Piece::WQ),
195 }
196 .uci_format()
197 );
198 }
199
200 #[test]
201 fn test_castling_conversion() {
202 assert_eq!(
203 "e1g1",
204 Move::Castle {
205 source: 1u64,
206 zone: CastleZone::WK
207 }
208 .uci_format()
209 );
210 assert_eq!(
211 "e1c1",
212 Move::Castle {
213 source: 1u64,
214 zone: CastleZone::WQ
215 }
216 .uci_format()
217 );
218 assert_eq!(
219 "e8g8",
220 Move::Castle {
221 source: 8u64,
222 zone: CastleZone::BK
223 }
224 .uci_format()
225 );
226 assert_eq!(
227 "e8c8",
228 Move::Castle {
229 source: 8u64,
230 zone: CastleZone::BQ
231 }
232 .uci_format()
233 );
234 }
235
236 #[test]
237 fn test_promotion_conversion() {
238 assert_eq!(
239 "e7d8q",
240 Move::Promotion {
241 source: 9u64,
242 from: Square::E7,
243 dest: Square::D8,
244 promoted: Piece::WQ,
245 capture: Some(Piece::BB),
246 }
247 .uci_format()
248 )
249 }
250
251 #[test]
252 fn test_enpassant_conversion() {
253 assert_eq!(
254 "e5d6",
255 Move::Enpassant {
256 source: 0u64,
257 side: Side::White,
258 from: Square::E5,
259 dest: Square::D6,
260 capture: Square::D5,
261 }
262 .uci_format()
263 )
264 }
265}