aboutsummaryrefslogtreecommitdiff
path: root/src/bitboard.rs
blob: d7a4fdb9e9102dd0c474adaa0a4b71aee5469cc5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
use crate::square::Square;

/// Finite set of up to 64 bits representing chess board squares
pub type Bitboard = u64;

/// Print bitboard on screen in the same way squares appear in memory
/// (i.e the board is actually flipped along X)
#[allow(dead_code)]
pub fn print(bb: Bitboard, title: &str) {
    println!("\n  {}", title);
    for rank in (0..8).rev() {
        print!("{}|", rank + 1);
        for file in 0..8 {
            let index = rank * 8 + file;
            print!("{}", if bb >> index & 1 == 1 { "⚫" } else { ". " });
            if file == 7 {
                println!();
            }
        }
    }
    return println!("  a b c d e f g h");
}

/// Return bitboard cardinality, aka number of elements in the set
pub fn pop_count(bb: Bitboard) -> u8 {
    if bb == 0 {
        return 0;
    }
    return pop_count(bb >> 1) + (bb & 1) as u8;
}

/// Return Bitboard with only Least Single Bit
pub fn ls1b(bb: Bitboard) -> Bitboard {
    if bb == 0 {
        return 0
    }
    bb & !(bb - 1)
}

/// Log base 2 (aka Trailing Zero Count)
///
/// WARNING: Only works for SINGLE Bitboards
/// Useful for calculating bit-index of LS1B
pub fn bitscan(bb: Bitboard) -> Square {
    debug_assert!(pop_count(bb) == 1, "Bitscan only works for SINGLE Bitboards!");
    Square::from(pop_count(bb - 1))
}

/// Finds and removes ls1b in the bitboard, returning it's index
pub fn bitscan_and_reset(bb: &mut Bitboard) -> Square {
    let ls1b_bitboard = ls1b(*bb);
    *bb ^= ls1b_bitboard;
    bitscan(ls1b_bitboard)
}

/// Convert bitboard into the list of squares
pub fn serialize_bitboard(bb: Bitboard) -> Vec<Square> {
    let mut serialized = Vec::with_capacity(64);
    let mut bitboard = bb;
    while bitboard > 0 {
        serialized.push(bitscan_and_reset(&mut bitboard));
    }
    serialized
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_pop_count() {
        assert_eq!(pop_count(127), 7);
    }

    #[test]
    fn test_ls1b() {
        assert_eq!(ls1b(38), 2);
        assert_eq!(ls1b(16), 16);
        assert_eq!(ls1b(20), 4);
    }

    #[test]
    fn test_bitscan() {
        assert_eq!(bitscan(4), Square::from(2));
        assert_eq!(bitscan(16), Square::from(4));
        assert_eq!(bitscan(64), Square::from(6));
        assert_eq!(bitscan(128), Square::from(7));
    }

    #[test]
    #[should_panic(expected = "Bitscan only works for SINGLE Bitboards!")]
    fn test_bitscan_with_non_single_bb() {
        bitscan(5);
    }

    #[test]
    fn test_serialize_bitboard() {
        let bb = 1 << 4 | 1 << 15 | 1 << 60;
        let serialized = serialize_bitboard(bb);
        assert_eq!(serialized[0], Square::from(4));
        assert_eq!(serialized[1], Square::from(15));
        assert_eq!(serialized[2], Square::from(60));
    }
}