Replace uses of types in r#move with types from the moves package types
This commit is contained in:
parent
aaad991899
commit
d668091d0d
16 changed files with 273 additions and 280 deletions
|
@ -1,7 +1,8 @@
|
||||||
// Eryn Wells <eryn@erynwells.me>
|
// Eryn Wells <eryn@erynwells.me>
|
||||||
|
|
||||||
use crate::{r#move::Castle, Position, PositionBuilder};
|
use crate::{Position, PositionBuilder};
|
||||||
use chessfriend_core::{piece, Color, File, Piece, PlacedPiece, Rank, Square};
|
use chessfriend_core::{piece, Color, File, Piece, PlacedPiece, Rank, Square};
|
||||||
|
use chessfriend_moves::Castle;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
macro_rules! fen {
|
macro_rules! fen {
|
||||||
|
|
|
@ -4,7 +4,6 @@ pub mod fen;
|
||||||
|
|
||||||
mod check;
|
mod check;
|
||||||
mod display;
|
mod display;
|
||||||
mod r#move;
|
|
||||||
mod move_generator;
|
mod move_generator;
|
||||||
mod position;
|
mod position;
|
||||||
mod sight;
|
mod sight;
|
||||||
|
@ -12,8 +11,8 @@ mod sight;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod macros;
|
mod macros;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod testing;
|
mod testing;
|
||||||
|
|
||||||
pub use position::{MoveBuilder as MakeMoveBuilder, Position, PositionBuilder};
|
pub use position::{MakeMoveError, MoveBuilder as MakeMoveBuilder, Position, PositionBuilder};
|
||||||
pub use r#move::{Castle, MakeMoveError, Move, MoveBuilder};
|
|
||||||
|
|
|
@ -7,6 +7,8 @@ mod move_set;
|
||||||
mod pawn;
|
mod pawn;
|
||||||
mod queen;
|
mod queen;
|
||||||
mod rook;
|
mod rook;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
pub(crate) use move_set::MoveSet;
|
pub(crate) use move_set::MoveSet;
|
||||||
|
@ -17,9 +19,10 @@ use self::{
|
||||||
queen::ClassicalMoveGenerator as QueenMoveGenerator,
|
queen::ClassicalMoveGenerator as QueenMoveGenerator,
|
||||||
rook::ClassicalMoveGenerator as RookMoveGenerator,
|
rook::ClassicalMoveGenerator as RookMoveGenerator,
|
||||||
};
|
};
|
||||||
use crate::{Move, Position};
|
use crate::Position;
|
||||||
use chessfriend_bitboard::BitBoard;
|
use chessfriend_bitboard::BitBoard;
|
||||||
use chessfriend_core::{Color, Piece, PlacedPiece, Shape, Square};
|
use chessfriend_core::{Color, Piece, PlacedPiece, Shape, Square};
|
||||||
|
use chessfriend_moves::Move;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
trait MoveGenerator {
|
trait MoveGenerator {
|
||||||
|
@ -66,7 +69,7 @@ macro_rules! move_generator_declaration {
|
||||||
};
|
};
|
||||||
($name:ident, getters) => {
|
($name:ident, getters) => {
|
||||||
impl $name {
|
impl $name {
|
||||||
pub(super) fn iter(&self) -> impl Iterator<Item = $crate::Move> + '_ {
|
pub(super) fn iter(&self) -> impl Iterator<Item = chessfriend_moves::Move> + '_ {
|
||||||
self.move_sets.values().flat_map(|set| set.moves())
|
self.move_sets.values().flat_map(|set| set.moves())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,13 +58,14 @@ impl MoveGeneratorInternal for KingMoveGenerator {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{assert_move_list, position, test_position, Move, MoveBuilder, PositionBuilder};
|
use crate::{assert_move_list, position, test_position, testing::*, PositionBuilder};
|
||||||
use chessfriend_bitboard::bitboard;
|
use chessfriend_bitboard::bitboard;
|
||||||
use chessfriend_core::{piece, Color, Square};
|
use chessfriend_core::{piece, Color, Square};
|
||||||
|
use chessfriend_moves::{Builder as MoveBuilder, Castle, Move};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn one_king() {
|
fn one_king() -> TestResult {
|
||||||
let pos = position![White King on E4];
|
let pos = position![White King on E4];
|
||||||
|
|
||||||
let generator = KingMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
|
let generator = KingMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
|
||||||
|
@ -74,24 +75,27 @@ mod tests {
|
||||||
bitboard![E5, F5, F4, F3, E3, D3, D4, D5]
|
bitboard![E5, F5, F4, F3, E3, D3, D4, D5]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let builder = MoveBuilder::push(&piece!(White King on E4));
|
||||||
let expected_moves: HashSet<Move> = HashSet::from_iter([
|
let expected_moves: HashSet<Move> = HashSet::from_iter([
|
||||||
MoveBuilder::new(piece!(White King), Square::E4, Square::D5).build(),
|
builder.clone().to(Square::D5).build()?,
|
||||||
MoveBuilder::new(piece!(White King), Square::E4, Square::E5).build(),
|
builder.clone().to(Square::E5).build()?,
|
||||||
MoveBuilder::new(piece!(White King), Square::E4, Square::F5).build(),
|
builder.clone().to(Square::F5).build()?,
|
||||||
MoveBuilder::new(piece!(White King), Square::E4, Square::F4).build(),
|
builder.clone().to(Square::F4).build()?,
|
||||||
MoveBuilder::new(piece!(White King), Square::E4, Square::F3).build(),
|
builder.clone().to(Square::F3).build()?,
|
||||||
MoveBuilder::new(piece!(White King), Square::E4, Square::E3).build(),
|
builder.clone().to(Square::E3).build()?,
|
||||||
MoveBuilder::new(piece!(White King), Square::E4, Square::D3).build(),
|
builder.clone().to(Square::D3).build()?,
|
||||||
MoveBuilder::new(piece!(White King), Square::E4, Square::D4).build(),
|
builder.clone().to(Square::D4).build()?,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let generated_moves: HashSet<Move> = generator.iter().collect();
|
let generated_moves: HashSet<Move> = generator.iter().collect();
|
||||||
|
|
||||||
assert_move_list!(generated_moves, expected_moves, pos);
|
assert_move_list!(generated_moves, expected_moves, pos);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn one_king_corner() {
|
fn one_king_corner() -> TestResult {
|
||||||
let pos = position![White King on A1];
|
let pos = position![White King on A1];
|
||||||
|
|
||||||
let generator = KingMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
|
let generator = KingMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
|
||||||
|
@ -104,13 +108,14 @@ mod tests {
|
||||||
"Generated:\n{generated_bitboard}\nExpected:\n{expected_bitboard}"
|
"Generated:\n{generated_bitboard}\nExpected:\n{expected_bitboard}"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let builder = MoveBuilder::push(&piece!(White King on A1));
|
||||||
let expected_moves = [
|
let expected_moves = [
|
||||||
MoveBuilder::new(piece!(White King), Square::A1, Square::A2).build(),
|
builder.clone().to(Square::A2).build()?,
|
||||||
MoveBuilder::new(piece!(White King), Square::A1, Square::B1).build(),
|
builder.clone().to(Square::B1).build()?,
|
||||||
MoveBuilder::new(piece!(White King), Square::A1, Square::B2).build(),
|
builder.clone().to(Square::B2).build()?,
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut generated_moves: HashSet<Move> = generator.iter().collect();
|
let mut generated_moves: HashSet<_> = generator.iter().collect();
|
||||||
|
|
||||||
for ex_move in expected_moves {
|
for ex_move in expected_moves {
|
||||||
assert!(
|
assert!(
|
||||||
|
@ -125,6 +130,8 @@ mod tests {
|
||||||
"Moves unexpectedly present: {:#?}",
|
"Moves unexpectedly present: {:#?}",
|
||||||
generated_moves
|
generated_moves
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -161,16 +168,8 @@ mod tests {
|
||||||
let generated_moves: HashSet<Move> = generator.iter().collect();
|
let generated_moves: HashSet<Move> = generator.iter().collect();
|
||||||
|
|
||||||
let king = piece!(White King);
|
let king = piece!(White King);
|
||||||
assert!(generated_moves.contains(
|
assert!(generated_moves.contains(&MoveBuilder::castling(Castle::KingSide).build()));
|
||||||
&MoveBuilder::new(king, Square::E1, Square::G1)
|
assert!(generated_moves.contains(&MoveBuilder::castling(Castle::QueenSide).build()));
|
||||||
.castle(Castle::KingSide)
|
|
||||||
.build()
|
|
||||||
));
|
|
||||||
assert!(generated_moves.contains(
|
|
||||||
&MoveBuilder::new(king, Square::E1, Square::C1)
|
|
||||||
.castle(Castle::QueenSide)
|
|
||||||
.build()
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -189,16 +188,8 @@ mod tests {
|
||||||
let generated_moves: HashSet<Move> = generator.iter().collect();
|
let generated_moves: HashSet<Move> = generator.iter().collect();
|
||||||
|
|
||||||
let king = piece!(White King);
|
let king = piece!(White King);
|
||||||
assert!(generated_moves.contains(
|
assert!(generated_moves.contains(&MoveBuilder::castling(Castle::KingSide).build()));
|
||||||
&MoveBuilder::new(king, Square::E1, Square::G1)
|
assert!(!generated_moves.contains(&MoveBuilder::castling(Castle::QueenSide).build()));
|
||||||
.castle(Castle::KingSide)
|
|
||||||
.build()
|
|
||||||
));
|
|
||||||
assert!(!generated_moves.contains(
|
|
||||||
&MoveBuilder::new(king, Square::E1, Square::C1)
|
|
||||||
.castle(Castle::QueenSide)
|
|
||||||
.build()
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -217,15 +208,7 @@ mod tests {
|
||||||
let generated_moves: HashSet<Move> = generator.iter().collect();
|
let generated_moves: HashSet<Move> = generator.iter().collect();
|
||||||
|
|
||||||
let king = piece!(White King);
|
let king = piece!(White King);
|
||||||
assert!(!generated_moves.contains(
|
assert!(!generated_moves.contains(&MoveBuilder::castling(Castle::KingSide).build()));
|
||||||
&MoveBuilder::new(king, Square::E1, Square::G1)
|
assert!(generated_moves.contains(&MoveBuilder::castling(Castle::QueenSide).build()));
|
||||||
.castle(Castle::KingSide)
|
|
||||||
.build()
|
|
||||||
));
|
|
||||||
assert!(generated_moves.contains(
|
|
||||||
&MoveBuilder::new(king, Square::E1, Square::C1)
|
|
||||||
.castle(Castle::QueenSide)
|
|
||||||
.build()
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,54 +34,35 @@ impl MoveGeneratorInternal for KnightMoveGenerator {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{position, Move, MoveBuilder};
|
use crate::{assert_move_list, position, testing::*};
|
||||||
use chessfriend_core::{piece, Color, Square};
|
use chessfriend_core::{piece, Color, Square};
|
||||||
|
use chessfriend_moves::Builder as MoveBuilder;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn one_knight() {
|
fn one_knight() -> TestResult {
|
||||||
let pos = position![
|
let pos = position![
|
||||||
White Knight on E4,
|
White Knight on E4,
|
||||||
];
|
];
|
||||||
|
|
||||||
let generator =
|
let generator =
|
||||||
KnightMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
|
KnightMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
|
||||||
|
let generated_moves: HashSet<_> = generator.iter().collect();
|
||||||
|
|
||||||
/*
|
let piece = piece!(White Knight on E4);
|
||||||
let bb = generator.bitboard();
|
let expected_moves = HashSet::from_iter([
|
||||||
assert_eq!(
|
MoveBuilder::push(&piece).to(Square::C3).build()?,
|
||||||
bb,
|
MoveBuilder::push(&piece).to(Square::D2).build()?,
|
||||||
BitBoard::new(
|
MoveBuilder::push(&piece).to(Square::F2).build()?,
|
||||||
0b00000000_00000000_00000000_00111000_00101000_00111000_00000000_00000000
|
MoveBuilder::push(&piece).to(Square::G3).build()?,
|
||||||
)
|
MoveBuilder::push(&piece).to(Square::C5).build()?,
|
||||||
);
|
MoveBuilder::push(&piece).to(Square::D6).build()?,
|
||||||
*/
|
MoveBuilder::push(&piece).to(Square::G5).build()?,
|
||||||
|
MoveBuilder::push(&piece).to(Square::F6).build()?,
|
||||||
|
]);
|
||||||
|
|
||||||
let expected_moves = [
|
assert_move_list!(generated_moves, expected_moves, pos);
|
||||||
MoveBuilder::new(piece!(White Knight), Square::E4, Square::C3).build(),
|
|
||||||
MoveBuilder::new(piece!(White Knight), Square::E4, Square::D2).build(),
|
|
||||||
MoveBuilder::new(piece!(White Knight), Square::E4, Square::F2).build(),
|
|
||||||
MoveBuilder::new(piece!(White Knight), Square::E4, Square::G3).build(),
|
|
||||||
MoveBuilder::new(piece!(White Knight), Square::E4, Square::C5).build(),
|
|
||||||
MoveBuilder::new(piece!(White Knight), Square::E4, Square::D6).build(),
|
|
||||||
MoveBuilder::new(piece!(White Knight), Square::E4, Square::G5).build(),
|
|
||||||
MoveBuilder::new(piece!(White Knight), Square::E4, Square::F6).build(),
|
|
||||||
];
|
|
||||||
|
|
||||||
let mut generated_moves: HashSet<Move> = generator.iter().collect();
|
Ok(())
|
||||||
|
|
||||||
for ex_move in expected_moves {
|
|
||||||
assert!(
|
|
||||||
generated_moves.remove(&ex_move),
|
|
||||||
"{:#?} was not generated",
|
|
||||||
&ex_move
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
assert!(
|
|
||||||
generated_moves.is_empty(),
|
|
||||||
"Moves unexpectedly present: {:#?}",
|
|
||||||
generated_moves
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Eryn Wells <eryn@erynwells.me>
|
// Eryn Wells <eryn@erynwells.me>
|
||||||
|
|
||||||
use chessfriend_bitboard::BitBoard;
|
use chessfriend_bitboard::BitBoard;
|
||||||
use chessfriend_core::{Color, Piece, PlacedPiece, Shape, Square};
|
use chessfriend_core::{PlacedPiece, Square};
|
||||||
use chessfriend_moves::{Builder as MoveBuilder, Castle, Move};
|
use chessfriend_moves::{Builder as MoveBuilder, Castle, Move};
|
||||||
|
|
||||||
/// A set of bitboards defining the moves for a single piece on the board.
|
/// A set of bitboards defining the moves for a single piece on the board.
|
||||||
|
@ -65,57 +65,34 @@ impl MoveSet {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn moves(&self) -> impl Iterator<Item = Move> + '_ {
|
pub(crate) fn moves(&self) -> impl Iterator<Item = Move> + '_ {
|
||||||
let piece = self.piece.piece();
|
let piece = &self.piece;
|
||||||
let from_square = self.piece.square();
|
|
||||||
|
|
||||||
self.bitboards
|
self.bitboards
|
||||||
.quiet
|
.quiet
|
||||||
.occupied_squares()
|
.occupied_squares()
|
||||||
.map(move |to_square| MoveBuilder::new(*piece, from_square, to_square).build())
|
.filter_map(|to_square| MoveBuilder::push(&self.piece).to(to_square).build().ok())
|
||||||
.chain(
|
.chain(
|
||||||
self.bitboards
|
self.bitboards
|
||||||
.captures
|
.captures
|
||||||
.occupied_squares()
|
.occupied_squares()
|
||||||
.map(move |to_square| {
|
.filter_map(|to_square| MoveBuilder::push(piece).to(to_square).build().ok()),
|
||||||
MoveBuilder::new(*piece, from_square, to_square)
|
|
||||||
.capturing(PlacedPiece::new(
|
|
||||||
Piece::new(Color::White, Shape::Pawn),
|
|
||||||
to_square,
|
|
||||||
))
|
|
||||||
.build()
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
.chain(
|
.chain(
|
||||||
if (self.special & 0b1) != 0 {
|
if (self.special & 0b1) != 0 {
|
||||||
Some(())
|
let mv = MoveBuilder::castling(Castle::KingSide).build();
|
||||||
|
Some(mv)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
.map(|()| {
|
.into_iter(),
|
||||||
MoveBuilder::new(
|
|
||||||
*piece,
|
|
||||||
from_square,
|
|
||||||
Castle::KingSide.target_squares(piece.color()).king,
|
|
||||||
)
|
|
||||||
.castle(Castle::KingSide)
|
|
||||||
.build()
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
.chain(
|
.chain(
|
||||||
if (self.special & 0b10) != 0 {
|
if (self.special & 0b10) != 0 {
|
||||||
Some(())
|
Some(MoveBuilder::castling(Castle::QueenSide).build())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
.map(|()| {
|
.into_iter(),
|
||||||
MoveBuilder::new(
|
|
||||||
*piece,
|
|
||||||
from_square,
|
|
||||||
Castle::QueenSide.target_squares(piece.color()).king,
|
|
||||||
)
|
|
||||||
.castle(Castle::QueenSide)
|
|
||||||
.build()
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
// Eryn Wells <eryn@erynwells.me>
|
// Eryn Wells <eryn@erynwells.me>
|
||||||
|
|
||||||
use super::{move_generator_declaration, MoveGeneratorInternal, MoveSet};
|
use super::{move_generator_declaration, MoveGeneratorInternal, MoveSet};
|
||||||
use crate::{r#move::Move, MoveBuilder, Position};
|
use crate::Position;
|
||||||
use chessfriend_bitboard::BitBoard;
|
use chessfriend_bitboard::BitBoard;
|
||||||
use chessfriend_core::{Color, PlacedPiece, Rank, Shape, Square};
|
use chessfriend_core::{Color, PlacedPiece, Rank, Shape, Square};
|
||||||
|
use chessfriend_moves::{Builder as MoveBuilder, Move};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -65,7 +66,7 @@ impl PawnMoveGenerator {
|
||||||
push_mask: BitBoard,
|
push_mask: BitBoard,
|
||||||
) -> BTreeMap<Square, MoveSet> {
|
) -> BTreeMap<Square, MoveSet> {
|
||||||
let piece = Self::piece(color);
|
let piece = Self::piece(color);
|
||||||
let mut moves_for_pieces =
|
let moves_for_pieces =
|
||||||
BTreeMap::from_iter(position.bitboard_for_piece(piece).occupied_squares().map(
|
BTreeMap::from_iter(position.bitboard_for_piece(piece).occupied_squares().map(
|
||||||
|square| {
|
|square| {
|
||||||
let piece = PlacedPiece::new(piece, square);
|
let piece = PlacedPiece::new(piece, square);
|
||||||
|
@ -75,10 +76,6 @@ impl PawnMoveGenerator {
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
|
||||||
if position.has_en_passant_square() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
moves_for_pieces
|
moves_for_pieces
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,23 +118,21 @@ impl PawnMoveGenerator {
|
||||||
fn en_passant_attack(position: &Position, piece: &PlacedPiece) -> Option<Move> {
|
fn en_passant_attack(position: &Position, piece: &PlacedPiece) -> Option<Move> {
|
||||||
match position.en_passant() {
|
match position.en_passant() {
|
||||||
Some(en_passant) => {
|
Some(en_passant) => {
|
||||||
let en_passant_bitboard: BitBoard = en_passant.target_square().into();
|
let target_square = en_passant.target_square();
|
||||||
|
|
||||||
|
let en_passant_bitboard: BitBoard = target_square.into();
|
||||||
let capture =
|
let capture =
|
||||||
BitBoard::pawn_attacks(piece.square(), piece.color()) & en_passant_bitboard;
|
BitBoard::pawn_attacks(piece.square(), piece.color()) & en_passant_bitboard;
|
||||||
|
|
||||||
if capture.is_empty() {
|
if capture.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
match position.piece_on_square(en_passant.capture_square()) {
|
match position.piece_on_square(en_passant.capture_square()) {
|
||||||
Some(captured_piece) => Some(
|
Some(_) => Some(
|
||||||
MoveBuilder::new(
|
MoveBuilder::push(piece)
|
||||||
*piece.piece(),
|
.capturing_en_passant_on(target_square)
|
||||||
piece.square(),
|
.build()
|
||||||
en_passant.target_square(),
|
.ok()?,
|
||||||
)
|
|
||||||
.capturing_en_passant(captured_piece)
|
|
||||||
.build(),
|
|
||||||
),
|
),
|
||||||
None => None,
|
None => None,
|
||||||
}
|
}
|
||||||
|
@ -150,46 +145,48 @@ impl PawnMoveGenerator {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{assert_move_list, position::DiagramFormatter, test_position, Move, MoveBuilder};
|
use crate::{assert_move_list, position::DiagramFormatter, test_position, testing::*};
|
||||||
use chessfriend_core::{piece, Color, Piece, Square};
|
use chessfriend_core::{piece, Color, Square};
|
||||||
|
use chessfriend_moves::{Builder as MoveBuilder, Move};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn one_2square_push() {
|
fn one_double_push() -> TestResult {
|
||||||
let pos = test_position![White Pawn on E2];
|
let pos = test_position![White Pawn on E2];
|
||||||
|
|
||||||
let generator = PawnMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
|
let generator = PawnMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
|
||||||
|
|
||||||
|
let pawn = piece!(White Pawn on E2);
|
||||||
let expected_moves = HashSet::from_iter([
|
let expected_moves = HashSet::from_iter([
|
||||||
MoveBuilder::new(piece!(White Pawn), Square::E2, Square::E3).build(),
|
MoveBuilder::push(&pawn).to(Square::E3).build()?,
|
||||||
MoveBuilder::new(piece!(White Pawn), Square::E2, Square::E4).build(),
|
MoveBuilder::double_push(pawn.square().file(), pawn.color()).build()?,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let generated_moves: HashSet<Move> = generator.iter().collect();
|
let generated_moves: HashSet<_> = generator.iter().collect();
|
||||||
|
|
||||||
assert_eq!(generated_moves, expected_moves);
|
assert_eq!(generated_moves, expected_moves);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn one_1square_push() {
|
fn one_single_push() -> TestResult {
|
||||||
let pos = test_position![White Pawn on E3];
|
let pos = test_position![White Pawn on E3];
|
||||||
|
|
||||||
let generator = PawnMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
|
let generator = PawnMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
|
||||||
|
let generated_moves: HashSet<_> = generator.iter().collect();
|
||||||
|
|
||||||
let expected_moves = HashSet::from_iter([MoveBuilder::new(
|
let expected_moves = HashSet::from_iter([MoveBuilder::push(&piece!(White Pawn on E3))
|
||||||
Piece::pawn(Color::White),
|
.to(Square::E4)
|
||||||
Square::E3,
|
.build()?]);
|
||||||
Square::E4,
|
|
||||||
)
|
|
||||||
.build()]);
|
|
||||||
|
|
||||||
let generated_moves: HashSet<Move> = generator.iter().collect();
|
|
||||||
|
|
||||||
assert_move_list!(generated_moves, expected_moves, pos);
|
assert_move_list!(generated_moves, expected_moves, pos);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn one_obstructed_2square_push() {
|
fn one_obstructed_2square_push() -> TestResult {
|
||||||
let pos = test_position![
|
let pos = test_position![
|
||||||
White Pawn on E2,
|
White Pawn on E2,
|
||||||
White Knight on E4,
|
White Knight on E4,
|
||||||
|
@ -199,16 +196,15 @@ mod tests {
|
||||||
|
|
||||||
let generator = PawnMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
|
let generator = PawnMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
|
||||||
|
|
||||||
let expected_moves = HashSet::from_iter([MoveBuilder::new(
|
let expected_moves = HashSet::from_iter([MoveBuilder::push(&piece!(White Pawn on E2))
|
||||||
Piece::pawn(Color::White),
|
.to(Square::E3)
|
||||||
Square::E2,
|
.build()?]);
|
||||||
Square::E3,
|
|
||||||
)
|
|
||||||
.build()]);
|
|
||||||
|
|
||||||
let generated_moves: HashSet<Move> = generator.iter().collect();
|
let generated_moves: HashSet<_> = generator.iter().collect();
|
||||||
|
|
||||||
assert_move_list!(generated_moves, expected_moves, pos);
|
assert_move_list!(generated_moves, expected_moves, pos);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -220,14 +216,14 @@ mod tests {
|
||||||
|
|
||||||
let generator = PawnMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
|
let generator = PawnMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
|
||||||
|
|
||||||
let generated_moves: HashSet<Move> = generator.iter().collect();
|
let generated_moves: HashSet<_> = generator.iter().collect();
|
||||||
let expected_moves: HashSet<Move> = HashSet::new();
|
let expected_moves: HashSet<_> = HashSet::new();
|
||||||
|
|
||||||
assert_move_list!(generated_moves, expected_moves, pos);
|
assert_move_list!(generated_moves, expected_moves, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn one_attack() {
|
fn one_attack() -> TestResult {
|
||||||
let pos = test_position![
|
let pos = test_position![
|
||||||
White Pawn on E4,
|
White Pawn on E4,
|
||||||
White Bishop on E5,
|
White Bishop on E5,
|
||||||
|
@ -236,20 +232,19 @@ mod tests {
|
||||||
|
|
||||||
let generator = PawnMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
|
let generator = PawnMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
|
||||||
|
|
||||||
let expected_moves =
|
let expected_moves = HashSet::from_iter([MoveBuilder::push(&piece!(White Pawn on E4))
|
||||||
HashSet::from_iter(
|
.capturing_on(Square::D5)
|
||||||
[MoveBuilder::new(piece!(White Pawn), Square::E4, Square::D5)
|
.build()?]);
|
||||||
.capturing(piece!(Black Knight on D5))
|
|
||||||
.build()],
|
|
||||||
);
|
|
||||||
|
|
||||||
let generated_moves: HashSet<Move> = generator.iter().collect();
|
let generated_moves: HashSet<_> = generator.iter().collect();
|
||||||
|
|
||||||
assert_eq!(generated_moves, expected_moves);
|
assert_move_list!(generated_moves, expected_moves, pos);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn one_double_attack() {
|
fn one_double_attack() -> TestResult {
|
||||||
let pos = test_position![
|
let pos = test_position![
|
||||||
White Pawn on E4,
|
White Pawn on E4,
|
||||||
White Bishop on E5,
|
White Bishop on E5,
|
||||||
|
@ -259,21 +254,44 @@ mod tests {
|
||||||
|
|
||||||
let generator = PawnMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
|
let generator = PawnMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
|
||||||
|
|
||||||
|
let builder = MoveBuilder::push(&piece!(White Pawn on E4));
|
||||||
let expected_moves = HashSet::from_iter([
|
let expected_moves = HashSet::from_iter([
|
||||||
MoveBuilder::new(piece!(White Pawn), Square::E4, Square::D5)
|
builder.clone().capturing_on(Square::D5).build()?,
|
||||||
.capturing(piece!(Black Knight on D5))
|
builder.clone().capturing_on(Square::F5).build()?,
|
||||||
.build(),
|
|
||||||
MoveBuilder::new(piece!(White Pawn), Square::E4, Square::F5)
|
|
||||||
.capturing(piece!(Black Queen on F5))
|
|
||||||
.build(),
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let generated_moves: HashSet<Move> = generator.iter().collect();
|
let generated_moves: HashSet<_> = generator.iter().collect();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
generated_moves, expected_moves,
|
generated_moves, expected_moves,
|
||||||
"generated: {:#?}\nexpected: {:#?}",
|
"generated: {:#?}\nexpected: {:#?}",
|
||||||
generated_moves, expected_moves
|
generated_moves, expected_moves
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn one_en_passant_attack() -> TestResult {
|
||||||
|
let pos = test_position!(Black, [
|
||||||
|
White Pawn on D4,
|
||||||
|
Black Pawn on E4,
|
||||||
|
], D3);
|
||||||
|
|
||||||
|
let generator = PawnMoveGenerator::new(&pos, Color::White, BitBoard::FULL, BitBoard::FULL);
|
||||||
|
let generated_moves: HashSet<Move> = generator.iter().collect();
|
||||||
|
|
||||||
|
let builder = MoveBuilder::push(&piece!(Black Pawn on E4));
|
||||||
|
let expected_moves = HashSet::from_iter([
|
||||||
|
builder
|
||||||
|
.clone()
|
||||||
|
.capturing_en_passant_on(Square::D3)
|
||||||
|
.build()?,
|
||||||
|
builder.clone().to(Square::E3).build()?,
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert_move_list!(generated_moves, expected_moves, pos);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,15 +6,13 @@
|
||||||
//! [1]: https://peterellisjones.com
|
//! [1]: https://peterellisjones.com
|
||||||
//! [2]: https://peterellisjones.com/posts/generating-legal-chess-moves-efficiently/
|
//! [2]: https://peterellisjones.com/posts/generating-legal-chess-moves-efficiently/
|
||||||
|
|
||||||
use crate::{
|
use crate::{assert_move_list, formatted_move_list, test_position, testing::*};
|
||||||
assert_move_list, formatted_move_list, move_generator::Moves, r#move::AlgebraicMoveFormatter,
|
|
||||||
test_position, Move, MoveBuilder,
|
|
||||||
};
|
|
||||||
use chessfriend_core::{piece, Square};
|
use chessfriend_core::{piece, Square};
|
||||||
|
use chessfriend_moves::Builder as MoveBuilder;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn pseudo_legal_move_generation() -> Result<(), String> {
|
fn pseudo_legal_move_generation() -> TestResult {
|
||||||
let pos = test_position!(Black, [
|
let pos = test_position!(Black, [
|
||||||
Black King on E8,
|
Black King on E8,
|
||||||
White King on E1,
|
White King on E1,
|
||||||
|
@ -24,7 +22,7 @@ fn pseudo_legal_move_generation() -> Result<(), String> {
|
||||||
let generated_moves = pos.moves();
|
let generated_moves = pos.moves();
|
||||||
let king_moves = generated_moves
|
let king_moves = generated_moves
|
||||||
.moves_for_piece(&piece!(Black King on E8))
|
.moves_for_piece(&piece!(Black King on E8))
|
||||||
.ok_or("No valid king moves")?;
|
.ok_or(TestError::NoLegalMoves)?;
|
||||||
|
|
||||||
assert!(!king_moves.can_move_to_square(Square::F8));
|
assert!(!king_moves.can_move_to_square(Square::F8));
|
||||||
assert!(!king_moves.can_move_to_square(Square::F7));
|
assert!(!king_moves.can_move_to_square(Square::F7));
|
||||||
|
@ -33,7 +31,7 @@ fn pseudo_legal_move_generation() -> Result<(), String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn gotcha_king_moves_away_from_a_checking_slider() -> Result<(), String> {
|
fn gotcha_king_moves_away_from_a_checking_slider() -> TestResult {
|
||||||
let pos = test_position!(Black, [
|
let pos = test_position!(Black, [
|
||||||
Black King on E7,
|
Black King on E7,
|
||||||
White King on E1,
|
White King on E1,
|
||||||
|
@ -43,7 +41,7 @@ fn gotcha_king_moves_away_from_a_checking_slider() -> Result<(), String> {
|
||||||
let generated_moves = pos.moves();
|
let generated_moves = pos.moves();
|
||||||
let king_moves = generated_moves
|
let king_moves = generated_moves
|
||||||
.moves_for_piece(&piece!(Black King on E7))
|
.moves_for_piece(&piece!(Black King on E7))
|
||||||
.ok_or("No valid king moves")?;
|
.ok_or(TestError::NoLegalMoves)?;
|
||||||
|
|
||||||
assert!(!king_moves.can_move_to_square(Square::E8));
|
assert!(!king_moves.can_move_to_square(Square::E8));
|
||||||
|
|
||||||
|
@ -51,7 +49,7 @@ fn gotcha_king_moves_away_from_a_checking_slider() -> Result<(), String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_evasions_1() {
|
fn check_evasions_1() -> TestResult {
|
||||||
let pos = test_position!(Black, [
|
let pos = test_position!(Black, [
|
||||||
Black King on E8,
|
Black King on E8,
|
||||||
White King on E1,
|
White King on E1,
|
||||||
|
@ -60,11 +58,12 @@ fn check_evasions_1() {
|
||||||
|
|
||||||
let generated_moves = pos.moves();
|
let generated_moves = pos.moves();
|
||||||
|
|
||||||
|
let builder = MoveBuilder::push(&piece!(Black King on E8));
|
||||||
let expected_moves = HashSet::from_iter([
|
let expected_moves = HashSet::from_iter([
|
||||||
MoveBuilder::new(piece!(Black King), Square::E8, Square::D8).build(),
|
builder.clone().to(Square::D8).build()?,
|
||||||
MoveBuilder::new(piece!(Black King), Square::E8, Square::E7).build(),
|
builder.clone().to(Square::E7).build()?,
|
||||||
MoveBuilder::new(piece!(Black King), Square::E8, Square::F7).build(),
|
builder.clone().to(Square::F7).build()?,
|
||||||
MoveBuilder::new(piece!(Black King), Square::E8, Square::F8).build(),
|
builder.clone().to(Square::F8).build()?,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
assert_move_list!(
|
assert_move_list!(
|
||||||
|
@ -72,10 +71,12 @@ fn check_evasions_1() {
|
||||||
expected_moves,
|
expected_moves,
|
||||||
pos
|
pos
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_evasions_double_check() {
|
fn check_evasions_double_check() -> TestResult {
|
||||||
let pos = test_position!(Black, [
|
let pos = test_position!(Black, [
|
||||||
Black King on E8,
|
Black King on E8,
|
||||||
Black Bishop on F6,
|
Black Bishop on F6,
|
||||||
|
@ -86,11 +87,12 @@ fn check_evasions_double_check() {
|
||||||
|
|
||||||
let generated_moves = pos.moves();
|
let generated_moves = pos.moves();
|
||||||
|
|
||||||
|
let builder = MoveBuilder::push(&piece!(Black King on E8));
|
||||||
let expected_moves = HashSet::from_iter([
|
let expected_moves = HashSet::from_iter([
|
||||||
MoveBuilder::new(piece!(Black King), Square::E8, Square::D8).build(),
|
builder.clone().to(Square::D8).build()?,
|
||||||
MoveBuilder::new(piece!(Black King), Square::E8, Square::D7).build(),
|
builder.clone().to(Square::D7).build()?,
|
||||||
MoveBuilder::new(piece!(Black King), Square::E8, Square::F7).build(),
|
builder.clone().to(Square::F7).build()?,
|
||||||
MoveBuilder::new(piece!(Black King), Square::E8, Square::F8).build(),
|
builder.clone().to(Square::F8).build()?,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
assert_move_list!(
|
assert_move_list!(
|
||||||
|
@ -98,10 +100,12 @@ fn check_evasions_double_check() {
|
||||||
expected_moves,
|
expected_moves,
|
||||||
pos
|
pos
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn single_check_with_blocker() {
|
fn single_check_with_blocker() -> TestResult {
|
||||||
let pos = test_position!(Black, [
|
let pos = test_position!(Black, [
|
||||||
Black King on E8,
|
Black King on E8,
|
||||||
Black Knight on G6,
|
Black Knight on G6,
|
||||||
|
@ -111,15 +115,15 @@ fn single_check_with_blocker() {
|
||||||
|
|
||||||
let generated_moves = pos.moves();
|
let generated_moves = pos.moves();
|
||||||
|
|
||||||
|
let king_builder = MoveBuilder::push(&piece!(Black King on E8));
|
||||||
|
let knight_builder = MoveBuilder::push(&piece!(Black Knight on G6));
|
||||||
let expected_moves = HashSet::from_iter([
|
let expected_moves = HashSet::from_iter([
|
||||||
MoveBuilder::new(piece!(Black King), Square::E8, Square::D8).build(),
|
king_builder.clone().to(Square::D8).build()?,
|
||||||
MoveBuilder::new(piece!(Black King), Square::E8, Square::D7).build(),
|
king_builder.clone().to(Square::D7).build()?,
|
||||||
MoveBuilder::new(piece!(Black King), Square::E8, Square::F7).build(),
|
king_builder.clone().to(Square::F7).build()?,
|
||||||
MoveBuilder::new(piece!(Black King), Square::E8, Square::F8).build(),
|
king_builder.clone().to(Square::F8).build()?,
|
||||||
MoveBuilder::new(piece!(Black Knight), Square::G6, Square::E7).build(),
|
knight_builder.clone().to(Square::E7).build()?,
|
||||||
MoveBuilder::new(piece!(Black Knight), Square::G6, Square::E5)
|
knight_builder.clone().capturing_on(Square::E5).build()?,
|
||||||
.capturing(piece!(White Rook on E5))
|
|
||||||
.build(),
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
assert_move_list!(
|
assert_move_list!(
|
||||||
|
@ -127,10 +131,12 @@ fn single_check_with_blocker() {
|
||||||
expected_moves,
|
expected_moves,
|
||||||
pos
|
pos
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn en_passant_check_capture() {
|
fn en_passant_check_capture() -> TestResult {
|
||||||
let pos = test_position!(Black, [
|
let pos = test_position!(Black, [
|
||||||
Black King on C5,
|
Black King on C5,
|
||||||
Black Pawn on E4,
|
Black Pawn on E4,
|
||||||
|
@ -143,17 +149,19 @@ fn en_passant_check_capture() {
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
generated_moves.contains(
|
generated_moves.contains(
|
||||||
&MoveBuilder::new(piece!(Black Pawn), Square::E4, Square::D3)
|
&MoveBuilder::push(&piece!(Black Pawn on E4))
|
||||||
.capturing_en_passant(piece!(White Pawn on D4))
|
.capturing_en_passant_on(Square::D4)
|
||||||
.build()
|
.build()?
|
||||||
),
|
),
|
||||||
"Valid moves: {:?}",
|
"Valid moves: {:?}",
|
||||||
formatted_move_list!(generated_moves, pos)
|
formatted_move_list!(generated_moves, pos)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn en_passant_check_block() {
|
fn en_passant_check_block() -> TestResult {
|
||||||
let pos = test_position!(Black, [
|
let pos = test_position!(Black, [
|
||||||
Black King on B5,
|
Black King on B5,
|
||||||
Black Pawn on E4,
|
Black Pawn on E4,
|
||||||
|
@ -167,13 +175,15 @@ fn en_passant_check_block() {
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
generated_moves.contains(
|
generated_moves.contains(
|
||||||
&MoveBuilder::new(piece!(Black Pawn), Square::E4, Square::D3)
|
&MoveBuilder::push(&piece!(Black Pawn on E4))
|
||||||
.capturing_en_passant(piece!(White Pawn on D4))
|
.capturing_en_passant_on(Square::D4)
|
||||||
.build()
|
.build()?
|
||||||
),
|
),
|
||||||
"Valid moves: {:?}",
|
"Valid moves: {:?}",
|
||||||
formatted_move_list!(generated_moves, pos)
|
formatted_move_list!(generated_moves, pos)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -204,7 +214,7 @@ fn pinned_pieces_rook_cannot_move_out_of_pin() -> Result<(), String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn en_passant_discovered_check() {
|
fn en_passant_discovered_check() -> TestResult {
|
||||||
let pos = test_position!(Black, [
|
let pos = test_position!(Black, [
|
||||||
Black King on A4,
|
Black King on A4,
|
||||||
Black Pawn on E4,
|
Black Pawn on E4,
|
||||||
|
@ -214,13 +224,15 @@ fn en_passant_discovered_check() {
|
||||||
|
|
||||||
let generated_moves = pos.moves().iter().collect::<HashSet<_>>();
|
let generated_moves = pos.moves().iter().collect::<HashSet<_>>();
|
||||||
|
|
||||||
let unexpected_move = MoveBuilder::new(piece!(Black Pawn), Square::E4, Square::D3)
|
let unexpected_move = MoveBuilder::push(&piece!(Black Pawn on E4))
|
||||||
.capturing_en_passant(piece!(White Pawn on D4))
|
.capturing_en_passant_on(Square::D4)
|
||||||
.build();
|
.build()?;
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
!generated_moves.contains(&unexpected_move),
|
!generated_moves.contains(&unexpected_move),
|
||||||
"Valid moves: {:?}",
|
"Valid moves: {:?}",
|
||||||
formatted_move_list!(generated_moves, pos)
|
formatted_move_list!(generated_moves, pos)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,36 +1,32 @@
|
||||||
// Eryn Wells <eryn@erynwells.me>
|
// Eryn Wells <eryn@erynwells.me>
|
||||||
|
|
||||||
use crate::{position, r#move::AlgebraicMoveFormatter, Move, MoveBuilder};
|
use crate::{assert_move_list, test_position, testing::*};
|
||||||
use chessfriend_core::{piece, Square};
|
use chessfriend_core::{piece, Square};
|
||||||
|
use chessfriend_moves::Builder as MoveBuilder;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn one_king() {
|
fn one_king() -> TestResult {
|
||||||
let pos = position![
|
let pos = test_position![
|
||||||
White King on D3,
|
White King on D3,
|
||||||
Black King on H6,
|
Black King on H6,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
let builder = MoveBuilder::push(&piece!(White King on D3));
|
||||||
let expected_moves = HashSet::from_iter([
|
let expected_moves = HashSet::from_iter([
|
||||||
MoveBuilder::new(piece!(White King), Square::D3, Square::D4).build(),
|
builder.clone().to(Square::D4).build()?,
|
||||||
MoveBuilder::new(piece!(White King), Square::D3, Square::E4).build(),
|
builder.clone().to(Square::E4).build()?,
|
||||||
MoveBuilder::new(piece!(White King), Square::D3, Square::E3).build(),
|
builder.clone().to(Square::E3).build()?,
|
||||||
MoveBuilder::new(piece!(White King), Square::D3, Square::E2).build(),
|
builder.clone().to(Square::E2).build()?,
|
||||||
MoveBuilder::new(piece!(White King), Square::D3, Square::D2).build(),
|
builder.clone().to(Square::D2).build()?,
|
||||||
MoveBuilder::new(piece!(White King), Square::D3, Square::C2).build(),
|
builder.clone().to(Square::C2).build()?,
|
||||||
MoveBuilder::new(piece!(White King), Square::D3, Square::C3).build(),
|
builder.clone().to(Square::C3).build()?,
|
||||||
MoveBuilder::new(piece!(White King), Square::D3, Square::C4).build(),
|
builder.clone().to(Square::C4).build()?,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let generated_moves: HashSet<Move> = pos.moves().iter().collect();
|
let generated_moves: HashSet<_> = pos.moves().iter().collect();
|
||||||
|
|
||||||
assert_eq!(
|
assert_move_list!(generated_moves, expected_moves, pos);
|
||||||
generated_moves,
|
|
||||||
expected_moves,
|
Ok(())
|
||||||
"{:?}",
|
|
||||||
generated_moves
|
|
||||||
.symmetric_difference(&expected_moves)
|
|
||||||
.map(|m| format!("{}", AlgebraicMoveFormatter::new(&m, &pos)))
|
|
||||||
.collect::<Vec<String>>()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,5 +3,5 @@
|
||||||
mod move_builder;
|
mod move_builder;
|
||||||
mod position_builder;
|
mod position_builder;
|
||||||
|
|
||||||
pub use move_builder::Builder as MoveBuilder;
|
pub use move_builder::{Builder as MoveBuilder, MakeMoveError};
|
||||||
pub use position_builder::Builder as PositionBuilder;
|
pub use position_builder::Builder as PositionBuilder;
|
||||||
|
|
|
@ -1,8 +1,19 @@
|
||||||
// Eryn Wells <eryn@erynwells.me>
|
// Eryn Wells <eryn@erynwells.me>
|
||||||
|
|
||||||
use crate::{position::flags::Flags, r#move::Castle, MakeMoveError, Move, Position};
|
use crate::{position::flags::Flags, Position};
|
||||||
use chessfriend_bitboard::BitBoard;
|
use chessfriend_bitboard::BitBoard;
|
||||||
use chessfriend_core::{Color, Direction, Piece, PlacedPiece, Shape, Square};
|
use chessfriend_core::{Color, Direction, Piece, PlacedPiece, Shape, Square};
|
||||||
|
use chessfriend_moves::{Castle, Move};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub enum MakeMoveError {
|
||||||
|
PlayerOutOfTurn,
|
||||||
|
NoPiece,
|
||||||
|
NoCapturedPiece,
|
||||||
|
NoLegalMoves,
|
||||||
|
IllegalCastle,
|
||||||
|
IllegalSquare(Square),
|
||||||
|
}
|
||||||
|
|
||||||
/// A position builder that builds a new position by making a move.
|
/// A position builder that builds a new position by making a move.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -51,14 +62,14 @@ where
|
||||||
M: MoveToMake,
|
M: MoveToMake,
|
||||||
{
|
{
|
||||||
pub fn make(self, mv: &Move) -> Result<Builder<'p, ValidatedMove>, MakeMoveError> {
|
pub fn make(self, mv: &Move) -> Result<Builder<'p, ValidatedMove>, MakeMoveError> {
|
||||||
let from_square = mv.from_square();
|
let origin_square = mv.origin_square();
|
||||||
|
|
||||||
let piece = self
|
let piece = self
|
||||||
.position
|
.position
|
||||||
.piece_on_square(from_square)
|
.piece_on_square(origin_square)
|
||||||
.ok_or(MakeMoveError::NoPiece)?;
|
.ok_or(MakeMoveError::NoPiece)?;
|
||||||
|
|
||||||
let to_square = mv.to_square();
|
let target_square = mv.target_square();
|
||||||
|
|
||||||
let moves = self
|
let moves = self
|
||||||
.position
|
.position
|
||||||
|
@ -72,8 +83,8 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
if !moves.can_move_to_square(to_square) {
|
if !moves.can_move_to_square(target_square) {
|
||||||
return Err(MakeMoveError::IllegalSquare(to_square));
|
return Err(MakeMoveError::IllegalSquare(target_square));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,8 +94,8 @@ where
|
||||||
let captured_piece = if mv.is_en_passant() {
|
let captured_piece = if mv.is_en_passant() {
|
||||||
// En passant captures the pawn directly ahead (in the player's direction) of the en passant square.
|
// En passant captures the pawn directly ahead (in the player's direction) of the en passant square.
|
||||||
let capture_square = match player {
|
let capture_square = match player {
|
||||||
Color::White => to_square.neighbor(Direction::South),
|
Color::White => target_square.neighbor(Direction::South),
|
||||||
Color::Black => to_square.neighbor(Direction::North),
|
Color::Black => target_square.neighbor(Direction::North),
|
||||||
}
|
}
|
||||||
.ok_or(MakeMoveError::NoCapturedPiece)?;
|
.ok_or(MakeMoveError::NoCapturedPiece)?;
|
||||||
|
|
||||||
|
@ -96,7 +107,7 @@ where
|
||||||
} else if mv.is_capture() {
|
} else if mv.is_capture() {
|
||||||
Some(
|
Some(
|
||||||
self.position
|
self.position
|
||||||
.piece_on_square(to_square)
|
.piece_on_square(target_square)
|
||||||
.ok_or(MakeMoveError::NoCapturedPiece)?,
|
.ok_or(MakeMoveError::NoCapturedPiece)?,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@ -140,8 +151,8 @@ where
|
||||||
} else {
|
} else {
|
||||||
let en_passant_square: Option<Square> = if mv.is_double_push() {
|
let en_passant_square: Option<Square> = if mv.is_double_push() {
|
||||||
match piece.color() {
|
match piece.color() {
|
||||||
Color::White => to_square.neighbor(Direction::South),
|
Color::White => target_square.neighbor(Direction::South),
|
||||||
Color::Black => to_square.neighbor(Direction::North),
|
Color::Black => target_square.neighbor(Direction::North),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -150,8 +161,8 @@ where
|
||||||
Ok(Builder {
|
Ok(Builder {
|
||||||
position: self.position,
|
position: self.position,
|
||||||
move_to_make: ValidatedMove::RegularMove {
|
move_to_make: ValidatedMove::RegularMove {
|
||||||
from_square,
|
from_square: origin_square,
|
||||||
to_square,
|
to_square: target_square,
|
||||||
moving_piece: piece,
|
moving_piece: piece,
|
||||||
captured_piece,
|
captured_piece,
|
||||||
promotion: mv.promotion(),
|
promotion: mv.promotion(),
|
||||||
|
@ -219,18 +230,19 @@ impl<'p> Builder<'p, ValidatedMove> {
|
||||||
} => {
|
} => {
|
||||||
let mut pieces = self.position.piece_bitboards().clone();
|
let mut pieces = self.position.piece_bitboards().clone();
|
||||||
|
|
||||||
let target_squares = castle.target_squares(player);
|
let parameters = castle.parameters(player);
|
||||||
|
|
||||||
let king_from: BitBoard = king.square().into();
|
let king_origin_square: BitBoard = king.square().into();
|
||||||
let king_to: BitBoard = target_squares.king.into();
|
let king_target_square: BitBoard = parameters.king_target_square().into();
|
||||||
*pieces.bitboard_for_piece_mut(king.piece()) ^= king_from | king_to;
|
*pieces.bitboard_for_piece_mut(king.piece()) ^=
|
||||||
|
king_origin_square | king_target_square;
|
||||||
|
|
||||||
let rook_from: BitBoard = rook.square().into();
|
let rook_from: BitBoard = rook.square().into();
|
||||||
let rook_to: BitBoard = target_squares.rook.into();
|
let rook_to: BitBoard = parameters.rook_target_square().into();
|
||||||
*pieces.bitboard_for_piece_mut(rook.piece()) ^= rook_from | rook_to;
|
*pieces.bitboard_for_piece_mut(rook.piece()) ^= rook_from | rook_to;
|
||||||
|
|
||||||
*pieces.bitboard_for_color_mut(player) &=
|
*pieces.bitboard_for_color_mut(player) &=
|
||||||
!(king_from | rook_from) | (king_to | rook_to);
|
!(king_origin_square | rook_from) | (king_target_square | rook_to);
|
||||||
|
|
||||||
Position::new(
|
Position::new(
|
||||||
player.other(),
|
player.other(),
|
||||||
|
@ -257,15 +269,24 @@ impl<'p> From<&'p Position> for Builder<'p, NoMove> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{position, r#move::Castle, MoveBuilder, PositionBuilder};
|
use crate::testing::*;
|
||||||
|
use crate::{position, PositionBuilder};
|
||||||
use chessfriend_core::piece;
|
use chessfriend_core::piece;
|
||||||
|
use chessfriend_moves::Builder as MoveBuilder;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn move_white_pawn_one_square() -> Result<(), MakeMoveError> {
|
fn move_white_pawn_one_square() -> TestResult {
|
||||||
let pos = position![White Pawn on E2];
|
let pos = position![White Pawn on E2];
|
||||||
let mv = MoveBuilder::new(piece!(White Pawn), Square::E2, Square::E3).build();
|
let mv = MoveBuilder::new()
|
||||||
|
.from(Square::E2)
|
||||||
|
.to(Square::E3)
|
||||||
|
.build()
|
||||||
|
.map_err(TestError::BuildMove)?;
|
||||||
|
|
||||||
let new_position = Builder::<NoMove>::new(&pos).make(&mv)?.build();
|
let new_position = Builder::<NoMove>::new(&pos)
|
||||||
|
.make(&mv)
|
||||||
|
.map_err(|err| TestError::MakeMove(err))?
|
||||||
|
.build();
|
||||||
println!("{}", &new_position);
|
println!("{}", &new_position);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -277,9 +298,13 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn move_white_pawn_two_squares() -> Result<(), MakeMoveError> {
|
fn move_white_pawn_two_squares() -> TestResult {
|
||||||
let pos = position![White Pawn on E2];
|
let pos = position![White Pawn on E2];
|
||||||
let mv = MoveBuilder::new(piece!(White Pawn), Square::E2, Square::E4).build();
|
let mv = MoveBuilder::new()
|
||||||
|
.from(Square::E2)
|
||||||
|
.to(Square::E4)
|
||||||
|
.build()
|
||||||
|
.map_err(TestError::BuildMove)?;
|
||||||
|
|
||||||
let new_position = Builder::<NoMove>::new(&pos).make(&mv)?.build();
|
let new_position = Builder::<NoMove>::new(&pos).make(&mv)?.build();
|
||||||
println!("{}", &new_position);
|
println!("{}", &new_position);
|
||||||
|
@ -308,9 +333,7 @@ mod tests {
|
||||||
];
|
];
|
||||||
println!("{}", &pos);
|
println!("{}", &pos);
|
||||||
|
|
||||||
let mv = MoveBuilder::new(piece!(White King), Square::E1, Square::G1)
|
let mv = MoveBuilder::castling(Castle::KingSide).build();
|
||||||
.castle(Castle::KingSide)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let new_position = Builder::<NoMove>::new(&pos).make(&mv)?.build();
|
let new_position = Builder::<NoMove>::new(&pos).make(&mv)?.build();
|
||||||
println!("{}", &new_position);
|
println!("{}", &new_position);
|
||||||
|
@ -328,7 +351,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn en_passant_capture() -> Result<(), MakeMoveError> {
|
fn en_passant_capture() -> TestResult {
|
||||||
let pos = PositionBuilder::new()
|
let pos = PositionBuilder::new()
|
||||||
.place_piece(piece!(White Pawn on B5))
|
.place_piece(piece!(White Pawn on B5))
|
||||||
.place_piece(piece!(Black Pawn on A7))
|
.place_piece(piece!(Black Pawn on A7))
|
||||||
|
@ -336,7 +359,8 @@ mod tests {
|
||||||
.build();
|
.build();
|
||||||
println!("{pos}");
|
println!("{pos}");
|
||||||
|
|
||||||
let black_pawn_move = MoveBuilder::new(piece!(Black Pawn), Square::A7, Square::A5).build();
|
let black_pawn_move = MoveBuilder::new().from(Square::A7).to(Square::A5).build()?;
|
||||||
|
|
||||||
assert!(black_pawn_move.is_double_push());
|
assert!(black_pawn_move.is_double_push());
|
||||||
assert!(!black_pawn_move.is_en_passant());
|
assert!(!black_pawn_move.is_en_passant());
|
||||||
|
|
||||||
|
@ -352,9 +376,9 @@ mod tests {
|
||||||
Some(piece!(White Pawn on B5))
|
Some(piece!(White Pawn on B5))
|
||||||
);
|
);
|
||||||
|
|
||||||
let white_pawn_capture = MoveBuilder::new(piece!(White Pawn), Square::B5, Square::A6)
|
let white_pawn_capture = MoveBuilder::push(&piece!(White Pawn on B5))
|
||||||
.capturing_en_passant(piece!(Black Pawn on A5))
|
.capturing_en_passant_on(Square::A5)
|
||||||
.build();
|
.build()?;
|
||||||
let en_passant_capture = Builder::<NoMove>::new(&en_passant_position)
|
let en_passant_capture = Builder::<NoMove>::new(&en_passant_position)
|
||||||
.make(&white_pawn_capture)?
|
.make(&white_pawn_capture)?
|
||||||
.build();
|
.build();
|
||||||
|
|
|
@ -120,13 +120,13 @@ impl Builder {
|
||||||
|
|
||||||
for color in Color::ALL {
|
for color in Color::ALL {
|
||||||
for castle in Castle::ALL {
|
for castle in Castle::ALL {
|
||||||
let starting_squares = castle.starting_squares(color);
|
let parameters = castle.parameters(color);
|
||||||
let has_rook_on_starting_square = self
|
let has_rook_on_starting_square = self
|
||||||
.pieces
|
.pieces
|
||||||
.get(&starting_squares.rook)
|
.get(¶meters.rook_origin_square())
|
||||||
.is_some_and(|piece| piece.shape() == Shape::Rook);
|
.is_some_and(|piece| piece.shape() == Shape::Rook);
|
||||||
let king_is_on_starting_square =
|
let king_is_on_starting_square =
|
||||||
self.kings[color as usize] == Some(starting_squares.king);
|
self.kings[color as usize] == Some(parameters.king_origin_square());
|
||||||
|
|
||||||
if !king_is_on_starting_square || !has_rook_on_starting_square {
|
if !king_is_on_starting_square || !has_rook_on_starting_square {
|
||||||
flags.clear_player_has_right_to_castle_flag(color, castle);
|
flags.clear_player_has_right_to_castle_flag(color, castle);
|
||||||
|
|
|
@ -45,8 +45,6 @@ impl Default for Flags {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::r#move::Castle;
|
|
||||||
use chessfriend_core::Color;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn castle_flags() {
|
fn castle_flags() {
|
||||||
|
|
|
@ -9,7 +9,7 @@ mod pieces;
|
||||||
mod position;
|
mod position;
|
||||||
|
|
||||||
pub use {
|
pub use {
|
||||||
builders::{MoveBuilder, PositionBuilder},
|
builders::{MakeMoveError, MoveBuilder, PositionBuilder},
|
||||||
diagram_formatter::DiagramFormatter,
|
diagram_formatter::DiagramFormatter,
|
||||||
pieces::Pieces,
|
pieces::Pieces,
|
||||||
position::Position,
|
position::Position,
|
||||||
|
|
|
@ -372,9 +372,10 @@ impl fmt::Display for Position {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{position, test_position, Castle, Position, PositionBuilder};
|
use super::*;
|
||||||
|
use crate::{position, test_position, Position, PositionBuilder};
|
||||||
use chessfriend_bitboard::bitboard;
|
use chessfriend_bitboard::bitboard;
|
||||||
use chessfriend_core::{piece, Color, Square};
|
use chessfriend_core::piece;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn piece_on_square() {
|
fn piece_on_square() {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Eryn Wells <eryn@erynwells.me>
|
// Eryn Wells <eryn@erynwells.me>
|
||||||
|
|
||||||
use crate::MakeMoveError;
|
use crate::MakeMoveError;
|
||||||
use chessfriend_moves::{BuildMoveError, BuildMoveResult, Move};
|
use chessfriend_moves::BuildMoveError;
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! assert_move_list {
|
macro_rules! assert_move_list {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue