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
31 changes: 22 additions & 9 deletions src/backend/move_gen/check_decider.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use crate::backend::move_gen::move_gen_king::KING_MOVES;
use crate::backend::piece::PieceType::King;
use crate::backend::move_gen::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;
Expand Down Expand Up @@ -36,15 +35,29 @@ pub fn is_in_check(game_state: &GameState, color: PieceColor) -> bool {
// I hope this makes sense :)
let king_square = get_kings_square(game_state, color);

// For now, I will just implement it for the king.
let king_move_bitboard = KING_MOVES[king_square.square_to_index() as usize];
let enemey_king_bitboard = game_state
.bit_board_manager()
.get_bitboard(Piece::new(King, color.opposite()));
let resulting_bitboard = king_move_bitboard & *enemey_king_bitboard;
resulting_bitboard.is_not_empty()
// 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 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;
// If so, we know that the king is in check.
if resulting_bitboard.is_not_empty() {
return true;
}
}
false
}

/// 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.bit_board_manager().get_bitboard(king);
Expand Down
1 change: 1 addition & 0 deletions src/backend/move_gen/compile_time/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod move_cache_non_sliders;
140 changes: 140 additions & 0 deletions src/backend/move_gen/compile_time/move_cache_non_sliders.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
use crate::backend::piece::PieceType;
use crate::backend::square::Square;
use crate::backend::state::bitboard::BitBoard;
use crate::constants::SQUARES_AMOUNT;

/// All of this gets generated at compile time, in the functions below.
/// At runtime, we only have to read the values.
/// It contains for each square, a bitboard with every square that the king could potentially move to set to 1.
/// Example:
/// This: `KING_MOVES[Square::new(0,0)]` returns at bitboard that looks like this:
/// `_ _ _ _ _ _ _ _`
/// `_ _ _ _ _ _ _ _`
/// `_ _ _ _ _ _ _ _`
/// `_ _ _ _ _ _ _ _`
/// `_ _ _ _ _ _ _ _`
/// `_ _ _ _ _ _ _ _`
/// `X X _ _ _ _ _ _`
/// `_ X _ _ _ _ _ _`
/// At runtime we have to apply some further checks to this bitboard:
/// 1. Are some of these squares blocked by friendly pieces?
/// 2. Would this move put me in check etc...?
pub const KING_MOVES: [BitBoard; SQUARES_AMOUNT] = calculate_potential_moves_cache(PieceType::King);

/// The same as above, but for the knight.
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"),
}
}

/// Initializes a collection of bitboards representing all possible king moves for each square.
///
/// Since this function is const, it can be evaluated at compile time.
/// # Parameters
/// - `generate_moves`: A function that generates a `BitBoard` with all possible moves for the piece from a given `Square`.
/// This function is called for each square on the board.
///
/// # Returns
/// An array of `BitBoard` of size `SQUARES_AMOUNT`, where each entry corresponds to the
/// possible moves for the square at the same index.
const fn calculate_potential_moves_cache(piece_type: PieceType) -> [BitBoard; SQUARES_AMOUNT] {
let mut potential_moves = [BitBoard::new(); SQUARES_AMOUNT];

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

// and generate the moves for that square
potential_moves[square_index] = match piece_type {
PieceType::Knight => generate_knight_moves(square),
PieceType::King => generate_king_moves(square),
_ => panic!("Invalid piece type"),
};

square_index += 1;
}

potential_moves
}

