[position, board, core, moves] Implement a bunch of make_move code

Implement making double push and promotion moves. Then write several tests to
exercise these. Add convenient static functions to the Move struct to build moves
quickly, without using the Builder.

Add a is_promotable_rank() method to Rank to check that a rank can be used for
promotion moves.

The tests found and fixed a bug in pawn movement where the en passant square was
being discarded when deciding whether an e.p. move can be made.
This commit is contained in:
Eryn Wells 2025-05-20 19:29:02 -07:00
parent 6591619e32
commit 039fd2b080
4 changed files with 257 additions and 22 deletions

View file

@ -1,7 +1,6 @@
// Eryn Wells <eryn@erynwells.me>
use crate::builder::Builder;
use crate::defs::Kind;
use crate::defs::{Kind, PromotionShape};
use chessfriend_core::{Rank, Shape, Square, Wing};
use std::fmt;
@ -14,6 +13,48 @@ use std::fmt;
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
pub struct Move(pub(crate) u16);
impl Move {
#[must_use]
pub fn quiet(origin: Square, target: Square) -> Self {
let origin_bits = (origin as u16) << 4;
let target_bits = (target as u16) << 10;
Move(origin_bits | target_bits)
}
#[must_use]
pub fn double_push(origin: Square, target: Square) -> Self {
let origin_bits = (origin as u16) << 4;
let target_bits = (target as u16) << 10;
let flag_bits = Kind::DoublePush as u16;
Move(origin_bits | target_bits | flag_bits)
}
#[must_use]
pub fn capture(origin: Square, target: Square) -> Self {
let origin_bits = (origin as u16) << 4;
let target_bits = (target as u16) << 10;
let flag_bits = Kind::Capture as u16;
Move(origin_bits | target_bits | flag_bits)
}
#[must_use]
pub fn en_passant_capture(origin: Square, target: Square) -> Self {
let origin_bits = (origin as u16) << 4;
let target_bits = (target as u16) << 10;
let flag_bits = Kind::EnPassantCapture as u16;
Move(origin_bits | target_bits | flag_bits)
}
#[must_use]
pub fn promotion(origin: Square, target: Square, shape: PromotionShape) -> Self {
let origin_bits = (origin as u16) << 4;
let target_bits = (target as u16) << 10;
let flag_bits = Kind::Promotion as u16;
let shape_bits = shape as u16;
Move(origin_bits | target_bits | flag_bits | shape_bits)
}
}
impl Move {
#[must_use]
#[allow(clippy::missing_panics_doc)]
@ -85,7 +126,7 @@ impl Move {
}
#[must_use]
pub fn promotion(&self) -> Option<Shape> {
pub fn promotion_shape(&self) -> Option<Shape> {
if !self.is_promotion() {
return None;
}
@ -139,7 +180,7 @@ impl fmt::Display for Move {
let transfer_char = self.transfer_char();
write!(f, "{origin}{transfer_char}{target}")?;
if let Some(promotion) = self.promotion() {
if let Some(promotion) = self.promotion_shape() {
write!(f, "={promotion}")?;
} else if self.is_en_passant() {
write!(f, " e.p.")?;