Skip to main content

syd_format/
position.rs

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 { // verify zobrist hashing and bbs from mailbox
209        #[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        // println!("from = {} to = {} piece = {} (piecetype = {}) type = {} promo = {} ep = {}", from, to, piece as u8, piece.piecetype() as u8, flag, promo, self.ep);
253        // println!("=> poss = {} & {}", piece.piecetype() == 0, ((to^from)&0b10000) != 0);
254
255        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        // if !self.verify(){
321        //     println!("from = {} to = {} piece = {} (piecetype = {}) type = {} promo = {} capture = {}", from, to, piece as u8, piece.piecetype() as u8, flag, promo, capture as u8);
322        //     unreachable!();
323        // }
324    }
325
326    pub fn tobullet(&self) -> ChessBoard {
327        // modified code from bulletformat, function from_raw
328
329        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}