/// Generates a `BitBoard` with all possible moves for a king piece from a given `Square`.
///
/// # Parameters
/// - `square`: A `Square` representing the current position of the king piece.
///
/// # Returns
/// A `BitBoard` containing all valid surrounding squares a king can move to.
const fn generate_king_moves(square: Square) -> BitBoard {
let mut bitboard = BitBoard::new();

// since this fn is const, we can't use a loop
// instead we use a while loop
// to iterate over the surrounding files
let mut file_offset = -1;
while file_offset <= 1 {
// and ranks
let mut rank_offset = -1;
while rank_offset <= 1 {
// skip the current square
if file_offset == 0 && rank_offset == 0 {
rank_offset += 1;
continue;
}
// create the relevant square
let current_square =
Square::new(square.file() + file_offset, square.rank() + rank_offset);

// and add it if it's valid
if current_square.is_valid() {
bitboard.fill_square(current_square);
}

rank_offset += 1;
}

file_offset += 1;
}
bitboard
}

/// Same as above but for the knight.
const fn generate_knight_moves(square: Square) -> BitBoard {
let mut bitboard = BitBoard::new();

// The offsets for the knight moves. Starting in the top left corner.
// `_ _ _ _ _ _ _ _`
// `_ _ 2 _ 3 _ _ _`
// `_ 1 _ _ _ 4 _ _`
// `_ _ _ K _ _ _ _`
// `_ 8 _ _ _ 5 _ _`
// `_ _ 7 _ 6 _ _ _`
// `_ _ _ _ _ _ _ _`
// `_ _ _ _ _ _ _ _`
let offset_x = [-2, -1, 1, 2, 2, 1, -1, -2];
let offset_y = [1, 2, 2, 1, -1, -2, -2, -1];

let mut index = 0;
while index < 8 {
// Calculate the current square.
let x_pos = square.file() + offset_x[index];
let y_pos = square.rank() + offset_y[index];
let current_square = Square::new(x_pos, y_pos);

// If it is valid, add it to the bitboard.
if current_square.is_valid() {
bitboard.fill_square(current_square);
}

index += 1;
}

bitboard
}
2 changes: 1 addition & 1 deletion src/backend/move_gen/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pub mod check_decider;
pub mod compile_time;
pub mod move_gen;
mod move_gen_king;
mod move_gen_non_sliders;
50 changes: 42 additions & 8 deletions src/backend/move_gen/move_gen.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use crate::backend::moove::Moove;
use crate::backend::move_gen::move_gen_king::KING_MOVES;
use crate::backend::move_gen::move_gen_non_sliders::get_moves_for_piece;
use crate::backend::piece::Piece;
use crate::backend::piece::PieceType::King;
use crate::backend::move_gen::compile_time::move_cache_non_sliders::get_moves_cache_for_piece;
use crate::backend::move_gen::move_gen_non_sliders::get_moves_for_non_slider_piece;
use crate::backend::piece::{Piece, PieceColor, PieceType};
use crate::backend::state::bitboard::BitBoard;
use crate::backend::state::bitboard_manager::BitBoardManager;
use crate::backend::state::game_state::GameState;
use crate::constants::SQUARES_AMOUNT;

/// Generates and returns all the valid moves for the current player's pieces
/// based on the provided game state. This is the entry point for the move generation.
Expand All @@ -22,10 +24,42 @@ pub fn get_moves(game_state: &GameState) -> Vec<Moove> {
// and then call them from here
// each of them should iterate over their relevant bitboard
let bitboard_manager = game_state.bit_board_manager();
// Bitboard containing all pieces of the active color. These block moves.
let friendly_pieces_bitboard = bitboard_manager.get_all_pieces_off(game_state.active_color());

// For now only the king is implemented.
let king = Piece::new(King, game_state.active_color());
let king_bitboard = bitboard_manager.get_bitboard(king);
get_moves_for_piece(KING_MOVES, *king_bitboard, friendly_pieces_bitboard)
let mut all_pseudo_legal_moves = Vec::new();
let active_color = game_state.active_color();

// Iterate over all piece types. This might need adjustment for sliding pieces.
for piece_type in PieceType::get_all_types() {
// Get the moves cache for the current piece type.
let moves_cache = get_moves_cache_for_piece(piece_type);
// Get all moves for the current piece type.
get_moves_for_piece(
&mut all_pseudo_legal_moves,
piece_type,
active_color,
moves_cache,
bitboard_manager,
friendly_pieces_bitboard,
);
}

all_pseudo_legal_moves
}

