chess_move_gen/
piece.rs

1use crate::side::Side;
2use std::fmt;
3
4type PieceInternal = u8;
5
6/// Represents a piece for a particular side (eg black knight)
7#[derive(PartialEq, PartialOrd, Copy, Clone)]
8pub struct Piece(PieceInternal);
9
10const CHARS: [char; 12] = ['B', 'b', 'Q', 'q', 'R', 'r', 'N', 'n', 'P', 'p', 'K', 'k'];
11// const SYMBOLS: [char; 14] = ['♙', '♟', '♘', '♞', '♗', '♝', '♖', '♜',
12//                             '♕', '♛', '♔', '♚', '.'];
13
14const NAMES: [&str; 12] = [
15    "white bishop",
16    "black bishop",
17    "white queen",
18    "black queen",
19    "white rook",
20    "black rook",
21    "white knight",
22    "black knight",
23    "white pawn",
24    "black pawn",
25    "white king",
26    "black king",
27];
28
29/// Represents a kind of piece (eg knight)
30#[derive(PartialEq, PartialOrd, Copy, Clone)]
31pub struct Kind(pub PieceInternal);
32
33impl Kind {
34    pub fn pc(self, side: Side) -> Piece {
35        Piece((self.0 << 1) | side.raw() as PieceInternal)
36    }
37
38    pub fn to_usize(self) -> usize {
39        self.0 as usize
40    }
41
42    pub const fn to_u8(self) -> u8 {
43        self.0 as u8
44    }
45
46    pub fn to_char(self) -> char {
47        CHARS[self.to_usize() << 1]
48    }
49
50    pub fn iter() -> KindsIter {
51        KindsIter(Kind(0))
52    }
53
54    pub fn to_string(self) -> &'static str {
55        KIND_NAMES[self.to_usize()]
56    }
57
58    pub fn string_plural(self) -> String {
59        KIND_NAMES[self.to_usize()].to_string() + "s"
60    }
61}
62
63const KIND_NAMES: [&str; 6] = ["bishop", "queen", "rook", "knight", "pawn", "king"];
64
65#[derive(Debug)]
66pub struct KindsIter(Kind);
67
68impl Iterator for KindsIter {
69    type Item = Kind;
70
71    fn next(&mut self) -> Option<Kind> {
72        let kd = self.0;
73
74        if kd >= NULL_KIND {
75            return None;
76        }
77
78        (self.0).0 += 1;
79
80        Some(kd)
81    }
82}
83
84impl fmt::Debug for Kind {
85    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
86        write!(f, "{}", self.to_char())
87    }
88}
89
90impl fmt::Display for Kind {
91    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
92        write!(f, "{}", self.to_char())
93    }
94}
95
96#[allow(dead_code)]
97pub const BISHOP: Kind = Kind(0);
98#[allow(dead_code)]
99pub const QUEEN: Kind = Kind(1);
100#[allow(dead_code)]
101pub const ROOK: Kind = Kind(2);
102#[allow(dead_code)]
103pub const KNIGHT: Kind = Kind(3);
104#[allow(dead_code)]
105pub const PAWN: Kind = Kind(4);
106#[allow(dead_code)]
107pub const KING: Kind = Kind(5);
108#[allow(dead_code)]
109pub const NULL_KIND: Kind = Kind(6);
110
111#[allow(dead_code)]
112pub const WHITE_BISHOP: Piece = Piece(0);
113#[allow(dead_code)]
114pub const BLACK_BISHOP: Piece = Piece(1);
115
116#[allow(dead_code)]
117pub const WHITE_QUEEN: Piece = Piece(2);
118#[allow(dead_code)]
119pub const BLACK_QUEEN: Piece = Piece(3);
120
121#[allow(dead_code)]
122pub const WHITE_ROOK: Piece = Piece(4);
123#[allow(dead_code)]
124pub const BLACK_ROOK: Piece = Piece(5);
125
126#[allow(dead_code)]
127pub const WHITE_KNIGHT: Piece = Piece(6);
128#[allow(dead_code)]
129pub const BLACK_KNIGHT: Piece = Piece(7);
130
131#[allow(dead_code)]
132pub const WHITE_PAWN: Piece = Piece(8);
133#[allow(dead_code)]
134pub const BLACK_PAWN: Piece = Piece(9);
135
136#[allow(dead_code)]
137pub const WHITE_KING: Piece = Piece(10);
138#[allow(dead_code)]
139pub const BLACK_KING: Piece = Piece(11);
140
141#[allow(dead_code)]
142pub const NULL_PIECE: Piece = Piece(12);
143
144impl Piece {
145    pub fn to_usize(&self) -> usize {
146        self.0 as usize
147    }
148
149    pub fn to_char(&self) -> char {
150        CHARS[self.to_usize()]
151    }
152
153    pub fn kind(&self) -> Kind {
154        Kind(self.0 >> 1)
155    }
156
157    pub fn is_none(&self) -> bool {
158        *self == NULL_PIECE
159    }
160
161    pub fn is_some(&self) -> bool {
162        *self != NULL_PIECE
163    }
164
165    // assumes piece present
166    pub fn is_slider(&self) -> bool {
167        debug_assert!(self.is_some());
168        self.0 <= BLACK_ROOK.0
169    }
170
171    pub fn to_string(&self) -> &'static str {
172        NAMES[self.to_usize()]
173    }
174
175    pub fn string_plural(&self) -> String {
176        NAMES[self.to_usize()].to_string() + "s"
177    }
178
179    pub fn side(&self) -> Side {
180        Side(self.to_usize() & 1)
181    }
182
183    pub fn iter() -> PiecesIter {
184        PiecesIter(Piece(0))
185    }
186
187    pub fn parse(chr: char) -> Result<Piece, String> {
188        for pc in Piece::iter() {
189            if pc.to_char() == chr {
190                return Ok(pc);
191            }
192        }
193        Err(format!("Invalid piece: {}", chr))
194    }
195}
196
197#[derive(Debug)]
198pub struct PiecesIter(Piece);
199
200impl Iterator for PiecesIter {
201    type Item = Piece;
202
203    fn next(&mut self) -> Option<Piece> {
204        let pc = self.0;
205
206        if pc >= NULL_PIECE {
207            return None;
208        }
209
210        (self.0).0 += 1;
211
212        Some(pc)
213    }
214}
215
216impl fmt::Display for Piece {
217    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
218        write!(f, "{}", self.to_char())
219    }
220}
221
222impl fmt::Debug for Piece {
223    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
224        write!(f, "{}", self.to_char())
225    }
226}
227
228#[cfg(test)]
229mod test {
230    use super::*;
231    use crate::side::{BLACK, WHITE};
232
233    #[test]
234    fn to_char() {
235        assert_eq!(BLACK_ROOK.to_char(), 'r');
236        assert_eq!(WHITE_KING.to_char(), 'K');
237    }
238
239    #[test]
240    fn side() {
241        assert_eq!(WHITE_PAWN.side(), WHITE);
242        assert_eq!(BLACK_KNIGHT.side(), BLACK);
243    }
244
245    #[test]
246    fn to_usize() {
247        assert_eq!(BLACK_ROOK.to_usize(), 5);
248        assert_eq!(WHITE_KING.to_usize(), 10);
249    }
250
251    #[test]
252    fn kind() {
253        assert_eq!(WHITE_PAWN.kind(), PAWN);
254        assert_eq!(BLACK_KING.kind(), KING);
255    }
256}