Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 39 additions & 16 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 11 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,25 @@ name = "mouse"
[dependencies]
getset = "0.1.6"

[build]
rustflags = ["-C", "target-cpu=native"]

[profile.release]
codegen-units = 1
lto = true

[dev-dependencies]
criterion = "0.7.0"
iai = "0.1"
iai = { git = "https://github.com/sigaloid/iai", rev = "d56a597" }
perft_fixtures = { path = "perft_fixtures" }

[[bench]]
name = "criterion_perft"
harness = false

# [[bench]]
# name = "iai_perft"
# harness = false
[[bench]]
name = "iai_perft"
harness = false

[[bench]]
name = "manual_perft"
Expand Down
26 changes: 9 additions & 17 deletions benches/criterion_perft.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use criterion::{Criterion, Throughput, criterion_group, criterion_main};
use mouse::backend::perft::perft;
use mouse::{GameState, get_pseudo_legal_moves};
use mouse::{State, get_pseudo_legal_moves};
use perft_fixtures::perft_fixtures::FAST_PERFT;

pub fn criterion_perft(c: &mut Criterion) {
Expand All @@ -12,7 +12,7 @@ pub fn criterion_perft(c: &mut Criterion) {
let depth = perft_set_up.depth;
let expected_nodes = perft_set_up.expected_nodes;

let mut state = GameState::new_from_fen(fen_string);
let mut state = State::new_from_fen(fen_string);

run_criterion_perft(c, name, depth, expected_nodes, &mut state);
}
Expand All @@ -23,47 +23,39 @@ fn run_criterion_perft(
name: String,
depth: u8,
expected_nodes: u64,
mut state: &mut GameState,
state: &mut State,
) {
let mut group = c.benchmark_group(&*name);
// group.sample_size(10);
// group.sampling_mode(Flat);
// group.warm_up_time(std::time::Duration::from_millis(1000));
group.throughput(Throughput::Elements(expected_nodes));
group.bench_function(&*name, |b| {
b.iter(|| {
perft(
std::hint::black_box(&mut state),
std::hint::black_box(depth),
)
})
b.iter(|| perft(std::hint::black_box(state), std::hint::black_box(depth)))
});
group.finish();
}

pub fn criterion_make_unmake_move(c: &mut Criterion) {
let mut state = GameState::new_from_fen(
"r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQ - 0 1",
);
let state =
State::new_from_fen("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQ - 0 1");
let moves = get_pseudo_legal_moves(&state);

let mut group = c.benchmark_group("General make unmake");
group.throughput(Throughput::Elements(moves.len() as u64));
group.bench_function("General make unmake", |b| {
b.iter(|| {
for moove in &moves[0..moves.len()] {
state.make_move(*moove);
state.unmake_move(*moove);
let _ = state.make_move(std::hint::black_box(*moove));
}
})
});
group.finish();
}

pub fn criterion_move_gen(c: &mut Criterion) {
let state = GameState::new_from_fen(
"r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1",
);
let state =
State::new_from_fen("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1");
let expected_moves = 48;

let mut group = c.benchmark_group("Move gen");
Expand Down
39 changes: 33 additions & 6 deletions benches/iai_perft.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use iai::black_box;
use mouse::GameState;
use mouse::State;
use mouse::backend::perft::perft;
use perft_fixtures::perft_fixtures::{NORMAL_PERFT, PerftFixture};
use perft_fixtures::perft_fixtures::{FAST_PERFT, PerftFixture};

pub fn iai_perft(perft_fixture: &PerftFixture) {
let fen_string = perft_fixture.perft_setup.fen;
Expand All @@ -11,13 +11,40 @@ pub fn iai_perft(perft_fixture: &PerftFixture) {
let depth = perft_fixture.depth;
let _ = perft_fixture.expected_nodes;

let mut state = GameState::new_from_fen(fen_string);
let state = State::new_from_fen(fen_string);

perft(black_box(&mut state), black_box(depth));
let _ = perft(black_box(&state), black_box(depth));
}

pub fn starting_perft() {
iai_perft(&NORMAL_PERFT[0]);
iai_perft(&FAST_PERFT[0]);
}

iai::main!(starting_perft);
pub fn position_2_perft() {
iai_perft(&FAST_PERFT[1]);
}

pub fn position_3_perft() {
iai_perft(&FAST_PERFT[2]);
}

pub fn position_4_perft() {
iai_perft(&FAST_PERFT[3]);
}

pub fn position_5_perft() {
iai_perft(&FAST_PERFT[4]);
}

pub fn position_6_perft() {
iai_perft(&FAST_PERFT[5]);
}

iai::main!(
starting_perft,
position_2_perft,
position_3_perft,
position_4_perft,
position_5_perft,
position_6_perft
);
6 changes: 3 additions & 3 deletions benches/manual_perft.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use mouse::GameState;
use mouse::State;
use mouse::backend::perft::perft;
use perft_fixtures::perft_fixtures::{NORMAL_PERFT, PerftFixture};
use std::time::Instant;
Expand Down Expand Up @@ -27,12 +27,12 @@ pub fn manual_perft() {

fn run_nps_perft(perft_fixture: PerftFixture) -> u64 {
let fen = perft_fixture.perft_setup.fen;
let mut game_state = GameState::new_from_fen(fen);
let state = State::new_from_fen(fen);

// Start timer to calculate nodes per second.
let now = Instant::now();

let nodes = perft(&mut game_state, perft_fixture.depth);
let nodes = perft(&state, perft_fixture.depth);

let elapsed = now.elapsed();
let nodes_per_second = nodes as f64 / elapsed.as_secs_f64();
Expand Down
54 changes: 26 additions & 28 deletions src/backend/movegen/check_decider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,20 @@ use crate::backend::movegen::compile_time::move_cache_non_sliders::{
KING_MOVES, KNIGHT_MOVES, PAWN_CAPTURE_MOVES,
};
use crate::backend::movegen::move_gen_sliders::calculate_slider_move_bitboard;
use crate::backend::state::board::bitboard::Bitboard;
use crate::backend::state::game::game_state::GameState;
use crate::backend::state::piece::PieceType::{Bishop, Queen, Rook};
use crate::backend::state::piece::{Piece, PieceColor, PieceType};
use crate::backend::state::board::bitboard::BitBoard;
use crate::backend::state::game::state::State;
use crate::backend::state::piece::Piece::{Bishop, Queen, Rook};
use crate::backend::state::piece::{ALL_PIECES, Piece, Side};
use crate::backend::state::square::Square;

pub fn is_in_check_on_square(
game_state: &GameState,
color: PieceColor,
king_square: Square,
) -> bool {
let friendly_bb = game_state.bb_manager().get_all_pieces_off(color);
let enemy_bb = game_state.bb_manager().get_all_pieces_off(color.opposite());
pub fn is_in_check_on_square(game_state: &State, color: Side, king_square: Square) -> bool {
let friendly_bb = game_state.bb_manager().get_all_pieces_bb_off(color);
let enemy_bb = game_state
.bb_manager()
.get_all_pieces_bb_off(color.opposite());

// Iterate over all pieces. Let`s assume we are checking for knights.
for piece_type in PieceType::get_all_types() {
for piece_type in ALL_PIECES {
// Get the bitboard that represents all possible attacks.
let attack_bitboard = get_attack_bitboard_for_piece_and_square(
piece_type,
Expand All @@ -28,11 +26,10 @@ pub fn is_in_check_on_square(
);

// Get the bitboard that marks where enemy knights are standing.
let enemy_piece = Piece::new(piece_type, color.opposite());
let enemy_piece_bitboard = game_state.bb_manager().get_bitboard(enemy_piece);
let enemy_piece_bitboard = game_state.bb_manager().get_piece_bb(piece_type) & enemy_bb;

// Check if at least one of the places we could move to contains an enemy knight.
let resulting_bitboard = attack_bitboard & *enemy_piece_bitboard;
let resulting_bitboard = attack_bitboard & enemy_piece_bitboard;
// If so, we know that the king is in check.
if resulting_bitboard.is_not_empty() {
return true;
Expand Down Expand Up @@ -60,7 +57,7 @@ pub fn is_in_check_on_square(
/// - A boolean value:
/// - `true` if the king of the specified color is in check.
/// - `false` otherwise.
pub fn is_in_check(game_state: &GameState, color: PieceColor) -> bool {
pub fn is_in_check(game_state: &State, color: Side) -> bool {
// Idea:
// If, for example, color == white, we want to figure out if white is currently in check.
// We then pretend that the white king is one after the other replaced by: pawn, rook, bishop, queen, king.
Expand All @@ -76,23 +73,24 @@ pub fn is_in_check(game_state: &GameState, color: PieceColor) -> bool {
}

/// Returns the square where the king of the respective side is located.
fn get_kings_square(game_state: &GameState, color: PieceColor) -> Square {
let king = Piece::new(PieceType::King, color);
let king_bitboard = game_state.bb_manager().get_bitboard(king);
king_bitboard.clone().next().unwrap()
fn get_kings_square(game_state: &State, color: Side) -> Square {
let king_bitboard = game_state.bb_manager().get_piece_bb(Piece::King);
let side_bb = game_state.bb_manager().get_all_pieces_bb_off(color);
let mut bb = king_bitboard & side_bb;
bb.next().unwrap()
}

fn get_attack_bitboard_for_piece_and_square(
piece_type: PieceType,
piece_color: PieceColor,
piece_type: Piece,
piece_color: Side,
square: Square,
friendly_bb: Bitboard,
enemy_bb: Bitboard,
) -> Bitboard {
friendly_bb: BitBoard,
enemy_bb: BitBoard,
) -> BitBoard {
match piece_type {
PieceType::King => KING_MOVES[square.square_to_index()],
PieceType::Knight => KNIGHT_MOVES[square.square_to_index()],
PieceType::Pawn => PAWN_CAPTURE_MOVES[piece_color as usize][square.square_to_index()],
Piece::King => KING_MOVES[square.square_to_index()],
Piece::Knight => KNIGHT_MOVES[square.square_to_index()],
Piece::Pawn => PAWN_CAPTURE_MOVES[piece_color as usize][square.square_to_index()],
Rook => calculate_slider_move_bitboard(Rook, square, friendly_bb, enemy_bb),
Bishop => calculate_slider_move_bitboard(Bishop, square, friendly_bb, enemy_bb),
Queen => calculate_slider_move_bitboard(Queen, square, friendly_bb, enemy_bb),
Expand Down
Loading