1
1
use std:: fmt:: { self , Debug } ;
2
2
3
- #[ cfg( feature = "python" ) ]
4
- use pyo3:: pyclass;
5
-
6
3
static I_SHIFT : u8 = 49 + 7 ;
7
4
static J_SHIFT : u8 = 49 ;
8
5
static BOARD_MASK : u64 = 0x1ffffffffffff ;
9
6
static IJ_MASK : u64 = 0x7ffe000000000000 ;
10
7
8
+ // A mask for the bits that do not get "shifted out" when changing the offset's i value.
9
+ // The index into this array is (offset_i_new - offset_i + 7), clamped to (0, 14).
10
+ static SHIFT_MASK_I : [ u64 ; 15 ] = [
11
+ 0b0000000000000000000000000000000000000000000000000 ,
12
+ 0b0000000000000000000000000000000000000000001111111 ,
13
+ 0b0000000000000000000000000000000000011111111111111 ,
14
+ 0b0000000000000000000000000000111111111111111111111 ,
15
+ 0b0000000000000000000001111111111111111111111111111 ,
16
+ 0b0000000000000011111111111111111111111111111111111 ,
17
+ 0b0000000111111111111111111111111111111111111111111 ,
18
+ 0b1111111111111111111111111111111111111111111111111 ,
19
+ 0b1111111111111111111111111111111111111111110000000 ,
20
+ 0b1111111111111111111111111111111111100000000000000 ,
21
+ 0b1111111111111111111111111111000000000000000000000 ,
22
+ 0b1111111111111111111110000000000000000000000000000 ,
23
+ 0b1111111111111100000000000000000000000000000000000 ,
24
+ 0b1111111000000000000000000000000000000000000000000 ,
25
+ 0b0000000000000000000000000000000000000000000000000 ,
26
+ ] ;
27
+
28
+ // A mask for the bits that do not get "shifted out" when changing the offset's j value.
29
+ // The index into this array is (offset_j_new - offset_j + 7), clamped to (0, 14).
30
+ static SHIFT_MASK_J : [ u64 ; 15 ] = [
31
+ 0b0000000000000000000000000000000000000000000000000 ,
32
+ 0b0000001000000100000010000001000000100000010000001 ,
33
+ 0b0000011000001100000110000011000001100000110000011 ,
34
+ 0b0000111000011100001110000111000011100001110000111 ,
35
+ 0b0001111000111100011110001111000111100011110001111 ,
36
+ 0b0011111001111100111110011111001111100111110011111 ,
37
+ 0b0111111011111101111110111111011111101111110111111 ,
38
+ 0b1111111111111111111111111111111111111111111111111 ,
39
+ 0b1111110111111011111101111110111111011111101111110 ,
40
+ 0b1111100111110011111001111100111110011111001111100 ,
41
+ 0b1111000111100011110001111000111100011110001111000 ,
42
+ 0b1110000111000011100001110000111000011100001110000 ,
43
+ 0b1100000110000011000001100000110000011000001100000 ,
44
+ 0b1000000100000010000001000000100000010000001000000 ,
45
+ 0b0000000000000000000000000000000000000000000000000 ,
46
+ ] ;
47
+
11
48
/// A [`Copy`] board representation that stores only a single
12
49
/// bit per field.
13
50
///
@@ -18,7 +55,7 @@ static IJ_MASK: u64 = 0x7ffe000000000000;
18
55
/// means of its [`IntoIterator`] instance.
19
56
///
20
57
/// Note that its "mutating" methods return a new object instead of really mutating.
21
- #[ cfg_attr( feature = "python" , pyclass) ]
58
+ #[ cfg_attr( feature = "python" , pyo3 :: pyclass) ]
22
59
#[ derive( Clone , Copy ) ]
23
60
pub struct BitBoard {
24
61
/// The low 49 bits are the board itself (7x7)
@@ -37,6 +74,24 @@ pub struct BitBoard {
37
74
/// and all the numbers in [0, 63i8] start with the bits 00.
38
75
/// Therefore, compression works by removing the highest bit,
39
76
/// and adding it back when reading.
77
+ ///
78
+ /// How do (i, j) coordinates map to bits in the board?
79
+ /// (i, j) is represented as the bit number (i * 7 + j), counted from
80
+ /// the least significant bit. So if you lay out a number like
81
+ /// 0b0000000000000011111111111111111111111111111111111 in blocks of 7
82
+ /// (which is also what the Debug impl does) like so:
83
+ ///
84
+ /// ```text
85
+ /// 0 0 0 0 0 0 0
86
+ /// 0 0 0 0 0 0 0
87
+ /// 1 1 1 1 1 1 1
88
+ /// 1 1 1 1 1 1 1
89
+ /// 1 1 1 1 1 1 1
90
+ /// 1 1 1 1 1 1 1
91
+ /// 1 1 1 1 1 1 1
92
+ /// ```
93
+ /// then this 2D array effectively has a coordinate system that has i going from the
94
+ /// bottom (0) to the top (6), and j going from the right (0) to the left (6).
40
95
bits : u64 ,
41
96
}
42
97
@@ -55,10 +110,8 @@ impl BitBoard {
55
110
// all the cards, it will fit within the 7x7 area.
56
111
let offset_i = i - 3 ;
57
112
let offset_j = j - 3 ;
58
- let offset_i_bits = u64:: from ( offset_i as u8 & 0b01111111u8 ) << I_SHIFT ;
59
- let offset_j_bits = u64:: from ( offset_j as u8 & 0b01111111u8 ) << J_SHIFT ;
60
113
Self {
61
- bits : offset_i_bits | offset_j_bits ,
114
+ bits : encode_offset ( offset_i , offset_j ) ,
62
115
}
63
116
}
64
117
@@ -165,10 +218,33 @@ impl BitBoard {
165
218
board_bits. count_ones( ) ,
166
219
( board_bits_shifted & BOARD_MASK ) . count_ones( )
167
220
) ;
168
- let offset_i_bits = u64:: from ( new_offset_i as u8 & 0b01111111u8 ) << I_SHIFT ;
169
- let offset_j_bits = u64:: from ( new_offset_j as u8 & 0b01111111u8 ) << J_SHIFT ;
221
+ let offset_bits = encode_offset ( new_offset_i, new_offset_j) ;
170
222
Self {
171
- bits : offset_i_bits | offset_j_bits | board_bits_shifted,
223
+ bits : offset_bits | board_bits_shifted,
224
+ }
225
+ }
226
+
227
+ pub ( crate ) fn shift_lossy ( self , new_center : ( i8 , i8 ) ) -> BitBoard {
228
+ assert ! ( new_center. 0 >= -52 ) ;
229
+ assert ! ( new_center. 1 >= -52 ) ;
230
+ assert ! ( new_center. 0 <= 52 ) ;
231
+ assert ! ( new_center. 1 <= 52 ) ;
232
+
233
+ let ( offset_i, offset_j) = self . offset ( ) ;
234
+ let ( new_offset_i, new_offset_j) = ( new_center. 0 - 3 , new_center. 1 - 3 ) ;
235
+ let ( diff_i, diff_j) = ( new_offset_i - offset_i, new_offset_j - offset_j) ;
236
+ let mask_i = SHIFT_MASK_I [ ( diff_i + 7 ) . clamp ( 0 , 14 ) as usize ] ;
237
+ let mask_j = SHIFT_MASK_J [ ( diff_j + 7 ) . clamp ( 0 , 14 ) as usize ] ;
238
+ let valid_bits = self . bits & mask_i & mask_j;
239
+ let shift_by = diff_i * 7 + diff_j;
240
+ let bits_shifted = if shift_by > 0 {
241
+ valid_bits >> shift_by
242
+ } else {
243
+ valid_bits << shift_by. abs ( )
244
+ } ;
245
+ let offset_bits = encode_offset ( new_offset_i, new_offset_j) ;
246
+ Self {
247
+ bits : offset_bits | bits_shifted,
172
248
}
173
249
}
174
250
@@ -224,16 +300,26 @@ impl BitBoard {
224
300
}
225
301
226
302
fn offset ( self ) -> ( i8 , i8 ) {
227
- // The highest bit of i_compressed is garbage and needs
228
- // to be replaced with the second-highest bit.
229
- let offset_i_compressed = 0b01111111i8 & ( self . bits >> I_SHIFT ) as i8 ;
230
- let offset_i = offset_i_compressed | ( ( offset_i_compressed & 0b01000000i8 ) << 1 ) ;
231
- let offset_j_compressed = 0b01111111i8 & ( self . bits >> J_SHIFT ) as i8 ;
232
- let offset_j = offset_j_compressed | ( ( offset_j_compressed & 0b01000000i8 ) << 1 ) ;
233
- ( offset_i, offset_j)
303
+ decode_offset ( self . bits )
234
304
}
235
305
}
236
306
307
+ fn decode_offset ( bits : u64 ) -> ( i8 , i8 ) {
308
+ // The highest bit of i_compressed is garbage and needs
309
+ // to be replaced with the second-highest bit.
310
+ let offset_i_compressed = 0b01111111i8 & ( bits >> I_SHIFT ) as i8 ;
311
+ let offset_i = offset_i_compressed | ( ( offset_i_compressed & 0b01000000i8 ) << 1 ) ;
312
+ let offset_j_compressed = 0b01111111i8 & ( bits >> J_SHIFT ) as i8 ;
313
+ let offset_j = offset_j_compressed | ( ( offset_j_compressed & 0b01000000i8 ) << 1 ) ;
314
+ ( offset_i, offset_j)
315
+ }
316
+
317
+ fn encode_offset ( offset_i : i8 , offset_j : i8 ) -> u64 {
318
+ let offset_i_bits = u64:: from ( offset_i as u8 & 0b01111111u8 ) << I_SHIFT ;
319
+ let offset_j_bits = u64:: from ( offset_j as u8 & 0b01111111u8 ) << J_SHIFT ;
320
+ offset_i_bits | offset_j_bits
321
+ }
322
+
237
323
impl Debug for BitBoard {
238
324
fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
239
325
let digits = format ! ( "{:049b}" , self . bits & BOARD_MASK ) ;
@@ -325,4 +411,13 @@ mod tests {
325
411
. insert ( 15 , 30 ) ;
326
412
assert_eq ! ( bb. bits, bb. recenter_to( ( 15 , 33 ) ) . recenter_to( ( 12 , 30 ) ) . bits) ;
327
413
}
414
+
415
+ #[ test]
416
+ fn shift ( ) {
417
+ let bb = BitBoard :: empty_board_centered_at ( 12 , 30 )
418
+ . insert ( 12 , 30 )
419
+ . insert ( 12 , 33 )
420
+ . insert ( 15 , 30 ) ;
421
+ assert_eq ! ( bb. bits, bb. shift_lossy( ( 15 , 33 ) ) . shift_lossy( ( 12 , 30 ) ) . bits) ;
422
+ }
328
423
}
0 commit comments