1use crate::castle::{Castle, KING_SIDE, QUEEN_SIDE};
2use crate::piece::*;
3use crate::square::{Square, SquareInternal};
4use std::fmt;
5
6pub type Internal = u8;
24const CASTLE_FLAG: Internal = 128;
25const CAPTURE_FLAG: Internal = 64;
26const PROMOTION_FLAG: Internal = 128;
27const EP_CAPTURE_FLAG: Internal = 64;
28#[allow(dead_code)]
29pub const NULL_MOVE: Move = Move { upper: 0, lower: 0 };
30
31#[allow(dead_code)]
32pub const QUEEN_SIDE_CASTLE: Move = Move {
33 lower: 0,
34 upper: CASTLE_FLAG | (QUEEN_SIDE.to_u8() << 6),
35};
36
37#[allow(dead_code)]
38pub const KING_SIDE_CASTLE: Move = Move {
39 lower: 0,
40 upper: CASTLE_FLAG | (KING_SIDE.to_u8() << 6),
41};
42
43#[derive(Copy, Clone, PartialEq, Eq, Hash)]
45#[repr(packed(2))] pub struct Move {
47 lower: Internal, upper: Internal, }
50
51impl Default for Move {
52 fn default() -> Self {
53 NULL_MOVE
54 }
55}
56
57impl Move {
58 pub fn from(self) -> Square {
59 Square::new((self.lower & 63) as SquareInternal)
60 }
61
62 pub fn to(self) -> Square {
63 Square::new((self.upper & 63) as SquareInternal)
64 }
65
66 pub fn promote_to(self) -> Kind {
67 debug_assert!(!self.is_castle());
68 debug_assert!(self.is_promotion());
69 Kind((self.upper & (!63)) >> 6)
70 }
71
72 pub fn distance(self) -> i32 {
74 debug_assert!(!self.is_castle());
75 (self.from().to_i32() - self.to().to_i32()).abs()
76 }
77
78 pub fn is_castle(self) -> bool {
79 ((self.upper & CASTLE_FLAG) != 0) && ((self.lower & (!63)) == 0)
80 }
81
82 pub fn is_capture(self) -> bool {
83 (self.lower & CAPTURE_FLAG) != 0
84 }
85
86 pub fn is_ep_capture(self) -> bool {
87 ((self.lower & (!63)) == CAPTURE_FLAG) && ((self.upper & (!63)) == EP_CAPTURE_FLAG)
88 }
89
90 pub fn is_promotion(self) -> bool {
91 (self.lower & PROMOTION_FLAG) != 0
92 }
93
94 pub fn castle(self) -> Castle {
95 debug_assert!(self.is_castle());
96
97 Castle::new(((self.upper & 64) >> 6) as usize)
98 }
99
100 pub fn new_move(from: Square, to: Square, is_capture: bool) -> Move {
101 Move {
102 lower: from.to_u8() | if is_capture { CAPTURE_FLAG } else { 0 },
103 upper: to.to_u8(),
104 }
105 }
106
107 pub fn new_push(from: Square, to: Square) -> Move {
108 Move {
109 lower: from.to_u8(),
110 upper: to.to_u8(),
111 }
112 }
113
114 pub fn new_capture(from: Square, to: Square) -> Move {
115 Move {
116 lower: from.to_u8() | CAPTURE_FLAG,
117 upper: to.to_u8(),
118 }
119 }
120
121 pub fn new_castle(castle: Castle) -> Move {
122 Move {
123 lower: 0,
124 upper: CASTLE_FLAG | (castle.to_u8() << 6),
125 }
126 }
127
128 pub fn new_promotion(from: Square, to: Square, promote_to: Kind) -> Move {
129 Move {
130 lower: from.to_u8() | PROMOTION_FLAG,
131 upper: to.to_u8() | (promote_to.to_u8() << 6),
132 }
133 }
134
135 pub fn new_capture_promotion(from: Square, to: Square, promote_to: Kind) -> Move {
136 Move {
137 lower: from.to_u8() | PROMOTION_FLAG | CAPTURE_FLAG,
138 upper: to.to_u8() | (promote_to.to_u8() << 6),
139 }
140 }
141
142 pub fn new_ep_capture(from: Square, to: Square) -> Move {
143 Move {
144 lower: from.to_u8() | CAPTURE_FLAG,
145 upper: to.to_u8() | EP_CAPTURE_FLAG,
146 }
147 }
148}
149
150impl fmt::Display for Move {
151 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
152 if self.is_castle() {
153 return write!(f, "{}", self.castle().pgn_string());
154 }
155
156 let mut s = String::new();
157
158 s += &self.from().to_string();
159
160 if self.is_capture() {
161 s.push('x');
162 }
163
164 s += &self.to().to_string();
165
166 if self.is_promotion() {
167 s.push('=');
168 s.push(self.promote_to().to_char());
169 }
170
171 if self.is_ep_capture() {
172 s += "e.p."
173 }
174
175 write!(f, "{}", &s)
176 }
177}
178
179impl fmt::Debug for Move {
180 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
181 write!(f, "{}", self.to_string())
182 }
183}
184
185#[derive(Clone, Copy)]
188pub struct MoveScore(Move, i16);
189
190impl MoveScore {
191 #[allow(dead_code)]
192 pub fn mv(self) -> Move {
193 self.0
194 }
195
196 #[allow(dead_code)]
197 pub fn score(self) -> i16 {
198 self.1
199 }
200
201 pub const fn new(mv: Move, score: i16) -> MoveScore {
202 MoveScore(mv, score)
203 }
204}
205
206impl fmt::Display for MoveScore {
207 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
208 write!(f, "{} ({})", self.0, self.1)
209 }
210}
211
212impl fmt::Debug for MoveScore {
213 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
214 write!(f, "{} ({})", self.0, self.1)
215 }
216}
217
218#[cfg(test)]
219mod test {
220 use super::*;
221 use crate::square::*;
222 use std::mem;
223
224 #[test]
225 fn test_packed() {
226 assert_eq!(2, mem::size_of::<Move>());
227 }
228
229 #[test]
230 fn push() {
231 let mv = Move::new_push(B2, B3);
232 assert_eq!(mv.from(), B2);
233 assert_eq!(mv.to(), B3);
234 assert_eq!(mv.is_capture(), false);
235 assert_eq!(mv.is_castle(), false);
236 assert_eq!(mv.is_promotion(), false);
237 assert_eq!(mv.is_ep_capture(), false);
238 }
239
240 #[test]
241 fn capture() {
242 let mv = Move::new_capture(B2, B5);
243 assert_eq!(mv.from(), B2);
244 assert_eq!(mv.to(), B5);
245 assert_eq!(mv.is_capture(), true);
246 assert_eq!(mv.is_castle(), false);
247 assert_eq!(mv.is_promotion(), false);
248 assert_eq!(mv.is_ep_capture(), false);
249 }
250
251 #[test]
252 fn promotion() {
253 let mv = Move::new_promotion(B7, B8, KNIGHT);
254 assert_eq!(mv.from(), B7);
255 assert_eq!(mv.to(), B8);
256 assert_eq!(mv.promote_to(), KNIGHT);
257 assert_eq!(mv.is_capture(), false);
258 assert_eq!(mv.is_castle(), false);
259 assert_eq!(mv.is_promotion(), true);
260 assert_eq!(mv.is_ep_capture(), false);
261 }
262
263 #[test]
264 fn capture_promotion() {
265 let mv = Move::new_capture_promotion(B7, B8, QUEEN);
266 assert_eq!(mv.from(), B7);
267 assert_eq!(mv.to(), B8);
268 assert_eq!(mv.promote_to(), QUEEN);
269 assert_eq!(mv.is_capture(), true);
270 assert_eq!(mv.is_castle(), false);
271 assert_eq!(mv.is_promotion(), true);
272 assert_eq!(mv.is_ep_capture(), false);
273 }
274
275 #[test]
276 fn castle_queen_side() {
277 let mv = Move::new_castle(QUEEN_SIDE);
278 assert_eq!(mv.is_castle(), true);
279 assert_eq!(mv.castle(), QUEEN_SIDE);
280 assert_eq!(mv.is_capture(), false);
281 assert_eq!(mv.is_promotion(), false);
282 assert_eq!(mv.is_ep_capture(), false);
283 }
284
285 #[test]
286 fn castle_king_side() {
287 let mv = Move::new_castle(KING_SIDE);
288 assert_eq!(mv.is_castle(), true);
289 assert_eq!(mv.castle(), KING_SIDE);
290 assert_eq!(mv.is_capture(), false);
291 assert_eq!(mv.is_promotion(), false);
292 assert_eq!(mv.is_ep_capture(), false);
293 }
294
295 #[test]
296 fn new_ep_capture() {
297 let mv = Move::new_ep_capture(D4, C3);
298 assert_eq!(mv.from(), D4);
299 assert_eq!(mv.to(), C3);
300 assert_eq!(mv.is_capture(), true);
301 assert_eq!(mv.is_castle(), false);
302 assert_eq!(mv.is_promotion(), false);
303 assert_eq!(mv.is_ep_capture(), true);
304 }
305
306 #[test]
307 fn to_string() {
308 assert_eq!(Move::new_castle(KING_SIDE).to_string(), "O-O");
309 assert_eq!(Move::new_castle(QUEEN_SIDE).to_string(), "O-O-O");
310
311 assert_eq!(Move::new_push(B2, B3).to_string(), "b2b3");
312 assert_eq!(Move::new_push(B2, D5).to_string(), "b2d5");
313 assert_eq!(Move::new_capture(B2, B5).to_string(), "b2xb5");
314 assert_eq!(Move::new_capture(B2, B3).to_string(), "b2xb3");
315
316 assert_eq!(Move::new_promotion(B7, B8, QUEEN).to_string(), "b7b8=Q");
317 assert_eq!(
318 Move::new_capture_promotion(C7, B8, QUEEN).to_string(),
319 "c7xb8=Q"
320 );
321
322 assert_eq!(Move::new_ep_capture(D4, C3).to_string(), "d4xc3e.p.");
323 }
324}