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}