1use std::{convert::TryFrom, iter::zip};
2use bulletformat::ChessBoard;
3
4#[repr(u8)]
5#[allow(dead_code)]
6#[derive(Copy, Clone, Eq, PartialEq)]
7pub enum PieceType{
8 Pawn,
9 Knight,
10 Bishop,
11 Rook,
12 Queen,
13 King,
14 Castle,
15 Void
16}
17
18#[repr(u8)]
19#[allow(dead_code)]
20#[derive(Copy, Clone, Eq, PartialEq)]
21pub enum Pieces{
22 WPawn, BPawn,
23 WKnight, BKnight,
24 WBishop, BBishop,
25 WRook, BRook,
26 WQueen, BQueen,
27 WKing, BKing,
28 WCASTLE, BCASLTE,
29 Void
30}
31
32#[allow(dead_code)]
33pub enum Color{White, Black}
34
35impl TryFrom<u8> for Color {
36 type Error = ();
37
38 fn try_from(v: u8) -> Result<Self, Self::Error> {
39 unsafe { std::mem::transmute(v) }
40 }
41}
42
43impl TryFrom<u8> for PieceType {
44 type Error = ();
45
46 fn try_from(v: u8) -> Result<Self, Self::Error> {
47 unsafe { std::mem::transmute(v) }
48 }
49}
50
51impl Pieces {
52 pub fn color(&self) -> Color {
53 (*self as u8 % 2).try_into().unwrap()
54 }
55 pub fn piecetype(&self) -> PieceType {
56 (*self as u8 / 2).try_into().unwrap()
57 }
58 pub fn from_viri(id: u8) -> Pieces {
59 ((id >> 3) | (id & 0b111) << 1).try_into().unwrap()
60 }
61 pub fn from_pc_color(pc: PieceType, c: Color) -> Pieces {
62 ((pc as u8) << 1 | c as u8).try_into().unwrap()
63 }
64}
65
66impl TryFrom<u8> for Pieces {
67 type Error = ();
68
69 fn try_from(v: u8) -> Result<Self, Self::Error> {
70 unsafe { std::mem::transmute(v) }
71 }
72}
73const ZOBR_CASTLE: usize = 12*64;
74const ZOBR_PASSANT: usize = ZOBR_CASTLE;
75const ZOBR_TURN: usize = ZOBR_PASSANT+65;
76#[cfg(feature = "hll")]
77const ZOBR_TOT: usize = ZOBR_TURN+1;
78#[cfg(feature = "hll")]
79const ZOBRISTS: [u64; ZOBR_TOT] = {
80 let mut res: [u64; ZOBR_TOT] = [0; ZOBR_TOT];
81
82 let mut state: u64 = 42;
83 cfor!(let mut idz: usize = 0; idz < ZOBR_TOT; idz += 1; {
84 state = state.wrapping_add(0x9E3779B97F4A7C15);
85 let mut z: u64 = state;
86 z = (z ^ (z >> 30)).wrapping_mul(0xBF58476D1CE4E5B9);
87 z = (z ^ (z >> 27)).wrapping_mul(0x94D049BB133111EB);
88 res[idz] = z ^ (z >> 31);
89 });
90 res[ZOBR_CASTLE+64] = 0;
91 res[ZOBR_PASSANT+64] = 0;
92 res
93};
94
95#[derive(Clone, Copy)]
96pub struct Position{
97 pub bbs: [u64; 8],
98 pub mailbox: [u8; 64],
99 pub score: i16,
100 pub bound: u8,
101 pub rule50: u8,
102 pub bm: u16,
103 pub stm: bool,
104 pub depth: u8,
105 pub ep: u8,
106 pub castle_mask: u64,
107 #[cfg(feature = "hll")]
108 pub hash: u64,
109}
110
111impl Default for Position {
112 fn default() -> Self {
113 Self {
114 bbs: [0; 8],
115 mailbox: [Pieces::Void as u8; 64],
116 score: 0,
117 bound: 0,
118 rule50: 0,
119 bm: 0,
120 stm: true,
121 depth: 0,
122 ep: 64,
123 castle_mask: 0,
124 #[cfg(feature = "hll")]
125 hash: 0,
126 }
127 }
128}
129
130const KINGPOS_CASTLE: [u8; 2] = [2, 6];
131const ROOKPOS_CASTLE: [u8; 2] = [3, 5];
132
133impl Position {
134 pub fn occupancy(&self) -> u64 {
135 return self.bbs[0] | self.bbs[1];
136 }
137 pub fn piece(&self, idx: usize) -> Pieces {
138 self.mailbox[idx].try_into().unwrap()
139 }
140 fn update_zobr(&mut self, _idx: usize) {
141#[cfg(feature = "hll")]
142{
143 self.hash ^= ZOBRISTS[_idx];
144}
145 }
146
147 #[allow(dead_code)]
148 pub fn print(&self) {
149 for i in 0..64 {
150 if self.mailbox[i]/2 == 7 {
151 print!("## ");
152 }else {
153 print!("{:2} ", self.mailbox[i]);
154 }
155 if i % 8 == 7 {
156 print!("\n");
157 }
158 }
159 }
160
161 pub fn push(&mut self, idx: u32, piece: Pieces) {
162 assert!(self.mailbox[idx as usize] == Pieces::Void as u8);
163 self.mailbox[idx as usize] = piece as u8;
164 self.bbs[piece.color() as usize] |= 1_u64 << idx;
165 self.bbs[piece.piecetype() as usize + 2] |= 1_u64 << idx;
166 self.update_zobr((piece as u32 * 64 + idx) as usize);
167 }
168
169 pub fn update_ep(&mut self, newep: usize) {
170 self.update_zobr(newep + ZOBR_PASSANT);
171 self.ep = newep as u8;
172 }
173 pub fn update_stm(&mut self, newstm: usize) {
174 #[cfg(feature = "hll")]
175 if newstm != 0 {
176 self.hash ^= ZOBRISTS[ZOBR_TURN];
177 }
178 self.stm = newstm != 0;
179 }
180 pub fn turn_stm(&mut self) {
181 self.update_zobr(ZOBR_TURN);
182 self.stm = !self.stm;
183 }
184 pub fn update_castle_rights(&mut self, castle_idx: usize) {
185 self.update_zobr(castle_idx + ZOBR_CASTLE);
186 self.castle_mask |= 1 << castle_idx as u64;
187 }
188
189 fn erase(&mut self, idx: usize){
190 let erased: Pieces = self.piece(idx);
191 if erased != Pieces::Void {
192 self.bbs[erased.piecetype() as usize + 2] ^= 1_u64 << idx;
193 self.bbs[erased.color() as usize] ^= 1_u64 << idx;
194 self.update_zobr(erased as usize * 64 + idx);
195 }
196 self.mailbox[idx] = Pieces::Void as u8;
197 }
198
199 fn erasesure(&mut self, idx: usize){
200 let erased: Pieces = self.piece(idx);
201 self.bbs[erased.piecetype() as usize + 2] ^= 1_u64 << idx;
202 self.bbs[erased.color() as usize] ^= 1_u64 << idx;
203 self.update_zobr(erased as usize * 64 + idx);
204 self.mailbox[idx] = Pieces::Void as u8;
205 }
206
207 #[allow(dead_code)]
208 fn verify(&self) -> bool { #[cfg(feature = "hll")]
210 {
211 let mut zobr: u64 = 0;
212 for i in 0..64 {
213 if self.mailbox[i] != Pieces::Void as u8 {
214 zobr ^= ZOBRISTS[i + 64 * self.mailbox[i] as usize];
215 }
216 }
217 zobr ^= ZOBRISTS[ZOBR_TURN] * self.stm as u64;
218 let mut mask = self.castle_mask;
219 while mask != 0 {
220 zobr ^= ZOBRISTS[ZOBR_CASTLE + mask.trailing_zeros() as usize];
221 mask &= mask-1;
222 }
223 zobr ^= ZOBRISTS[ZOBR_PASSANT + self.ep as usize];
224 if zobr != self.hash {
225 return false;
226 }
227 }
228 let mut newbbs: [u64; 8] = [0; 8];
229 for i in 0..64 {
230 if self.mailbox[i] != Pieces::Void as u8 {
231 newbbs[self.mailbox[i] as usize % 2] |= 1_u64 << i;
232 newbbs[self.mailbox[i] as usize / 2 + 2] |= 1_u64 << i;
233 }
234 }
235 for (n, o) in zip(newbbs, self.bbs) {
236 if n != o {
237 return false;
238 }
239 }
240 true
241 }
242
243 pub fn play(&mut self, mv: u16) {
244 let from = (mv & 0x3f) as u8;
245 let to = ((mv >> 6) & 0x3f) as u8;
246 let promo = (mv >> 12) & 0b11;
247 let flag = mv >> 14;
248 let piece: Pieces = self.piece(from as usize);
249 assert!(piece != Pieces::Void);
250 let capture: Pieces = self.piece(to as usize);
251 let _reset: bool = (capture != Pieces::Void && flag != 2) || piece.piecetype() == PieceType::Pawn;
252 if flag == 0 {
256
257 let masktofrom: u64 =
258 (1_u64 << to as u64) | (1_u64 << from as u64) |
259 (piece.piecetype() == PieceType::King) as u64 * (0xff_u64 << piece.color() as u64 * 56);
260
261 let mut mask: u64 = self.castle_mask & masktofrom;
262 self.castle_mask ^= mask;
263
264 self.update_zobr(ZOBR_CASTLE + mask.trailing_zeros() as usize);
265 mask &= mask.wrapping_sub(1);
266
267 self.update_zobr(ZOBR_CASTLE + mask.trailing_zeros() as usize);
268
269 self.update_zobr(self.ep as usize + ZOBR_PASSANT);
270 self.ep = (piece.piecetype() == PieceType::Pawn && ((to ^ from) & 0b10_000) != 0) as u8 * to as u8;
271 self.ep += 64*(self.ep == 0) as u8;
272
273 self.update_zobr(self.ep as usize + ZOBR_PASSANT);
274
275 self.erasesure(from as usize);
276 self.erase(to as usize);
277 self.push(to as u32, piece);
278 } else if flag == 1 {
279 assert!(self.ep < 64);
280 assert!(self.mailbox[self.ep as usize]/2 == 0);
281 self.erasesure(from as usize);
282 self.erasesure(self.ep as usize);
283 self.push(to as u32, piece);
284 self.update_zobr(self.ep as usize + ZOBR_PASSANT);
285 self.ep = 64;
286 } else if flag == 2 {
287
288 let to_king = (from&56) | KINGPOS_CASTLE[(from < to) as usize];
289 let to_rook = (from&56) | ROOKPOS_CASTLE[(from < to) as usize];
290
291 let mut mask: u64 = (0xff << (from & 56) as u64) & self.castle_mask;
292 self.castle_mask ^= mask;
293 self.update_zobr(ZOBR_CASTLE + mask.trailing_zeros() as usize);
294 mask &= mask.wrapping_sub(1);
295 self.update_zobr(ZOBR_CASTLE + mask.trailing_zeros() as usize);
296
297 self.erasesure(from as usize);
298 self.erasesure(to as usize);
299 self.push(to_king as u32, piece);
300 self.push(to_rook as u32, Pieces::from_pc_color(PieceType::Rook, piece.color()));
301
302 self.update_zobr(self.ep as usize + ZOBR_PASSANT);
303 self.ep = 64;
304 } else {
305 assert!(flag == 3);
306 let to_piece = Pieces::from_pc_color(((promo+1) as u8).try_into().unwrap(), piece.color());
307 if capture.piecetype() == PieceType::Rook && (self.castle_mask & (1 << to as u64) != 0){
308 self.update_zobr(ZOBR_CASTLE + to as usize);
309 self.castle_mask &= !(1 << to as u64);
310 }
311 self.update_zobr(self.ep as usize + ZOBR_PASSANT);
312 self.ep = 64;
313
314 self.erasesure(from as usize);
315 self.erase(to as usize);
316 self.push(to as u32, to_piece);
317 }
318
319 self.turn_stm();
320 }
325
326 pub fn tobullet(&self) -> ChessBoard {
327 let occ;
330 if self.stm {
331 occ = self.occupancy().swap_bytes();
332 } else {
333 occ = self.occupancy();
334 }
335 let mut pcs = [0; 16];
336
337 let mut idx = 0;
338 let mut occ2 = occ;
339 while occ2 > 0 {
340 let sq = occ2.trailing_zeros();
341 occ2 &= occ2 - 1;
342
343 let fullpiece = self.mailbox[sq as usize ^ 56 * self.stm as usize];
344 let colour = (fullpiece % 2) ^ self.stm as u8;
345 let piece = fullpiece / 2;
346
347 let pc = (colour << 3) | piece as u8;
348
349 pcs[idx / 2] |= pc << (4 * (idx & 1));
350
351 idx += 1;
352 }
353 let ksq = (self.bbs[ self.stm as usize] & self.bbs[7]).trailing_zeros() as u8 ^ (56 * self.stm as u8);
354 let opp_ksq = (self.bbs[!self.stm as usize] & self.bbs[7]).trailing_zeros() as u8 ^ (56 * !self.stm as u8);
355
356 ChessBoard {
357 occ,
358 pcs,
359 score:self.score,
360 result:self.bound,
361 ksq,
362 opp_ksq,
363 extra: [0; 3],
364 }
365 }
366}