aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/board/mod.rs1
-rw-r--r--src/board/move_generation.rs (renamed from src/grossmeister/move_generation.rs)120
-rw-r--r--src/grossmeister/mod.rs1
-rw-r--r--src/grossmeister/search.rs66
-rw-r--r--src/lib.rs2
-rw-r--r--src/player.rs7
6 files changed, 102 insertions, 95 deletions
diff --git a/src/board/mod.rs b/src/board/mod.rs
index aab3136..ff01ddc 100644
--- a/src/board/mod.rs
+++ b/src/board/mod.rs
@@ -4,6 +4,7 @@ use self::{zobrist::{ZobristSeed, Zobrist}, piece::Piece, color::Color};
pub mod io;
pub mod color;
pub mod piece;
+pub mod move_generation;
mod zobrist;
#[derive(Debug, Clone, Copy)]
diff --git a/src/grossmeister/move_generation.rs b/src/board/move_generation.rs
index ea40d18..77714c8 100644
--- a/src/grossmeister/move_generation.rs
+++ b/src/board/move_generation.rs
@@ -1,11 +1,9 @@
-use std::cmp::Ordering;
-
use crate::{moves::{Move, MoveKind}, board::{color::Color, piece::Piece, CastlingSide}, bitboard::BitboardFns, square::Square};
-use super::Grossmeister;
+use super::Board;
-impl Grossmeister {
+impl Board {
pub fn generate_pseudolegal_moves(&self) -> Vec<Move> {
let mut result = Vec::new();
result.append(&mut self.generate_moves_core(true));
@@ -14,16 +12,16 @@ impl Grossmeister {
}
fn generate_moves_core(&self, tactical_only: bool) -> Vec<Move> {
- let color = self.board.color();
- let player_pieces = self.board.pieces_by_color(color);
+ let color = self.color();
+ let player_pieces = self.pieces_by_color(color);
let mut moves = Vec::with_capacity(256);
- let empty = self.board.empty();
+ let empty = self.empty();
let targets = if tactical_only {
- self.board.color_occupancy(color.flip()) ^ match color {
+ self.color_occupancy(color.flip()) ^ match color {
// Exclude opponent king because we can't capture it
- Color::White => self.board.piece_sets[Piece::KingBlack as usize],
- Color::Black => self.board.piece_sets[Piece::King as usize],
+ Color::White => self.piece_sets[Piece::KingBlack as usize],
+ Color::Black => self.piece_sets[Piece::King as usize],
}
} else {
empty
@@ -39,9 +37,9 @@ impl Grossmeister {
for source in piece.serialize() {
let piece_type = Piece::from(piece_id);
let move_targets = match piece_type {
- Piece::Bishop => self.attacks.bishop(self.board.occupancy, source),
- Piece::Rook => self.attacks.rook(self.board.occupancy, source),
- Piece::Queen => self.attacks.queen(self.board.occupancy, source),
+ Piece::Bishop => self.attacks.bishop(self.occupancy, source),
+ Piece::Rook => self.attacks.rook(self.occupancy, source),
+ Piece::Queen => self.attacks.queen(self.occupancy, source),
Piece::Knight => self.attacks.knight[source as usize],
Piece::King => self.attacks.king[source as usize],
Piece::Pawn => if tactical_only {
@@ -72,7 +70,7 @@ impl Grossmeister {
// En Passant
if piece_type == Piece::Pawn && tactical_only {
- for target in (move_targets & self.board.ep_bitboard()).serialize() {
+ for target in (move_targets & self.ep_bitboard()).serialize() {
moves.push(Move { source, target, kind: MoveKind::EnPassant });
}
}
@@ -117,9 +115,9 @@ impl Grossmeister {
king_home_position,
king_home_position.west_one(),
king_home_position.west_one().west_one(),
- ].iter().any(|square| self.board.is_square_attacked(*square, color.flip()));
+ ].iter().any(|square| self.is_square_attacked(*square, color.flip()));
- if all_empty && !any_checks && self.board.castling_rights[color as usize][CastlingSide::Queen as usize] {
+ if all_empty && !any_checks && self.castling_rights[color as usize][CastlingSide::Queen as usize] {
moves.push(Move {
source: king_home_position,
target: king_home_position.west_one().west_one(),
@@ -138,9 +136,9 @@ impl Grossmeister {
king_home_position,
king_home_position.east_one(),
king_home_position.east_one().east_one(),
- ].iter().any(|square| self.board.is_square_attacked(*square, color.flip()));
+ ].iter().any(|square| self.is_square_attacked(*square, color.flip()));
- if all_empty && !any_checks && self.board.castling_rights[color as usize][CastlingSide::King as usize] {
+ if all_empty && !any_checks && self.castling_rights[color as usize][CastlingSide::King as usize] {
moves.push(Move {
source: king_home_position,
target: king_home_position.east_one().east_one(),
@@ -155,65 +153,6 @@ impl Grossmeister {
}
moves
}
-
- /// Evaluate move for move ordering, prioritizing efficient captures
- /// where low-value pieces capture high-value pieces
- fn eval_move(&self, m: Move) -> f32 {
- if m.is_tactical() {
- let [source_eval, target_eval] = [m.source, m.target]
- .map(|sq| self.board.piece_by_square(sq))
- .map(|p| {
- match p {
- Some(p) => p.static_eval(),
- None => 0.,
- }
- });
- return 2. * target_eval - source_eval
- }
- 0.0
- }
-
- pub fn order_moves(&self, moves: Vec<Move>, killer_moves: Vec<Move>) -> Vec<Move> {
- let mut moves_with_eval: Vec<(Move, f32)> = moves
- .iter()
- .map(|m| (*m, self.eval_move(*m)))
- .collect();
-
- moves_with_eval.sort_unstable_by(|(a, a_eval), (b, b_eval)| {
- if *a_eval == 0.0 && *b_eval == 0.0 {
- // Prioritize equal captures over non-captures
- if a.is_tactical() && !b.is_tactical() {
- return Ordering::Less
- }
- if b.is_tactical() && !a.is_tactical() {
- return Ordering::Greater
- }
- }
- a_eval.total_cmp(b_eval).reverse()
- });
-
- let mut ordered_moves: Vec<Move> = moves_with_eval.iter().map(|(m, _)| *m).collect();
-
- // Insert killer moves after winning captures
- let equal_capture_index = moves_with_eval
- .iter()
- .position(|(m, eval)| m.is_tactical() && *eval == 0.0)
- .unwrap_or(0);
-
- for killer in killer_moves {
- if let Some(index) = ordered_moves.iter().position(|m| *m == killer) {
- let mov = ordered_moves.remove(index);
- ordered_moves.insert(equal_capture_index, mov);
- }
- }
-
- if let Some(transposition) = self.transposition() {
- ordered_moves.insert(0, transposition.mov);
- }
-
- ordered_moves
- }
-
}
#[cfg(test)]
@@ -224,11 +163,10 @@ mod tests {
#[test]
fn generate_pseudolegal_moves_starting_position() {
- let board = Board::new();
- let mut gm = Grossmeister::new(board);
- let moves = gm.generate_pseudolegal_moves();
- gm.board.ply += 1;
- let black_moves = gm.generate_pseudolegal_moves();
+ let mut board = Board::new();
+ let moves = board.generate_pseudolegal_moves();
+ board.ply += 1;
+ let black_moves = board.generate_pseudolegal_moves();
assert_eq!(moves.len(), 20);
assert_eq!(black_moves.len(), 20);
@@ -242,21 +180,21 @@ mod tests {
#[test]
fn moved_king_castle() {
let fen = String::from("4k2r/ppp1n3/8/4R1Pp/5P2/q1P5/P1P1BP2/1K1R4 b - - 2 22");
- let board = Board::from_FEN(fen);
- let mut gm = Grossmeister::new(board);
- gm.board.ply += 1;
+ let mut board = Board::from_FEN(fen);
+ board.ply += 1;
// Shuffle kings around, returning to the same position
- gm.board.make_move(Move { source: Square::E8, target: Square::F8, kind: MoveKind::Quiet });
- gm.board.make_move(Move { source: Square::B1, target: Square::A1, kind: MoveKind::Quiet });
- gm.board.make_move(Move { source: Square::F8, target: Square::E8, kind: MoveKind::Quiet });
- gm.board.make_move(Move { source: Square::A1, target: Square::B1, kind: MoveKind::Quiet });
- gm.board.print();
+ board.make_move(Move { source: Square::E8, target: Square::F8, kind: MoveKind::Quiet });
+ board.make_move(Move { source: Square::B1, target: Square::A1, kind: MoveKind::Quiet });
+ board.make_move(Move { source: Square::F8, target: Square::E8, kind: MoveKind::Quiet });
+ board.make_move(Move { source: Square::A1, target: Square::B1, kind: MoveKind::Quiet });
+ board.print();
- let moves = gm.generate_pseudolegal_moves();
+ let moves = board.generate_pseudolegal_moves();
let castle = moves.iter().find(|m| **m == Move { source: Square::E8, target: Square::G8, kind: MoveKind::Castle });
println!("{:?}", board.castling_rights);
assert!(castle.is_none(), "Castle should not be allowed after king has moved");
}
}
+
diff --git a/src/grossmeister/mod.rs b/src/grossmeister/mod.rs
index 6ed5f17..d9e5d1d 100644
--- a/src/grossmeister/mod.rs
+++ b/src/grossmeister/mod.rs
@@ -2,7 +2,6 @@ use crate::{board::Board, attacks::Attacks};
use self::ttable::{TranspositionTable, TTABLE_SIZE};
mod ttable;
-mod move_generation;
mod evaluation;
mod search;
diff --git a/src/grossmeister/search.rs b/src/grossmeister/search.rs
index 3efa9f8..ff6375c 100644
--- a/src/grossmeister/search.rs
+++ b/src/grossmeister/search.rs
@@ -1,3 +1,4 @@
+use std::cmp::Ordering;
use std::{time::{Instant, Duration}, f32::INFINITY};
use crate::{moves::{Move, MoveKind}, board::io::IO};
@@ -48,7 +49,7 @@ impl Grossmeister {
return (self.quiscence(alpha, beta), principal_variation);
}
- let mut moves = self.generate_pseudolegal_moves();
+ let mut moves = self.board.generate_pseudolegal_moves();
moves = self.order_moves(moves, parent_killers.to_vec());
let mut should_pv_search = true;
@@ -141,7 +142,7 @@ impl Grossmeister {
pub fn quiscence(&mut self, mut alpha: f32, beta: f32) -> f32 {
let color = self.board.color();
- let mut moves = self.generate_pseudolegal_moves();
+ let mut moves = self.board.generate_pseudolegal_moves();
moves = self.order_moves(moves, Vec::new());
if !self.board.is_king_in_check(color) {
@@ -259,6 +260,65 @@ impl Grossmeister {
}
}
+ /// Evaluate move for move ordering, prioritizing efficient captures
+ /// where low-value pieces capture high-value pieces
+ fn eval_move(&self, m: Move) -> f32 {
+ if m.is_tactical() {
+ let [source_eval, target_eval] = [m.source, m.target]
+ .map(|sq| self.board.piece_by_square(sq))
+ .map(|p| {
+ match p {
+ Some(p) => p.static_eval(),
+ None => 0.,
+ }
+ });
+ return 2. * target_eval - source_eval
+ }
+ 0.0
+ }
+
+ pub fn order_moves(&self, moves: Vec<Move>, killer_moves: Vec<Move>) -> Vec<Move> {
+ let mut moves_with_eval: Vec<(Move, f32)> = moves
+ .iter()
+ .map(|m| (*m, self.eval_move(*m)))
+ .collect();
+
+ moves_with_eval.sort_unstable_by(|(a, a_eval), (b, b_eval)| {
+ if *a_eval == 0.0 && *b_eval == 0.0 {
+ // Prioritize equal captures over non-captures
+ if a.is_tactical() && !b.is_tactical() {
+ return Ordering::Less
+ }
+ if b.is_tactical() && !a.is_tactical() {
+ return Ordering::Greater
+ }
+ }
+ a_eval.total_cmp(b_eval).reverse()
+ });
+
+ let mut ordered_moves: Vec<Move> = moves_with_eval.iter().map(|(m, _)| *m).collect();
+
+ // Insert killer moves after winning captures
+ let equal_capture_index = moves_with_eval
+ .iter()
+ .position(|(m, eval)| m.is_tactical() && *eval == 0.0)
+ .unwrap_or(0);
+
+ for killer in killer_moves {
+ if let Some(index) = ordered_moves.iter().position(|m| *m == killer) {
+ let mov = ordered_moves.remove(index);
+ ordered_moves.insert(equal_capture_index, mov);
+ }
+ }
+
+ if let Some(transposition) = self.transposition() {
+ ordered_moves.insert(0, transposition.mov);
+ }
+
+ ordered_moves
+ }
+
+
pub fn perft(&mut self, depth: u8, print: bool) -> PerftResult {
let mut result = PerftResult::default();
@@ -268,7 +328,7 @@ impl Grossmeister {
}
let color = self.board.color();
- let moves = self.generate_pseudolegal_moves();
+ let moves = self.board.generate_pseudolegal_moves();
if print {
println!("Running perft for depth {}. Color to move is {:?}\n{} moves available", depth, color, moves.len());
diff --git a/src/lib.rs b/src/lib.rs
index beebe77..93dc573 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -3,4 +3,6 @@ pub mod bitboard;
pub mod board;
pub mod attacks;
pub mod moves;
+pub mod player;
pub mod grossmeister;
+
diff --git a/src/player.rs b/src/player.rs
new file mode 100644
index 0000000..26cf0cc
--- /dev/null
+++ b/src/player.rs
@@ -0,0 +1,7 @@
+use crate::{board::Board, moves::Move};
+
+pub trait Player {
+ /// Analyze a position on a given board, giving
+ /// the score for the side to move and Principal Variation.
+ fn analyze(&self, board: Board) -> (f32, Vec<Move>);
+}