use crate::board::state_info::PartialKeys;
use crate::board::zobrist::ZobristKey;
use crate::types::{Color, EnteringKingRule, File, HandPiece, Piece, PieceType, Square};
use std::convert::TryFrom;
use std::fmt;
pub type Ply = u16;
#[repr(transparent)]
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
pub struct PackedPiece(u8);
const PIECE_TYPE_FROM_INDEX: [PieceType; PieceType::COUNT] = [
PieceType::NONE,
PieceType::PAWN,
PieceType::LANCE,
PieceType::KNIGHT,
PieceType::SILVER,
PieceType::BISHOP,
PieceType::ROOK,
PieceType::GOLD,
PieceType::KING,
PieceType::PRO_PAWN,
PieceType::PRO_LANCE,
PieceType::PRO_KNIGHT,
PieceType::PRO_SILVER,
PieceType::HORSE,
PieceType::DRAGON,
PieceType::GOLD_LIKE,
];
impl PackedPiece {
pub const EMPTY: Self = Self(0);
#[inline]
#[must_use]
pub const fn raw(self) -> u8 {
self.0
}
#[inline]
#[must_use]
pub fn new(piece_type: PieceType, color: Color, promoted: bool) -> Self {
if piece_type == PieceType::NONE {
return Self::EMPTY;
}
let mut value = u8::try_from(piece_type.to_index()).expect("piece type index fits in u8");
if color == Color::WHITE {
value |= 0x10; }
if promoted {
value |= 0x20; }
Self(value)
}
#[inline]
#[must_use]
pub fn from_piece(piece: Piece) -> Self {
if piece == Piece::NONE {
return Self::EMPTY;
}
let piece_type = piece.piece_type();
let color = piece.color();
let promoted = matches!(
piece_type,
PieceType::PRO_PAWN
| PieceType::PRO_LANCE
| PieceType::PRO_KNIGHT
| PieceType::PRO_SILVER
| PieceType::HORSE
| PieceType::DRAGON
);
let base_piece_type = if promoted {
match piece_type {
PieceType::PRO_PAWN => PieceType::PAWN,
PieceType::PRO_LANCE => PieceType::LANCE,
PieceType::PRO_KNIGHT => PieceType::KNIGHT,
PieceType::PRO_SILVER => PieceType::SILVER,
PieceType::HORSE => PieceType::BISHOP,
PieceType::DRAGON => PieceType::ROOK,
_ => piece_type,
}
} else {
piece_type
};
Self::new(base_piece_type, color, promoted)
}
#[inline]
#[must_use]
pub fn to_piece(self) -> Piece {
if self == Self::EMPTY {
return Piece::NONE;
}
let piece_type = self.piece_type();
let color = self.color();
if self.is_promoted() {
let promoted_type = match piece_type {
PieceType::PAWN => PieceType::PRO_PAWN,
PieceType::LANCE => PieceType::PRO_LANCE,
PieceType::KNIGHT => PieceType::PRO_KNIGHT,
PieceType::SILVER => PieceType::PRO_SILVER,
PieceType::BISHOP => PieceType::HORSE,
PieceType::ROOK => PieceType::DRAGON,
_ => piece_type, };
Piece::from_parts(color, promoted_type)
} else {
Piece::from_parts(color, piece_type)
}
}
#[inline]
#[must_use]
pub fn piece_type(self) -> PieceType {
let idx = usize::from(self.0 & 0x0F);
PIECE_TYPE_FROM_INDEX.get(idx).copied().unwrap_or(PieceType::NONE)
}
#[inline]
#[must_use]
pub const fn color(self) -> Color {
if (self.0 & 0x10) != 0 {
Color::WHITE
} else {
Color::BLACK
}
}
#[inline]
#[must_use]
pub const fn is_promoted(self) -> bool {
(self.0 & 0x20) != 0
}
#[inline]
#[must_use]
pub const fn is_empty(self) -> bool {
self.0 == 0
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct BoardArray([Piece; Square::COUNT]);
impl BoardArray {
#[must_use]
pub const fn filled(piece: Piece) -> Self {
Self([piece; Square::COUNT])
}
#[must_use]
pub const fn empty() -> Self {
Self::filled(Piece::NONE)
}
#[inline]
pub fn set(&mut self, sq: Square, piece: Piece) {
debug_assert!(sq.is_on_board(), "Invalid square: {sq:?}");
self.0[sq.to_board_index()] = piece;
}
#[inline]
#[must_use]
pub fn get(&self, sq: Square) -> Piece {
debug_assert!(sq.is_on_board(), "Invalid square: {sq:?}");
self.0[sq.to_board_index()]
}
#[inline]
#[must_use]
pub unsafe fn get_unchecked(&self, sq: Square) -> Piece {
unsafe {
*self.0.get_unchecked(sq.to_board_index())
}
}
pub fn iter(&self) -> impl Iterator<Item = (Square, Piece)> + '_ {
self.0.iter().enumerate().map(|(idx, &piece)| (Square::from_index(idx), piece))
}
}
impl Default for BoardArray {
fn default() -> Self {
Self::empty()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct CapturedPieceDelta {
pub piece: Piece,
pub hand_count_before: u32,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MoveDelta32 {
Drop {
color: Color,
piece_type: PieceType,
to: Square,
hand_count_before: u32,
},
Board {
color: Color,
from: Square,
to: Square,
moved_piece_before: Piece,
moved_piece_after: Piece,
captured: Option<CapturedPieceDelta>,
},
}
impl MoveDelta32 {
#[must_use]
pub const fn drop(
color: Color,
piece_type: PieceType,
to: Square,
hand_count_before: u32,
) -> Self {
Self::Drop { color, piece_type, to, hand_count_before }
}
#[must_use]
pub const fn board(
color: Color,
from: Square,
to: Square,
moved_piece_before: Piece,
moved_piece_after: Piece,
captured: Option<CapturedPieceDelta>,
) -> Self {
Self::Board { color, from, to, moved_piece_before, moved_piece_after, captured }
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct MoveApplyFacts {
pub delta: MoveDelta32,
pub mv: crate::types::Move32,
pub side_moved: Color,
pub side_to_move_after: Color,
pub from: Option<Square>,
pub to: Square,
pub moved_piece_before: Piece,
pub moved_piece_after: Piece,
pub captured_piece: Piece,
pub captured_hand_count_before: Option<u32>,
pub dropped_piece_type: Option<PieceType>,
pub drop_hand_count_before: Option<u32>,
pub promoted: bool,
pub moved_king: bool,
pub gives_check: bool,
pub board_key_after: ZobristKey,
pub hand_key_after: ZobristKey,
pub key_after: ZobristKey,
pub partial_keys_after: PartialKeys,
}
impl MoveApplyFacts {
#[must_use]
pub fn from_delta(
delta: MoveDelta32,
mv: crate::types::Move32,
gives_check: bool,
board_key_after: ZobristKey,
hand_key_after: ZobristKey,
key_after: ZobristKey,
partial_keys_after: PartialKeys,
) -> Self {
match delta {
MoveDelta32::Drop { color, piece_type, to, hand_count_before } => Self {
delta,
mv,
side_moved: color,
side_to_move_after: color.flip(),
from: None,
to,
moved_piece_before: Piece::NONE,
moved_piece_after: Piece::from_parts(color, piece_type),
captured_piece: Piece::NONE,
captured_hand_count_before: None,
dropped_piece_type: Some(piece_type),
drop_hand_count_before: Some(hand_count_before),
promoted: false,
moved_king: false,
gives_check,
board_key_after,
hand_key_after,
key_after,
partial_keys_after,
},
MoveDelta32::Board {
color,
from,
to,
moved_piece_before,
moved_piece_after,
captured,
} => {
let captured_piece = captured.map_or(Piece::NONE, |captured| captured.piece);
Self {
delta,
mv,
side_moved: color,
side_to_move_after: color.flip(),
from: Some(from),
to,
moved_piece_before,
moved_piece_after,
captured_piece,
captured_hand_count_before: captured.map(|captured| captured.hand_count_before),
dropped_piece_type: None,
drop_hand_count_before: None,
promoted: moved_piece_before != moved_piece_after,
moved_king: moved_piece_before.piece_type() == PieceType::KING,
gives_check,
board_key_after,
hand_key_after,
key_after,
partial_keys_after,
}
}
}
}
#[inline]
#[must_use]
pub const fn search_stack_site(self) -> MoveStackSiteFacts {
MoveStackSiteFacts { moved_piece: self.moved_piece_after, to: self.to }
}
#[inline]
#[must_use]
pub const fn captured_piece(self) -> Piece {
self.captured_piece
}
#[inline]
#[must_use]
pub fn is_capture(self) -> bool {
!self.captured_piece.is_empty()
}
#[inline]
#[must_use]
pub const fn is_drop(self) -> bool {
self.from.is_none()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct MoveStackSiteFacts {
pub moved_piece: Piece,
pub to: Square,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MoveError {
NoStateInfo,
StackUnderflow,
StateCapacityExceeded,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ValidationError {
NoKing(Color),
TwoKings(Color),
DoublePawn(File, Color),
InvalidHandCount { piece: HandPiece, count: u32 },
InvalidPlacement(Square, PieceType),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ValidationIssue {
NoKing(Color),
TwoKings(Color),
DoublePawn(File, Color),
InvalidHandCount { piece: HandPiece, count: u32 },
InvalidPlacement(Square, PieceType),
}
impl fmt::Display for ValidationIssue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::NoKing(color) => write!(f, "{color:?} king is missing"),
Self::TwoKings(color) => write!(f, "{color:?} has two or more kings"),
Self::DoublePawn(file, color) => {
write!(f, "{color:?} has double pawn on file {file:?}")
}
Self::InvalidHandCount { piece, count } => {
write!(f, "invalid hand count for {piece:?}: {count}")
}
Self::InvalidPlacement(sq, pt) => {
write!(f, "invalid placement of {pt:?} on {sq:?}")
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ValidationReport {
issues: Vec<ValidationIssue>,
}
impl ValidationReport {
#[must_use]
pub fn is_valid(&self) -> bool {
self.issues.is_empty()
}
#[must_use]
pub fn issues(&self) -> &[ValidationIssue] {
&self.issues
}
#[must_use]
pub fn into_issues(self) -> Vec<ValidationIssue> {
self.issues
}
#[cfg(feature = "validation")]
pub(crate) fn new(issues: Vec<ValidationIssue>) -> Self {
Self { issues }
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DeclarationEvaluation {
pub rule: EnteringKingRule,
pub can_declare: bool,
pub detail: DeclarationDetail,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DeclarationDetail {
NoRule,
TryRule {
king_square: Option<Square>,
try_square: Square,
can_reach: bool,
can_occupy: bool,
is_safe: bool,
},
PointRule {
not_in_check: bool,
king_in_enemy_camp: bool,
pieces_in_camp: i32,
enough_pieces: bool,
points: i32,
required_points: i32,
},
}