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
3 changes: 3 additions & 0 deletions perftree_script.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash

./target/debug/Mouse "$@"
3 changes: 0 additions & 3 deletions src/backend/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
pub mod moove;
pub(crate) mod movegen;
pub mod perft;
pub mod piece;
pub mod square;
pub(crate) mod state;
33 changes: 24 additions & 9 deletions src/backend/movegen/check_decider.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use crate::backend::movegen::compile_time::move_cache_non_sliders::get_moves_cache_for_piece;
use crate::backend::piece::{Piece, PieceColor, PieceType};
use crate::backend::square::Square;
use crate::backend::state::game_state::GameState;
use crate::backend::movegen::compile_time::move_cache_non_sliders::{
KING_MOVES, KNIGHT_MOVES, PAWN_CAPTURE_MOVES,
};
use crate::backend::state::board::bitboard::BitBoard;
use crate::backend::state::game::game_state::GameState;
use crate::backend::state::piece::{Piece, PieceColor, PieceType};
use crate::backend::state::square::Square;

/// Checks if a given player's king is in check in the current game state.
///
Expand Down Expand Up @@ -38,17 +41,16 @@ pub fn is_in_check(game_state: &GameState, color: PieceColor) -> bool {
// SLIDER: I think this needs to be changed for sliders.
// Iterate over all pieces. Let`s assume we are checking for knights.
for piece_type in PieceType::get_all_types() {
// Get the move cache for the knight...
let moves_cache = get_moves_cache_for_piece(piece_type);
// ...and get the potential moves for the knight from the square of the king.
let piece_move_bitboard = moves_cache[king_square.square_to_index()];
// Get the bitboard that represents all possible attacks.
let attack_bitboard =
get_attack_bitboard_for_piece_and_square(piece_type, color, king_square);

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

// Check if at least one of the places we could move to contains an enemy knight.
let resulting_bitboard = piece_move_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 All @@ -64,3 +66,16 @@ fn get_kings_square(game_state: &GameState, color: PieceColor) -> Square {
let king_square = king_bitboard.get_all_true_squares();
king_square[0]
}

fn get_attack_bitboard_for_piece_and_square(
piece_type: PieceType,
piece_color: PieceColor,
square: Square,
) -> 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()],
_ => panic!("Not implemented yet"),
}
}
119 changes: 108 additions & 11 deletions src/backend/movegen/compile_time/move_cache_non_sliders.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::backend::piece::PieceType;
use crate::backend::square::Square;
use crate::backend::state::bitboard::BitBoard;
use crate::constants::SQUARES_AMOUNT;
use crate::backend::state::board::bitboard::BitBoard;
use crate::backend::state::piece::{PieceColor, PieceType};
use crate::backend::state::square::Square;
use crate::constants::{SIDES, SQUARES_AMOUNT};

/// All of this gets generated at compile time, in the functions below.
/// At runtime, we only have to read the values.
Expand All @@ -25,15 +25,25 @@ pub const KING_MOVES: [BitBoard; SQUARES_AMOUNT] = calculate_potential_moves_cac
pub const KNIGHT_MOVES: [BitBoard; SQUARES_AMOUNT] =
calculate_potential_moves_cache(PieceType::Knight);

