diff --git a/src/stats.rs b/src/stats.rs index a42c3b3..6db8c5a 100644 --- a/src/stats.rs +++ b/src/stats.rs @@ -1,7 +1,7 @@ //! Board statistics used for heuristics. use crate::board::*; -use crate::rules::GameState; +use crate::rules::{GameState, get_player_moves}; /// Storage for board pieces stats. #[derive(Debug, Clone, PartialEq)] @@ -27,6 +27,20 @@ impl BoardStats { } } + /// Create two new BoardStats objects from the board, for both sides. + /// + /// The playing color will have its stats filled in the first + /// BoardStats object, its opponent in the second. + pub fn new_from(board: &Board, game_state: &GameState) -> (BoardStats, BoardStats) { + let mut stats = (BoardStats::new(), BoardStats::new()); + let mut gs = game_state.clone(); + stats.0.compute(board, &gs); + gs.color = opposite(gs.color); + stats.1.compute(board, &gs); + stats + } + + /// Reset all stats to 0. pub fn reset(&mut self) { self.num_pawns = 0; self.num_bishops = 0; @@ -48,86 +62,88 @@ impl BoardStats { self.reset(); let color = game_state.color; // Compute mobility for all pieces. - self.mobility = rules::get_player_moves(board, game_state, true).len() as i32; + self.mobility = get_player_moves(board, game_state, true).len() as i32; // Compute amount of each piece. - for (piece, p) in get_piece_iterator(board) { - let (pos_f, pos_r) = p; - if piece == SQ_E || !is_color(piece, color) { - continue - } - match get_type(piece) { - SQ_R => stats.num_rooks += 1, - SQ_N => stats.num_knights += 1, - SQ_B => stats.num_bishops += 1, - SQ_Q => stats.num_queens += 1, - SQ_K => stats.num_kings += 1, - SQ_P => { - stats.num_pawns += 1; - let mut doubled = false; - let mut isolated = true; - let mut backward = true; - for r in 0..8 { - // Check for doubled pawns. - if - !doubled && - is_piece(get_square(board, &(pos_f, r)), color|SQ_P) && r != pos_r - { - doubled = true; - } - // Check for isolated pawns. - if - isolated && - ( - // Check on the left file if not on a-file... - ( - pos_f > POS_MIN && - is_piece(get_square(board, &(pos_f - 1, r)), color|SQ_P) - ) || - // Check on the right file if not on h-file... - ( - pos_f < POS_MAX && - is_piece(get_square(board, &(pos_f + 1, r)), color|SQ_P) - ) - ) - { - isolated = false; - } - // Check for backward pawns. - if backward { - if color == SQ_WH && r <= pos_r { - if ( - pos_f > POS_MIN && - is_type(get_square(board, &(pos_f - 1, r)), SQ_P) - ) || ( - pos_f < POS_MAX && - is_type(get_square(board, &(pos_f + 1, r)), SQ_P) - ) { - backward = false; - } - } else if color == SQ_BL && r >= pos_r { - if ( - pos_f > POS_MIN && - is_type(get_square(board, &(pos_f - 1, r)), SQ_P) - ) || ( - pos_f < POS_MAX && - is_type(get_square(board, &(pos_f + 1, r)), SQ_P) - ) { - backward = false; - } - } - } - } - if doubled { - stats.num_doubled_pawns += 1; - } - if isolated { - stats.num_isolated_pawns += 1; - } - if backward { - stats.num_backward_pawns += 1; - } - }, - _ => {} + for file in 0..8 { + for rank in 0..8 { + let square = sq(file, rank); + if board.is_empty(square) || board.get_color(square) != color { + continue + } + match board.get_piece(square) { + ROOK => self.num_rooks += 1, + KNIGHT => self.num_knights += 1, + BISHOP => self.num_bishops += 1, + QUEEN => self.num_queens += 1, + KING => self.num_kings += 1, + PAWN => { + self.num_pawns += 1; + // FIXME redo pawn stats properly + // let mut doubled = false; + // let mut isolated = true; + // let mut backward = true; + // for r in 0..8 { + // // Check for doubled pawns. + // if !doubled { + // let other_color = board.get_color(sq(file, r)); + // is_piece(get_square(board, &(pos_f, r)), color|SQ_P) && r != pos_r + // doubled = true; + // } + // // Check for isolated pawns. + // if + // isolated && + // ( + // // Check on the left file if not on a-file... + // ( + // pos_f > POS_MIN && + // is_piece(get_square(board, &(pos_f - 1, r)), color|SQ_P) + // ) || + // // Check on the right file if not on h-file... + // ( + // pos_f < POS_MAX && + // is_piece(get_square(board, &(pos_f + 1, r)), color|SQ_P) + // ) + // ) + // { + // isolated = false; + // } + // // Check for backward pawns. + // if backward { + // if color == SQ_WH && r <= pos_r { + // if ( + // pos_f > POS_MIN && + // is_type(get_square(board, &(pos_f - 1, r)), SQ_P) + // ) || ( + // pos_f < POS_MAX && + // is_type(get_square(board, &(pos_f + 1, r)), SQ_P) + // ) { + // backward = false; + // } + // } else if color == SQ_BL && r >= pos_r { + // if ( + // pos_f > POS_MIN && + // is_type(get_square(board, &(pos_f - 1, r)), SQ_P) + // ) || ( + // pos_f < POS_MAX && + // is_type(get_square(board, &(pos_f + 1, r)), SQ_P) + // ) { + // backward = false; + // } + // } + // } + // } + // if doubled { + // self.num_doubled_pawns += 1; + // } + // if isolated { + // self.num_isolated_pawns += 1; + // } + // if backward { + // self.num_backward_pawns += 1; + // } + }, + _ => {} + } } } } @@ -146,31 +162,6 @@ impl std::fmt::Display for BoardStats { } } -/// Create two new BoardStats objects from the board, for both sides. -/// -/// See `compute_stats_into` for details. -pub fn compute_stats(board: &Board, game_state: &GameState) -> (BoardStats, BoardStats) { - let mut stats = (BoardStats::new(), BoardStats::new()); - compute_stats_into(board, game_state, &mut stats); - stats -} - -/// Compute stats for both the current player and its opponent. -/// -/// The playing color will have its stats filled in the first -/// BoardStats object, its opponent in the second. -pub fn compute_stats_into( - board: &Board, - game_state: &rules::GameState, - stats: &mut (BoardStats, BoardStats) -) { - let mut gs = game_state.clone(); - compute_color_stats_into(board, &gs, &mut stats.0); - gs.color = opposite(gs.color); - compute_color_stats_into(board, &gs, &mut stats.1); -} - - #[cfg(test)] mod tests { use super::*; @@ -178,8 +169,8 @@ mod tests { #[test] fn test_compute_stats() { // Check that initial stats are correct. - let b = new(); - let gs = rules::GameState::new(); + let b = Board::new(); + let gs = GameState::new(); let initial_stats = BoardStats { num_pawns: 8, num_bishops: 2, @@ -192,56 +183,56 @@ mod tests { num_isolated_pawns: 0, mobility: 20, }; - let mut stats = compute_stats(&b, &gs); + let mut stats = BoardStats::new_from(&b, &gs); eprintln!("{}", stats.0); eprintln!("{}", stats.1); assert!(stats.0 == stats.1); assert!(stats.0 == initial_stats); // Check that doubled pawns are correctly counted. - let mut b = new_empty(); - set_square(&mut b, &pos("d4"), SQ_WH_P); - set_square(&mut b, &pos("d6"), SQ_WH_P); - compute_color_stats_into(&b, &gs, &mut stats.0); + let mut b = Board::new_empty(); + b.set_square(D4, WHITE, PAWN); + b.set_square(D6, WHITE, PAWN); + stats.0.compute(&b, &gs); assert_eq!(stats.0.num_doubled_pawns, 2); // Add a pawn on another file, no changes expected. - set_square(&mut b, &pos("e6"), SQ_WH_P); - compute_color_stats_into(&b, &gs, &mut stats.0); + b.set_square(E6, WHITE, PAWN); + stats.0.compute(&b, &gs); assert_eq!(stats.0.num_doubled_pawns, 2); // Add a pawn backward in the d-file: there are now 3 doubled pawns. - set_square(&mut b, &pos("d2"), SQ_WH_P); - compute_color_stats_into(&b, &gs, &mut stats.0); + b.set_square(D2, WHITE, PAWN); + stats.0.compute(&b, &gs); assert_eq!(stats.0.num_doubled_pawns, 3); // Check that isolated and backward pawns are correctly counted. assert_eq!(stats.0.num_isolated_pawns, 0); assert_eq!(stats.0.num_backward_pawns, 2); // A bit weird? // Protect d4 pawn with a friend in e3: it is not isolated nor backward anymore. - set_square(&mut b, &pos("e3"), SQ_WH_P); - compute_color_stats_into(&b, &gs, &mut stats.0); + b.set_square(E3, WHITE, PAWN); + stats.0.compute(&b, &gs); assert_eq!(stats.0.num_doubled_pawns, 5); assert_eq!(stats.0.num_isolated_pawns, 0); assert_eq!(stats.0.num_backward_pawns, 1); // Add an adjacent friend to d2 pawn: no pawns are left isolated or backward. - set_square(&mut b, &pos("c2"), SQ_WH_P); - compute_color_stats_into(&b, &gs, &mut stats.0); + b.set_square(C2, WHITE, PAWN); + stats.0.compute(&b, &gs); assert_eq!(stats.0.num_doubled_pawns, 5); assert_eq!(stats.0.num_isolated_pawns, 0); assert_eq!(stats.0.num_backward_pawns, 0); // Add an isolated/backward white pawn in a far file. - set_square(&mut b, &pos("a2"), SQ_WH_P); - compute_color_stats_into(&b, &gs, &mut stats.0); + b.set_square(A2, WHITE, PAWN); + stats.0.compute(&b, &gs); assert_eq!(stats.0.num_doubled_pawns, 5); assert_eq!(stats.0.num_isolated_pawns, 1); assert_eq!(stats.0.num_backward_pawns, 1); // Check for pawns that are backward but not isolated. - let mut b = new_empty(); + let mut b = Board::new_empty(); // Here, d4 pawn protects both e5 and e3, but it is backward. - set_square(&mut b, &pos("d4"), SQ_WH_P); - set_square(&mut b, &pos("e5"), SQ_WH_P); - set_square(&mut b, &pos("e3"), SQ_WH_P); - compute_color_stats_into(&b, &gs, &mut stats.0); + b.set_square(D4, WHITE, PAWN); + b.set_square(E5, WHITE, PAWN); + b.set_square(E3, WHITE, PAWN); + stats.0.compute(&b, &gs); assert_eq!(stats.0.num_doubled_pawns, 2); assert_eq!(stats.0.num_isolated_pawns, 0); assert_eq!(stats.0.num_backward_pawns, 1);