[board, core, position] A simple static evaluation method for scoring positions
Implement a new Evaluator struct that evaluates a Board and returns a score. This evaluation mechanism uses only a material balance function. It doesn't account for anything else. Supporting this, add a Counts struct to the internal piece set structure of a Board. This struct is responsible for keeping counts of how many pieces of each shape are on the board for each color. Export a count_piece() method on Board that returns a count of the number of pieces of a particular color and shape. Implement a newtype wrapper around i32 called Score that represents the score of a position in centipawns, i.e. hundredths of a pawn. Add piece values to the Shape enum.
This commit is contained in:
parent
481ae70698
commit
7f25548335
10 changed files with 249 additions and 10 deletions
|
@ -3,7 +3,7 @@
|
|||
use crate::{
|
||||
CastleRights, PieceSet,
|
||||
display::DiagramFormatter,
|
||||
piece_sets::{PlacePieceError, PlacePieceStrategy},
|
||||
piece_sets::{Counter, PlacePieceError, PlacePieceStrategy},
|
||||
zobrist::{ZobristHash, ZobristState},
|
||||
};
|
||||
use chessfriend_bitboard::BitBoard;
|
||||
|
@ -219,6 +219,11 @@ impl Board {
|
|||
|
||||
removed_piece
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn count_piece(&self, piece: &Piece) -> Counter {
|
||||
self.pieces.count(piece)
|
||||
}
|
||||
}
|
||||
|
||||
impl Board {
|
||||
|
|
|
@ -9,9 +9,10 @@ use thiserror::Error;
|
|||
|
||||
#[macro_export]
|
||||
macro_rules! fen {
|
||||
($fen_string:literal) => {
|
||||
($fen_string:literal) => {{
|
||||
use $crate::fen::FromFenStr;
|
||||
Board::from_fen_str($fen_string)
|
||||
};
|
||||
}};
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Error, Eq, PartialEq)]
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
mod counts;
|
||||
mod mailbox;
|
||||
|
||||
use self::mailbox::Mailbox;
|
||||
use self::{counts::Counts, mailbox::Mailbox};
|
||||
use chessfriend_bitboard::{BitBoard, IterationDirection};
|
||||
use chessfriend_core::{Color, Piece, Shape, Square};
|
||||
use std::{
|
||||
|
@ -11,6 +12,8 @@ use std::{
|
|||
};
|
||||
use thiserror::Error;
|
||||
|
||||
pub(crate) use counts::Counter;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||
pub enum PlacePieceStrategy {
|
||||
#[default]
|
||||
|
@ -29,6 +32,7 @@ pub enum PlacePieceError {
|
|||
#[derive(Clone, Debug, Default, Eq)]
|
||||
pub struct PieceSet {
|
||||
mailbox: Mailbox,
|
||||
counts: Counts,
|
||||
color_occupancy: [BitBoard; Color::NUM],
|
||||
shape_occupancy: [BitBoard; Shape::NUM],
|
||||
}
|
||||
|
@ -36,18 +40,21 @@ pub struct PieceSet {
|
|||
impl PieceSet {
|
||||
pub(crate) fn new(pieces: [[BitBoard; Shape::NUM]; Color::NUM]) -> Self {
|
||||
let mut mailbox = Mailbox::default();
|
||||
let mut counts = Counts::default();
|
||||
let mut color_occupancy: [BitBoard; Color::NUM] = Default::default();
|
||||
let mut shape_occupancy: [BitBoard; Shape::NUM] = Default::default();
|
||||
|
||||
for (color_index, color) in Color::iter().enumerate() {
|
||||
for (color_index, color) in Color::into_iter().enumerate() {
|
||||
for (shape_index, shape) in Shape::into_iter().enumerate() {
|
||||
let bitboard = pieces[color_index][shape_index];
|
||||
|
||||
color_occupancy[color_index] |= bitboard;
|
||||
shape_occupancy[shape_index] |= bitboard;
|
||||
|
||||
counts.increment(color, shape);
|
||||
|
||||
for square in bitboard.occupied_squares(&IterationDirection::default()) {
|
||||
let piece = Piece::new(*color, shape);
|
||||
let piece = Piece::new(color, shape);
|
||||
mailbox.set(piece, square);
|
||||
}
|
||||
}
|
||||
|
@ -55,6 +62,7 @@ impl PieceSet {
|
|||
|
||||
Self {
|
||||
mailbox,
|
||||
counts,
|
||||
color_occupancy,
|
||||
shape_occupancy,
|
||||
}
|
||||
|
@ -94,6 +102,10 @@ impl PieceSet {
|
|||
self.mailbox.get(square)
|
||||
}
|
||||
|
||||
pub(crate) fn count(&self, piece: &Piece) -> Counter {
|
||||
self.counts.get(piece.color, piece.shape)
|
||||
}
|
||||
|
||||
// TODO: Rename this. Maybe get_all() is better?
|
||||
pub(crate) fn find_pieces(&self, piece: Piece) -> BitBoard {
|
||||
let color_occupancy = self.color_occupancy[piece.color as usize];
|
||||
|
@ -120,6 +132,7 @@ impl PieceSet {
|
|||
|
||||
self.color_occupancy[color as usize].set(square);
|
||||
self.shape_occupancy[shape as usize].set(square);
|
||||
self.counts.increment(color, shape);
|
||||
self.mailbox.set(piece, square);
|
||||
|
||||
Ok(existing_piece)
|
||||
|
@ -127,8 +140,12 @@ impl PieceSet {
|
|||
|
||||
pub(crate) fn remove(&mut self, square: Square) -> Option<Piece> {
|
||||
if let Some(piece) = self.mailbox.get(square) {
|
||||
self.color_occupancy[piece.color as usize].clear(square);
|
||||
self.shape_occupancy[piece.shape as usize].clear(square);
|
||||
let color_index = piece.color as usize;
|
||||
let shape_index = piece.shape as usize;
|
||||
|
||||
self.color_occupancy[color_index].clear(square);
|
||||
self.shape_occupancy[shape_index].clear(square);
|
||||
self.counts.decrement(piece.color, piece.shape);
|
||||
self.mailbox.remove(square);
|
||||
|
||||
Some(piece)
|
||||
|
|
60
board/src/piece_sets/counts.rs
Normal file
60
board/src/piece_sets/counts.rs
Normal file
|
@ -0,0 +1,60 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
use chessfriend_core::{Color, Shape, Square};
|
||||
|
||||
pub(crate) type Counter = u8;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub(super) struct Counts([[Counter; Shape::NUM]; Color::NUM]);
|
||||
|
||||
impl Counts {
|
||||
pub fn get(&self, color: Color, shape: Shape) -> Counter {
|
||||
self.0[color as usize][shape as usize]
|
||||
}
|
||||
|
||||
pub fn increment(&mut self, color: Color, shape: Shape) {
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
const SQUARE_NUM: u8 = Square::NUM as u8;
|
||||
|
||||
let updated_value = self.0[color as usize][shape as usize] + 1;
|
||||
if updated_value <= SQUARE_NUM {
|
||||
self.0[color as usize][shape as usize] = updated_value;
|
||||
} else {
|
||||
unreachable!("piece count for {color} {shape} overflowed");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decrement(&mut self, color: Color, shape: Shape) {
|
||||
let count = self.0[color as usize][shape as usize];
|
||||
if let Some(updated_count) = count.checked_sub(1) {
|
||||
self.0[color as usize][shape as usize] = updated_count;
|
||||
} else {
|
||||
unreachable!("piece count for {color} {shape} underflowed");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn set(&mut self, color: Color, shape: Shape, value: u8) {
|
||||
self.0[color as usize][shape as usize] = value;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "underflowed")]
|
||||
fn underflow() {
|
||||
let mut counts = Counts::default();
|
||||
counts.decrement(Color::White, Shape::Queen);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "overflowed")]
|
||||
fn overflow() {
|
||||
let mut counts = Counts::default();
|
||||
counts.set(Color::White, Shape::Queen, 64);
|
||||
counts.increment(Color::White, Shape::Queen);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue