diff --git a/src/eval.rs b/src/eval.rs index 6aaeeed..956a2e8 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -41,8 +41,8 @@ impl Eval for Board { ]; for pc in [Rook, Queen, Pawn, Knight, Bishop, King] { - let tally_white = self.pl(Color::White).board(pc).0.count_ones(); - let tally_black = self.pl(Color::Black).board(pc).0.count_ones(); + let tally_white = self[Color::White][pc].0.count_ones(); + let tally_black = self[Color::Black][pc].0.count_ones(); let tally = EvalInt::try_from(tally_white).unwrap() - EvalInt::try_from(tally_black).unwrap(); diff --git a/src/lib.rs b/src/lib.rs index 5783e86..cbf3d44 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,6 +14,7 @@ Copyright © 2024 dogeystamp #![deny(rust_2018_idioms)] use std::fmt::Display; +use std::ops::{Index, IndexMut}; use std::str::FromStr; pub mod eval; @@ -372,23 +373,11 @@ impl Mailbox { /// /// Default is all empty. #[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] -struct Player { +pub struct PlayerBoards { /// Bitboards for individual pieces. Piece -> locations. bit: [Bitboard; N_PIECES], } -impl Player { - /// Get board (non-mutable) for a specific piece. - fn board(&self, pc: Piece) -> &Bitboard { - &self.bit[pc as usize] - } - - /// Get board (mutable) for a specific piece. - fn board_mut(&mut self, pc: Piece) -> &mut Bitboard { - &mut self.bit[pc as usize] - } -} - /// Castling rights for one player #[derive(Debug, Default, PartialEq, Eq, Clone, Copy)] pub struct CastlePlayer { @@ -402,9 +391,9 @@ pub struct CastlePlayer { #[derive(Debug, Default, PartialEq, Eq, Clone, Copy)] pub struct CastleRights([CastlePlayer; N_COLORS]); -impl ToString for CastleRights { +impl Display for CastleRights { /// Convert to FEN castling rights format. - fn to_string(&self) -> String { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut ret = String::with_capacity(4); for (val, ch) in [ (self.0[Color::White as usize].k, 'K'), @@ -419,7 +408,7 @@ impl ToString for CastleRights { if ret.is_empty() { ret.push('-') } - ret + write!(f, "{}", ret) } } @@ -429,7 +418,7 @@ impl ToString for CastleRights { #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] pub struct Board { /// Player bitboards - players: [Player; N_COLORS], + players: [PlayerBoards; N_COLORS], /// Mailbox (array) board. Location -> piece. mail: Mailbox, @@ -458,26 +447,6 @@ impl Board { Board::from_fen(START_POSITION).unwrap() } - /// Get mutable reference to a player. - fn pl_mut(&mut self, col: Color) -> &mut Player { - &mut self.players[col as usize] - } - - /// Get immutable reference to a player. - fn pl(&self, col: Color) -> &Player { - &self.players[col as usize] - } - - /// Get immutable reference to castling rights. - pub fn pl_castle(&self, col: Color) -> &CastlePlayer { - &self.castle.0[col as usize] - } - - /// Get mutable reference to castling rights. - fn pl_castle_mut(&mut self, col: Color) -> &mut CastlePlayer { - &mut self.castle.0[col as usize] - } - /// Get iterator over all squares. pub fn squares() -> impl Iterator { (0..N_SQUARES).map(Square::try_from).map(|x| x.unwrap()) @@ -486,8 +455,8 @@ impl Board { /// Create a new piece in a location, and pop any existing piece in the destination. pub fn set_piece(&mut self, idx: Square, pc: ColPiece) -> Option { let dest_pc = self.del_piece(idx); - let pl = self.pl_mut(pc.col); - pl.board_mut(pc.into()).on_sq(idx); + let pl = &mut self[pc.col]; + pl[pc.into()].on_sq(idx); *self.mail.sq_mut(idx) = Some(pc); dest_pc } @@ -503,8 +472,8 @@ impl Board { /// Delete the piece in a location, and return ("pop") that piece. pub fn del_piece(&mut self, idx: Square) -> Option { if let Some(pc) = *self.mail.sq_mut(idx) { - let pl = self.pl_mut(pc.col); - pl.board_mut(pc.into()).off_sq(idx); + let pl = &mut self[pc.col]; + pl[pc.into()].off_sq(idx); *self.mail.sq_mut(idx) = None; Some(pc) } else { @@ -555,7 +524,7 @@ impl Board { /// Is a given player in check? pub fn is_check(&self, pl: Color) -> bool { - for src in self.pl(pl).board(Piece::King).into_iter() { + for src in self[pl][Piece::King] { macro_rules! detect_checker { ($dirs: ident, $pc: pat, $keep_going: expr) => { for dir in $dirs.into_iter() { @@ -606,6 +575,48 @@ impl Board { const MAX_MOVES: usize = 9_999; } +impl Index for Board { + type Output = PlayerBoards; + + fn index(&self, col: Color) -> &Self::Output { + &self.players[col as usize] + } +} + +impl IndexMut for Board { + fn index_mut(&mut self, col: Color) -> &mut Self::Output { + &mut self.players[col as usize] + } +} + +impl Index for CastleRights { + type Output = CastlePlayer; + + fn index(&self, col: Color) -> &Self::Output { + &self.0[col as usize] + } +} + +impl IndexMut for CastleRights { + fn index_mut(&mut self, col: Color) -> &mut Self::Output { + &mut self.0[col as usize] + } +} + +impl Index for PlayerBoards { + type Output = Bitboard; + + fn index(&self, pc: Piece) -> &Self::Output { + &self.bit[pc as usize] + } +} + +impl IndexMut for PlayerBoards { + fn index_mut(&mut self, pc: Piece) -> &mut Self::Output { + &mut self.bit[pc as usize] + } +} + impl core::fmt::Display for Board { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut str = String::with_capacity(N_SQUARES + BOARD_HEIGHT); @@ -693,9 +704,7 @@ mod tests { } let board = Board::from_fen("8/4p3/1q1Q1p2/4p3/1p1r4/8/8/8 w - - 0 1").unwrap(); - let white_queens = board - .pl(Color::White) - .board(Piece::Queen) + let white_queens = board[Color::White][Piece::Queen] .into_iter() .collect::>(); assert_eq!(white_queens, vec![Square::from_str("d6").unwrap()]) diff --git a/src/movegen.rs b/src/movegen.rs index 5ed1601..a96febc 100644 --- a/src/movegen.rs +++ b/src/movegen.rs @@ -296,7 +296,7 @@ impl Move { pos.half_moves = 0; } - let castle = &mut pos.pl_castle_mut(pc_src.col); + let castle = &mut pos.castle[pc_src.col]; if matches!(pc_src.pc, Piece::King) { // forfeit castling rights castle.k = false; @@ -564,10 +564,10 @@ fn is_legal(board: &mut Board, mv: Move) -> bool { impl MoveGen for Board { fn gen_moves(&mut self, gen_type: MoveGenType) -> impl IntoIterator { let mut ret = Vec::new(); - let pl = self.pl(self.turn); + let pl = self[self.turn]; macro_rules! squares { ($pc: ident) => { - pl.board(Piece::$pc).into_iter() + pl[Piece::$pc].into_iter() }; } @@ -583,7 +583,7 @@ impl MoveGen for Board { for src in squares!(King) { move_slider(self, src, &mut ret, SliderDirection::Star, false); let (r, c) = src.to_row_col_signed(); - let rights = self.pl_castle(self.turn); + let rights = self.castle[self.turn]; let castle_sides = [(rights.k, 2, BOARD_WIDTH as isize - 1), (rights.q, -2, 0)]; for (is_allowed, move_offset, endpoint) in castle_sides { if !is_allowed { @@ -784,8 +784,8 @@ mod tests { use std::collections::hash_set::HashSet; use Piece::*; for pc in [Rook, Bishop, Knight, Queen, King, Pawn] { - let white: HashSet<_> = pos.pl(Color::White).board(pc).into_iter().collect(); - let black: HashSet<_> = pos.pl(Color::Black).board(pc).into_iter().collect(); + let white: HashSet<_> = pos[Color::White][pc].into_iter().collect(); + let black: HashSet<_> = pos[Color::Black][pc].into_iter().collect(); let intersect = white.intersection(&black).collect::>(); assert!( intersect.is_empty(),