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 piece(&self, idx: usize) -> Pieces {
135        self.mailbox[idx].try_into().unwrap()
136    }
137    fn update_zobr(&mut self, _idx: usize) {
138#[cfg(feature = "hll")]
139{
140        self.hash ^= ZOBRISTS[_idx];
141}
142    }
143
144    #[allow(dead_code)]
145    pub fn print(&self) {
146        for i in 0..64 {
147            if self.mailbox[i]/2 == 7 {
148                print!("## ");
149            }else {
150                print!("{:2} ", self.mailbox[i]);
151            }
152            if i % 8 == 7 {
153                print!("\n");
154            }
155        }
156    }
157
158    pub fn push(&mut self, idx: u32, piece: Pieces) {
159        assert!(self.mailbox[idx as usize] == Pieces::Void as u8);
160        self.mailbox[idx as usize] = piece as u8;
161        self.bbs[piece.color() as usize] |= 1_u64 << idx;
162        self.bbs[piece.piecetype() as usize + 2] |= 1_u64 << idx;
163        self.update_zobr((piece as u32 * 64 + idx) as usize);
164    }
165
166    pub fn update_ep(&mut self, newep: usize) {
167        self.update_zobr(newep + ZOBR_PASSANT);
168        self.ep = newep as u8;
169    }
170    pub fn update_stm(&mut self, newstm: usize) {
171        #[cfg(feature = "hll")]
172        if newstm != 0 {
173            self.hash ^= ZOBRISTS[ZOBR_TURN];
174        }
175        self.stm = newstm != 0;
176    }
177    pub fn turn_stm(&mut self) {
178        self.update_zobr(ZOBR_TURN);
179        self.stm = !self.stm;
180    }
181    pub fn update_castle_rights(&mut self, castle_idx: usize) {
182        self.update_zobr(castle_idx + ZOBR_CASTLE);
183        self.castle_mask |= 1 << castle_idx as u64;
184    }
185
186    fn erase(&mut self, idx: usize){
187        let erased: Pieces = self.piece(idx);
188        if erased != Pieces::Void {
189            self.bbs[erased.piecetype() as usize + 2] ^= 1_u64 << idx;
190            self.bbs[erased.color() as usize] ^= 1_u64 << idx;
191            self.update_zobr(erased as usize * 64 + idx);
192        }
193        self.mailbox[idx] = Pieces::Void as u8;
194    }
195
196    fn erasesure(&mut self, idx: usize){
197        let erased: Pieces = self.piece(idx);
198        self.bbs[erased.piecetype() as usize + 2] ^= 1_u64 << idx;
199        self.bbs[erased.color() as usize] ^= 1_u64 << idx;
200        self.update_zobr(erased as usize * 64 + idx);
201        self.mailbox[idx] = Pieces::Void as u8;
202    }
203
204    #[allow(dead_code)]
205    fn verify(&self) -> bool { // verify zobrist hashing and bbs from mailbox
206        #[cfg(feature = "hll")]
207        {
208            let mut zobr: u64 = 0;
209            for i in 0..64 {
210                if self.mailbox[i] != Pieces::Void as u8 {
211                    zobr ^= ZOBRISTS[i + 64 * self.mailbox[i] as usize];
212                }
213            }
214            zobr ^= ZOBRISTS[ZOBR_TURN] * self.stm as u64;
215            let mut mask = self.castle_mask;
216            while mask != 0 {
217                zobr ^= ZOBRISTS[ZOBR_CASTLE + mask.trailing_zeros() as usize];
218                mask &= mask-1;
219            }
220            zobr ^= ZOBRISTS[ZOBR_PASSANT + self.ep as usize];
221            if zobr != self.hash {
222                return false;
223            }
224        }
225        let mut newbbs: [u64; 8] = [0; 8];
226        for i in 0..64 {
227            if self.mailbox[i] != Pieces::Void as u8 {
228                newbbs[self.mailbox[i] as usize % 2] |= 1_u64 << i;
229                newbbs[self.mailbox[i] as usize / 2 + 2] |= 1_u64 << i;
230            }
231        }
232        for (n, o) in zip(newbbs, self.bbs) {
233            if n != o {
234                return false;
235            }
236        }
237        true
238    }
239
240    pub fn play(&mut self, mv: u16) {
241        let from = (mv & 0x3f) as u8;
242        let to = ((mv >> 6) & 0x3f) as u8;
243        let promo = (mv >> 12) & 0b11;
244        let flag = mv >> 14;
245        let piece: Pieces = self.piece(from as usize);
246        assert!(piece != Pieces::Void);
247        let capture: Pieces = self.piece(to as usize);
248        let _reset: bool = (capture != Pieces::Void && flag != 2) || piece.piecetype() == PieceType::Pawn;
249        // println!("from = {} to = {} piece = {} (piecetype = {}) type = {} promo = {} ep = {}", from, to, piece as u8, piece.piecetype() as u8, flag, promo, self.ep);
250        // println!("=> poss = {} & {}", piece.piecetype() == 0, ((to^from)&0b10000) != 0);
251
252        if flag == 0 {
253
254            let masktofrom: u64 = 
255                (1_u64 << to as u64) | (1_u64 << from as u64) | 
256                (piece.piecetype() == PieceType::King) as u64 * (0xff_u64 << piece.color() as u64 * 56);
257            
258            let mut mask: u64 = self.castle_mask & masktofrom;
259            self.castle_mask ^= mask;
260
261            self.update_zobr(ZOBR_CASTLE + mask.trailing_zeros() as usize);
262            mask &= mask.wrapping_sub(1);
263
264            self.update_zobr(ZOBR_CASTLE + mask.trailing_zeros() as usize);
265
266            self.update_zobr(self.ep as usize + ZOBR_PASSANT);
267            self.ep = (piece.piecetype() == PieceType::Pawn && ((to ^ from) & 0b10_000) != 0) as u8 * to as u8;
268            self.ep += 64*(self.ep == 0) as u8;
269            
270            self.update_zobr(self.ep as usize + ZOBR_PASSANT);
271
272            self.erasesure(from as usize);
273            self.erase(to as usize);
274            self.push(to as u32, piece);
275        } else if flag == 1 {
276            assert!(self.ep < 64);
277            assert!(self.mailbox[self.ep as usize]/2 == 0);
278            self.erasesure(from as usize);
279            self.erasesure(self.ep as usize);
280            self.push(to as u32, piece);
281            self.update_zobr(self.ep as usize + ZOBR_PASSANT);
282            self.ep = 64;
283        } else if flag == 2 {
284
285            let to_king = (from&56) | KINGPOS_CASTLE[(from < to) as usize];
286            let to_rook = (from&56) | ROOKPOS_CASTLE[(from < to) as usize];
287
288            let mut mask: u64 = (0xff << (from & 56) as u64) & self.castle_mask;
289            self.castle_mask ^= mask;
290            self.update_zobr(ZOBR_CASTLE + mask.trailing_zeros() as usize);
291            mask &= mask.wrapping_sub(1);
292            self.update_zobr(ZOBR_CASTLE + mask.trailing_zeros() as usize);
293
294            self.erasesure(from as usize);
295            self.erasesure(to as usize);
296            self.push(to_king as u32, piece);
297            self.push(to_rook as u32, Pieces::from_pc_color(PieceType::Rook, piece.color()));
298
299            self.update_zobr(self.ep as usize + ZOBR_PASSANT);
300            self.ep = 64;
301        } else {
302            assert!(flag == 3);
303            let to_piece = Pieces::from_pc_color(((promo+1) as u8).try_into().unwrap(), piece.color());
304            if capture.piecetype() == PieceType::Rook && (self.castle_mask & (1 << to as u64) != 0){
305                self.update_zobr(ZOBR_CASTLE + to as usize);
306                self.castle_mask &= !(1 << to as u64);
307            }
308            self.update_zobr(self.ep as usize + ZOBR_PASSANT);
309            self.ep = 64;
310
311            self.erasesure(from as usize);
312            self.erase(to as usize);
313            self.push(to as u32, to_piece);
314        }
315
316        self.turn_stm();
317        // if !self.verify(){
318        //     println!("from = {} to = {} piece = {} (piecetype = {}) type = {} promo = {} capture = {}", from, to, piece as u8, piece.piecetype() as u8, flag, promo, capture as u8);
319        //     unreachable!();
320        // }
321    }
322
323    pub fn tobullet(&self) -> ChessBoard {
324        let mut res = ChessBoard::from_raw(self.bbs, self.stm as usize, self.score, self.bound as f32).unwrap();
325        res.result = self.bound;
326        res.score = self.score;
327        res
328    }
329}