1use std::hint::unreachable_unchecked;
2
3use crate::bitboard::{BitBoard, EMPTY};
4use crate::color::Color;
5use crate::file::File;
6use crate::square::Square;
7
8use crate::magic::{KINGSIDE_CASTLE_SQUARES, QUEENSIDE_CASTLE_SQUARES};
9
10#[repr(u8)]
12#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Debug, Hash)]
13pub enum CastleRights {
14 NoRights = 0,
15 KingSide = 1,
16 QueenSide = 2,
17 Both = 3,
18}
19
20pub const NUM_CASTLE_RIGHTS: usize = 4;
22
23pub const ALL_CASTLE_RIGHTS: [CastleRights; NUM_CASTLE_RIGHTS] = [CastleRights::NoRights, CastleRights::KingSide, CastleRights::QueenSide, CastleRights::Both];
25
26const CASTLES_PER_SQUARE: [[u8; 64]; 2] = [
27 [
28 2, 0, 0, 0, 3, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ],
37 [
38 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 1,
46 ],
47];
48
49impl CastleRights {
50 pub fn has_kingside(&self) -> bool {
52 self.to_index() & 1 == 1
53 }
54
55 pub fn has_queenside(&self) -> bool {
57 self.to_index() & 2 == 2
58 }
59
60 pub fn square_to_castle_rights(color: Color, sq: Square) -> CastleRights {
61 CastleRights::from_index(unsafe { *CASTLES_PER_SQUARE.get_unchecked(color.to_index()).get_unchecked(sq.to_index()) } as usize)
62 }
63
64 pub fn kingside_squares(&self, color: Color) -> BitBoard {
66 unsafe { *KINGSIDE_CASTLE_SQUARES.get_unchecked(color.to_index()) }
67 }
68
69 pub fn queenside_squares(&self, color: Color) -> BitBoard {
71 unsafe { *QUEENSIDE_CASTLE_SQUARES.get_unchecked(color.to_index()) }
72 }
73
74 pub fn remove(&self, remove: CastleRights) -> CastleRights {
76 CastleRights::from_index(self.to_index() & !remove.to_index())
77 }
78
79 pub fn add(&self, add: CastleRights) -> CastleRights {
81 CastleRights::from_index(self.to_index() | add.to_index())
82 }
83
84 pub fn to_index(&self) -> usize {
86 *self as usize
87 }
88
89 pub fn from_index(i: usize) -> CastleRights {
91 match i & 3 {
92 0 => CastleRights::NoRights,
93 1 => CastleRights::KingSide,
94 2 => CastleRights::QueenSide,
95 3 => CastleRights::Both,
96 _ => unsafe { unreachable_unchecked() },
97 }
98 }
99
100 pub fn unmoved_rooks(&self, color: Color) -> BitBoard {
102 match *self {
103 CastleRights::NoRights => EMPTY,
104 CastleRights::KingSide => BitBoard::set(color.to_my_backrank(), File::H),
105 CastleRights::QueenSide => BitBoard::set(color.to_my_backrank(), File::A),
106 CastleRights::Both => BitBoard::set(color.to_my_backrank(), File::A) ^ BitBoard::set(color.to_my_backrank(), File::H),
107 }
108 }
109
110 pub fn to_string(&self, color: Color) -> String {
121 let result = match *self {
122 CastleRights::NoRights => "",
123 CastleRights::KingSide => "k",
124 CastleRights::QueenSide => "q",
125 CastleRights::Both => "kq",
126 };
127
128 if color == Color::White { result.to_uppercase() } else { result.to_string() }
129 }
130
131 pub fn rook_square_to_castle_rights(square: Square) -> CastleRights {
133 match square.get_file() {
134 File::A => CastleRights::QueenSide,
135 File::H => CastleRights::KingSide,
136 _ => CastleRights::NoRights,
137 }
138 }
139}