/// Gets all pseudo legal moves for one piece.
fn get_moves_for_piece(
all_pseudo_legal_moves: &mut Vec<Moove>,
piece_type: PieceType,
active_color: PieceColor,
moves_cache: [BitBoard; SQUARES_AMOUNT],
bitboard_manager: &BitBoardManager,
friendly_pieces_bitboard: BitBoard,
) {
let piece = Piece::new(piece_type, active_color);
let piece_bitboard = bitboard_manager.get_bitboard(piece);
let mut moves =
get_moves_for_non_slider_piece(moves_cache, *piece_bitboard, friendly_pieces_bitboard);
all_pseudo_legal_moves.append(&mut moves);
}
83 changes: 0 additions & 83 deletions src/backend/move_gen/move_gen_king.rs

This file was deleted.

16 changes: 11 additions & 5 deletions src/backend/move_gen/move_gen_non_sliders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ use crate::constants::SQUARES_AMOUNT;
///
/// # Returns
/// A `Vec<Moove>` containing all the possible moves for the given piece(s).
pub fn get_moves_for_piece(
pub fn get_moves_for_non_slider_piece(
moves_cache: [BitBoard; SQUARES_AMOUNT],
piece_bitboard: BitBoard,
friendly_pieces_bitboard: BitBoard,
) -> Vec<Moove> {
// PERF: Instead of creating a new vector for each piece, we could reuse the same vector and append to it.
let mut moves: Vec<Moove> = Vec::new();

// Example: We are doing this for all knights.
Expand All @@ -29,9 +30,12 @@ pub fn get_moves_for_piece(
let squares_with_piece = piece_bitboard.get_all_true_squares();
// We then iterate over all these squares...
for square in squares_with_piece.iter() {
// ... get the potential moves for the piece on that square...
// SLIDER: (This only works this easily for non-sliders)
let potential_moves_bitboard = moves_cache[square.square_to_index()];
//... and get all the moves for the piece on that square.
let mut moves_for_square =
get_moves_for_square(moves_cache, *square, friendly_pieces_bitboard);
get_moves_for_square(potential_moves_bitboard, *square, friendly_pieces_bitboard);
// Lastly, we append them :)
moves.append(moves_for_square.as_mut());
}
Expand All @@ -40,6 +44,7 @@ pub fn get_moves_for_piece(
}

/// Computes all possible moves for a given square and friendly piece positions.
/// SLIDER: Should also work for sliders.
///
/// # Parameters
///
Expand All @@ -55,11 +60,13 @@ pub fn get_moves_for_piece(
/// A `Vec` of `Moove` structs that represent all legal moves
/// the piece on the provided square can make.
fn get_moves_for_square(
moves_cache: [BitBoard; SQUARES_AMOUNT],
potential_moves_bitboard: BitBoard,
square: Square,
friendly_pieces_bitboard: BitBoard,
) -> Vec<Moove> {
// This is a bitboard with a 1 at every square the piece can move to if nothing is blocking it.
// SLIDER: I think the following code should also work for sliders.

// `potential_moves_bitboard` is a BitBoard with a 1 at every square the piece can move to if nothing is blocking it.
// For a king at A1 it would look like this:
// _ _ _ _ _ _ _ _
// _ _ _ _ _ _ _ _
Expand All @@ -69,7 +76,6 @@ fn get_moves_for_square(
// _ _ _ _ _ _ _ _
// X X _ _ _ _ _ _
// _ X _ _ _ _ _ _
let potential_moves_bitboard = moves_cache[square.square_to_index() as usize];

// The friendly pieces bitboard might look like this if we have the king on A1 and a pawn on A2 and B2:
// _ _ _ _ _ _ _ _
Expand Down
Loading
Loading