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
22 changes: 9 additions & 13 deletions src/backend/game_state/fen_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,20 +267,16 @@ pub fn moove_from_uci_notation(uci_notation: &str) -> Moove {
let to = square_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),
if let Some(char) = promotion_char {
let promotion_type = match char {
'r' => (Rook),
'n' => (Knight),
'b' => (Bishop),
'q' => (Queen),
_ => panic!("Invalid promotion type {:?}", uci_notation),
},
};
return Moove::new_promotion(from, to, promotion_type)
};

Moove {
from,
to,
promotion_type,
}
Moove::new(from, to)
}
22 changes: 11 additions & 11 deletions src/backend/game_state/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,10 @@ impl State {
let mut next_ir_data = IrreversibleData::new_from_previous_state(&self.irreversible_data);

// Get the type of moved piece.
let moved_piece = self.bb_manager.get_piece_at_square(moove.from).unwrap();
let moved_piece = self.bb_manager.get_piece_at_square(moove.get_from()).unwrap();

// Usually the square something was captured on (if something was captured at all) is the square we moved to...
let mut capture_square = moove.to;
let mut capture_square = moove.get_to();
if moved_piece == Pawn {
// ... unless this is an en passant capture, we then need to update the capture square.
next_state.make_move_ep_capture(moove, &mut capture_square);
Expand All @@ -91,26 +91,26 @@ impl State {
let mut moved_piece_bb = next_state.bb_manager.get_piece_bb_mut(moved_piece);

// Clear the square that the piece was moved from.
moved_piece_bb.clear_square(moove.from);
moved_piece_bb.clear_square(moove.get_from());

// Update the moved piece bb if it was a pawn promotion
match moove.promotion_type {
match moove.get_promotion_type() {
None => {}
Some(promotion_type) => {
moved_piece_bb = next_state.bb_manager.get_piece_bb_mut(promotion_type);
}
}
// Fill the square it moved to.
moved_piece_bb.fill_square(moove.to);
moved_piece_bb.fill_square(moove.get_to());

next_state
.bb_manager
.get_all_pieces_bb_off_mut(self.active_color)
.fill_square(moove.to);
.fill_square(moove.get_to());
next_state
.bb_manager
.get_all_pieces_bb_off_mut(self.active_color)
.clear_square(moove.from);
.clear_square(moove.get_from());

// Some special king handling
if moved_piece == King {
Expand All @@ -120,7 +120,7 @@ impl State {
next_state.make_move_castling_rights_on_rook_move_or_capture(
&mut next_ir_data,
moved_piece,
moove.from,
moove.get_from(),
self.active_color,
);

Expand All @@ -136,10 +136,10 @@ impl State {
// if an en passant square exists
if let Some(ep_square) = ep_square
// and if we moved to the ep_square
&& ep_square == moove.to
&& ep_square == moove.get_to()
{
// update the captured square to the ep_square - offset
*capture_square = back_by_one(moove.to, self.active_color);
*capture_square = back_by_one(moove.get_to(), self.active_color);
}
}

Expand Down Expand Up @@ -178,7 +178,7 @@ impl State {
) {
if moove.is_double_pawn_push() {
// the pawn starting square and one forward
let ep_square = back_by_one(moove.to, self.active_color);
let ep_square = back_by_one(moove.get_to(), self.active_color);

irreversible_data.en_passant_square = Some(ep_square);
}
Expand Down
3 changes: 1 addition & 2 deletions src/backend/movegen/move_gen_pawn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,7 @@ fn pawn_bb_to_moves_promotion(
let rank = get_rank(square);
let offset_square = square_from_rank_and_file(rank + rank_offset, file + file_offset);
for piece_type in PROMOTABLE_PIECES {
let mut moove = Moove::new(offset_square, square);
moove.promotion_type = Some(piece_type);
let moove = Moove::new_promotion(offset_square, square, piece_type);
moves.push(moove);
}
}
Expand Down
100 changes: 74 additions & 26 deletions src/backend/types/moove.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::cmp::Ordering;
use std::fmt::{Display, Formatter};
use crate::backend::types::piece::Piece;
use crate::backend::types::piece::{Piece, PROMOTABLE_PIECES};
use crate::backend::types::square::{get_file, square_to_string, Square};

#[derive(Copy, Clone)]
Expand All @@ -18,46 +19,63 @@ impl CastleType {
/// 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.
///
/// PERF: This could be squeezed into a bitfield like, for example, stockfish does:
/// Around line 370: https://github.com/official-stockfish/Stockfish/blob/master/src/types.h
/// I have not done this yet for two reasons:
/// 1. I'm not sure, without any benchmarks if it gains any performance.
/// 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.
///
/// Alternatively, each Move could store a bitboard, with one bit set where we currently are and one where we are going to.
/// To "make" this move we then would only need to xor it with the bitboard for the piece.
#[derive(Copy, Clone, Debug, Ord, Eq, PartialEq, PartialOrd)]
/// Based on: https://github.com/official-stockfish/Stockfish/blob/master/src/types.h
/// It's stored in 16 bits
/// The first six are for the from index, the next six for the to index, leaving us with 4 bits remaining.
/// Two of those are used to encode the type of promotion piece. Either Rook, Knight, Bishop, or Queen
/// The next stores whether promotion has occurred
/// One bit and thus three possible data points free!
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct Moove {
pub from: Square,
pub to: Square,
pub promotion_type: Option<Piece>,
bitfield: u16
}

impl Moove {
/// Creates a new `Move` instance with 'promotion_type' set to 0.
pub const fn new(from: Square, to: Square) -> Moove {
Moove {
from,
to,
promotion_type: Option::None,
let mut result = from as u16 | ((to as u16) << 6);
result |= 0;
Moove { bitfield: result }
}

pub fn new_promotion(from: Square, to: Square, promotion_type: Piece) -> Moove {
Moove { bitfield: from as u16 | ((to as u16) << 6) | (promotion_type as u16) << 12 | 1 << 14 }
}

pub fn get_from(&self) -> Square {
let mask = 0b0000_0000_0011_1111u16;
(self.bitfield & mask) as Square
}

pub fn get_to(&self) -> Square {
let mask = 0b0000_1111_1100_0000u16;
((self.bitfield & mask) >> 6) as Square
}

pub fn get_promotion_type(&self) -> Option<Piece> {
let promo_mask = 0b0100_0000_0000_0000u16;
if (self.bitfield & promo_mask) == 0 {
return None;
}

let type_mask = 0b0011_0000_0000_0000u16;
let piece_index = (self.bitfield & type_mask) >> 12;

Some(PROMOTABLE_PIECES[piece_index as usize])
}

/// 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.abs_diff(self.to) == 16
self.get_from().abs_diff(self.get_to()) == 16
}

/// This assumes that the moved piece is a king and only checks if the file changed by 2.
/// TODO: Not sure if this is bug free but i think so lol
pub fn is_castle(&self) -> bool {
self.from.abs_diff(self.to) == 2
self.get_from().abs_diff(self.get_to()) == 2
}

pub fn get_castle_type(&self) -> CastleType {
if get_file(self.to) == 6 {
if get_file(self.get_to()) == 6 {
CastleType::Short
} else {
CastleType::Long
Expand All @@ -70,9 +88,9 @@ impl Display for Moove {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut result = String::new();

result.push_str(&square_to_string(self.from));
result.push_str(&square_to_string(self.to));
result.push_str(match self.promotion_type {
result.push_str(&square_to_string(self.get_from()));
result.push_str(&square_to_string(self.get_to()));
result.push_str(match self.get_promotion_type() {
None => "",
Some(promotion_type) => match promotion_type {
Piece::Rook => "r",
Expand All @@ -86,3 +104,33 @@ impl Display for Moove {
write!(f, "{}", result)
}
}

/// Implements ordering. Needed to sort them when comparing with perftree output.
/// This should only be called during debugging, not for performance-critical operations.
impl PartialOrd for Moove {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
}

impl Ord for Moove {
fn cmp(&self, other: &Self) -> Ordering {
let own_from = self.get_from();
let other_from = other.get_from();

let own_to = self.get_to();
let other_to = self.get_to();

if own_from > other_from {
return Ordering::Greater;
} else if own_from < other_from {
return Ordering::Less;
}

if own_to > other_to {
return Ordering::Greater;
} else if own_to < other_to {
return Ordering::Less;
}
Ordering::Equal
}
}

8 changes: 6 additions & 2 deletions src/backend/types/piece.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
/// Represents the different pieces.
///
/// Dear god, this is a bit hacky.
/// Do not change the order of this to differ from the PROMOTABLE_PIECES list.
#[derive(Copy, Clone, Debug, Ord, Eq, PartialEq, PartialOrd)]
pub enum Piece {
Pawn,
Rook,
Knight,
Bishop,
Queen,
King,
Pawn,
}

/// The order of this has to match with the Piece enum.
pub const ALL_PIECES: [Piece; 6] = [
Piece::Pawn,
Piece::Rook,
Piece::Knight,
Piece::Bishop,
Piece::Queen,
Piece::King,
Piece::Pawn,
];

pub const PROMOTABLE_PIECES: [Piece; 4] = [Piece::Rook, Piece::Knight, Piece::Bishop, Piece::Queen];
Expand Down
Loading