pub fn get_moves_cache_for_piece(piece_type: PieceType) -> [BitBoard; SQUARES_AMOUNT] {
match piece_type {
PieceType::Knight => KNIGHT_MOVES,
PieceType::King => KING_MOVES,
_ => panic!("Invalid piece type"),
}
enum PawnMoveType {
Quiet,
Capture,
DoublePush,
}

/// Initializes a collection of bitboards representing all possible king moves for each square.
/// All quiet moves for pawns.
pub const PAWN_QUIET_MOVES: [[BitBoard; SQUARES_AMOUNT]; SIDES] =
generate_pawn_moves(PawnMoveType::Quiet);

/// All capture moves for pawns.
pub const PAWN_CAPTURE_MOVES: [[BitBoard; SQUARES_AMOUNT]; SIDES] =
generate_pawn_moves(PawnMoveType::Capture);

/// All capture moves for pawns.
pub const PAWN_DOUBLE_PUSH_MOVES: [[BitBoard; SQUARES_AMOUNT]; SIDES] =
generate_pawn_moves(PawnMoveType::DoublePush);

/// Initializes a collection of bitboards representing all possible moves for each square.
///
/// Since this function is const, it can be evaluated at compile time.
/// # Parameters
Expand Down Expand Up @@ -138,3 +148,90 @@ const fn generate_knight_moves(square: Square) -> BitBoard {

bitboard
}

const fn generate_pawn_moves(pawn_move_type: PawnMoveType) -> [[BitBoard; SQUARES_AMOUNT]; SIDES] {
let mut quiet_moves = [[BitBoard::new(); SQUARES_AMOUNT]; SIDES];

let mut side_index = 0;
while side_index < 2 {
let active_color = match side_index {
0 => PieceColor::White,
1 => PieceColor::Black,
_ => panic!("Invalid side index"),
};
let mut potential_moves = [BitBoard::new(); SQUARES_AMOUNT];

// iterate over all squares
let mut square_index: usize = 0;
while square_index < SQUARES_AMOUNT {
let mut bitboard = BitBoard::new();
// generate a square struct from the index
let square = Square::index_to_square(square_index as i8);

match pawn_move_type {
PawnMoveType::Quiet => {
generate_pawn_quiet_moves(square, &mut bitboard, active_color);
}
PawnMoveType::Capture => {
generate_pawn_attack_moves(square, &mut bitboard, active_color);
}
PawnMoveType::DoublePush => {
generate_pawn_double_push_moves(square, &mut bitboard, active_color);
}
}

// and generate the moves for that square
potential_moves[square_index] = bitboard;

square_index += 1;
}

quiet_moves[side_index] = potential_moves;

side_index += 1;
}

quiet_moves
}

const fn generate_pawn_quiet_moves(
square: Square,
bitboard: &mut BitBoard,
active_color: PieceColor,
) {
let forward_square = square.forward_by_one(active_color);
if forward_square.is_valid() {
bitboard.fill_square(forward_square);
}
}

const fn generate_pawn_attack_moves(
square: Square,
bitboard: &mut BitBoard,
active_color: PieceColor,
) {
let right_diagonal_square = square.right_by_one().forward_by_one(active_color);
let left_diagonal_square = square.left_by_one().forward_by_one(active_color);

if right_diagonal_square.is_valid() {
bitboard.fill_square(right_diagonal_square);
}
if left_diagonal_square.is_valid() {
bitboard.fill_square(left_diagonal_square);
}
}

const fn generate_pawn_double_push_moves(
square: Square,
bitboard: &mut BitBoard,
active_color: PieceColor,
) {
if !square.is_pawn_start(active_color) {
return;
}

let double_pushed_square = square
.forward_by_one(active_color)
.forward_by_one(active_color);
bitboard.fill_square(double_pushed_square);
}
3 changes: 2 additions & 1 deletion src/backend/movegen/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod check_decider;
pub mod compile_time;
pub mod moove;
pub mod move_gen;
mod move_gen_non_sliders;
mod move_gen_pawn_util;
63 changes: 42 additions & 21 deletions src/backend/moove.rs → src/backend/movegen/moove.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,9 @@
use crate::backend::square::Square;
use crate::backend::state::piece::PieceType;
use crate::backend::state::piece::PieceType::{Bishop, Knight, Queen, Rook};
use crate::backend::state::square::Square;
use getset::{CloneGetters, Setters};
use std::fmt::{Display, Formatter};

/// Represents the various types of promotions that can occur in a game of chess.
///
/// Has an additional `NONE` option to represent no promotion.
#[derive(Copy, Clone, Debug)]
pub enum PromotionType {
Rook,
Knight,
Bishop,
Queen,
None,
}

/// This encodes a single move. Sidenote: This is called Moove, since Move is a keyword in Rust...
/// It knows where a piece moved from and where it moved to.
/// Also stores to which piece a pawn promoted if one did at all.
Expand All @@ -25,14 +15,14 @@ pub enum PromotionType {
/// Sure, the move would be smaller, but accessing a variable would be slower, since it requires bit shifting etc.
/// In the end it comes down to a trade-off between cache locality and number of instructions per read.
/// 2. It would certainly make the code less readable.
#[derive(Copy, Clone, Debug, CloneGetters, Setters)]
#[derive(Copy, Clone, Debug, CloneGetters, Setters, Ord, Eq, PartialEq, PartialOrd)]
pub struct Moove {
#[getset(get_clone = "pub", set = "pub")]
from: Square,
#[getset(get_clone = "pub", set = "pub")]
to: Square,
#[getset(get_clone = "pub", set = "pub")]
promotion_type: PromotionType,
promotion_type: Option<PieceType>,
}

impl Moove {
Expand All @@ -41,7 +31,35 @@ impl Moove {
Moove {
from,
to,
promotion_type: PromotionType::None,
promotion_type: Option::None,
}
}

/// This assumes that the moved piece is a pawn and only checks if the rank changed by 2.
pub fn is_double_pawn_push(&self) -> bool {
(self.from.rank() - self.to.rank()).abs() == 2
}

pub fn new_from_uci_notation(uci_notation: &str) -> Moove {
let from = Square::new_from_uci_notation(&uci_notation[0..2]);
let to = Square::new_from_uci_notation(&uci_notation[2..4]);

let promotion_char = uci_notation.chars().nth(4);
let promotion_type = match promotion_char {
None => Option::None,
Some(char) => match char {
'r' => Some(Rook),
'n' => Some(Knight),
'b' => Some(Bishop),
'q' => Some(Queen),
_ => panic!("Invalid promotion type {:?}", uci_notation),
},
};

Moove {
from,
to,
promotion_type,
}
}
}
Expand All @@ -54,11 +72,14 @@ impl Display for Moove {
result.push_str(&self.from.to_string());
result.push_str(&self.to.to_string());
result.push_str(match self.promotion_type {
PromotionType::Rook => "r",
PromotionType::Knight => "n",
PromotionType::Bishop => "b",
PromotionType::Queen => "q",
PromotionType::None => "",
None => "",
Some(promotion_type) => match promotion_type {
Rook => "r",
Knight => "n",
Bishop => "b",
Queen => "q",
_ => panic!("Invalid promotion type {:?}", promotion_type),
},
});

write!(f, "{}", result)
Expand Down
Loading