aboutsummaryrefslogtreecommitdiff
path: root/src/board.rs
blob: 991820a43749b382a9fcb713217b12bb83ba966d (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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
use crate::bitboard::Bitboard;

#[derive(Debug)]
pub struct Board {
    pub pieces: [Bitboard; 12],
}


enum PieceTypes {
    Pawn,
    PawnBlack,
    Knight,
    KnightBlack,
    Bishop,
    BishopBlack,
    Rook,
    RookBlack,
    Queen,
    QueenBlack,
    King,
    KingBlack,
}


const PIECE_CHARS: [&str; 12] = [
    "♟︎", "♙",
    "♞", "♘",
    "♝", "♗",
    "♜", "♖",
    "♛", "♕",
    "♚", "♔",
];


impl Board {
    #[allow(non_snake_case)]
    pub fn from_FEN(fen: String) -> Self {
        let mut pieces = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];

        let mut rank = 7;
        let mut file = 0i32;

        for character in fen.chars() {
            let index = rank * 8 + file;
            let position = 1 << index.clamp(0, 63);

            if character.is_numeric() {
                let digit = match character.to_digit(10) {
                    None => todo!("What to do here?"),
                    Some(digit) => digit,
                };
                if digit > 0 && digit <= 8 {
                    file += digit as i32;
                }
            } else {
                match character {
                    'P' => pieces[PieceTypes::Pawn as usize] |= position,
                    'N' => pieces[PieceTypes::Knight as usize] |= position,
                    'B' => pieces[PieceTypes::Bishop as usize] |= position,
                    'R' => pieces[PieceTypes::Rook as usize] |= position,
                    'Q' => pieces[PieceTypes::Queen as usize] |= position,
                    'K' => pieces[PieceTypes::King as usize] |= position,
                    'p' => pieces[PieceTypes::PawnBlack as usize] |= position,
                    'n' => pieces[PieceTypes::KnightBlack as usize] |= position,
                    'b' => pieces[PieceTypes::BishopBlack as usize] |= position,
                    'r' => pieces[PieceTypes::RookBlack as usize] |= position,
                    'q' => pieces[PieceTypes::QueenBlack as usize] |= position,
                    'k' => pieces[PieceTypes::KingBlack as usize] |= position,
                    '/' => {
                        rank -= 1;
                        file = -1; // So it becomes 0
                    },
                    ' ' => { break }, // TODO: break for now, parse everything else later
                    '-' => {}, // TODO
                    'w' => {}, // TODO
                    _ => todo!("Unexpected character!"),
                }
                file += 1;
            }
        }

        Self { pieces }
    }

    pub fn print(&self) {
        println!();
        for rank in (0..8).rev() {
            print!("{}|", rank + 1);
            for file in 0..8 {
                let index = rank * 8 + file;
                let position: Bitboard = 1 << index;
                let mut found = false;
                for (piec_type, piece_bitboard) in self.pieces.iter().enumerate() {
                    if (piece_bitboard & position) > 0 {
                        found = true;
                        print!("{} ", PIECE_CHARS[piec_type]);
                    }
                }
                if !found {
                    print!(". ");
                }
            }
            println!();
        }
        println!("  a b c d e f g h");
    }
}


enum Color {
    White,
    Black,
}

/// Aliases to board square indexes
#[allow(dead_code)]
enum Square {
  A1, B1, C1, D1, E1, F1, G1, H1,
  A2, B2, C2, D2, E2, F2, G2, H2,
  A3, B3, C3, D3, E3, F3, G3, H3,
  A4, B4, C4, D4, E4, F4, G4, H4,
  A5, B5, C5, D5, E5, F5, G5, H5,
  A6, B6, C6, D6, E6, F6, G6, H6,
  A7, B7, C7, D7, E7, F7, G7, H7,
  A8, B8, C8, D8, E8, F8, G8, H8,
}

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

    #[test]
    fn test_square_enum() {
        assert_eq!(Square::A1 as u8, 0);
        assert_eq!(Square::F1 as u8, 5);
        assert_eq!(Square::H8 as u8, 63);
    }

    #[test]
    fn test_from_fen() {
        let fen = String::from("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1");
        let board = Board::from_FEN(fen);

        assert_eq!(bitboard::pop_count(board.pieces[PieceTypes::Pawn as usize]), 8);
        assert_eq!(bitboard::pop_count(board.pieces[PieceTypes::Knight as usize]), 2);
        assert_eq!(bitboard::pop_count(board.pieces[PieceTypes::Bishop as usize]), 2);
        assert_eq!(bitboard::pop_count(board.pieces[PieceTypes::Rook as usize]), 2);
        assert_eq!(bitboard::pop_count(board.pieces[PieceTypes::Queen as usize]), 1);
        assert_eq!(bitboard::pop_count(board.pieces[PieceTypes::King as usize]), 1);

        assert_eq!(bitboard::pop_count(board.pieces[PieceTypes::PawnBlack as usize]), 8);
        assert_eq!(bitboard::pop_count(board.pieces[PieceTypes::KnightBlack as usize]), 2);
        assert_eq!(bitboard::pop_count(board.pieces[PieceTypes::BishopBlack as usize]), 2);
        assert_eq!(bitboard::pop_count(board.pieces[PieceTypes::RookBlack as usize]), 2);
        assert_eq!(bitboard::pop_count(board.pieces[PieceTypes::QueenBlack as usize]), 1);
        assert_eq!(bitboard::pop_count(board.pieces[PieceTypes::KingBlack as usize]), 1);

        assert_eq!(bitboard::bitscan(board.pieces[PieceTypes::King as usize]), Square::E1 as u8);
        assert_eq!(bitboard::bitscan(board.pieces[PieceTypes::QueenBlack as usize]), Square::D8 as u8);

        board.print();
    }
}