use super::Position;
use crate::board::bitboard_set::BitboardSet;
use crate::types::{
AperyMove32, Color, File, MOVE_END, MOVE_NONE, MOVE_NULL, MOVE_RESIGN, MOVE_WIN, Move, Move32,
Piece, PieceType, Rank, Square,
};
fn square_from_csa(s: &str) -> Option<Square> {
if s.len() != 2 {
return None;
}
let mut chars = s.chars();
let file_char = chars.next()?;
let rank_char = chars.next()?;
if !file_char.is_ascii_digit() || !rank_char.is_ascii_digit() {
return None;
}
let file_digit = file_char.to_digit(10)?;
let rank_digit = rank_char.to_digit(10)?;
if !(1..=9).contains(&file_digit) || !(1..=9).contains(&rank_digit) {
return None;
}
let file = File::new(i8::try_from(file_digit - 1).ok()?);
let rank = Rank::new(i8::try_from(rank_digit - 1).ok()?);
Some(Square::from_file_rank(file, rank))
}
fn piece_type_from_csa(code: &str) -> Option<PieceType> {
match code {
"FU" => Some(PieceType::PAWN),
"KY" => Some(PieceType::LANCE),
"KE" => Some(PieceType::KNIGHT),
"GI" => Some(PieceType::SILVER),
"KI" => Some(PieceType::GOLD),
"KA" => Some(PieceType::BISHOP),
"HI" => Some(PieceType::ROOK),
"OU" => Some(PieceType::KING),
"TO" => Some(PieceType::PRO_PAWN),
"NY" => Some(PieceType::PRO_LANCE),
"NK" => Some(PieceType::PRO_KNIGHT),
"NG" => Some(PieceType::PRO_SILVER),
"UM" => Some(PieceType::HORSE),
"RY" => Some(PieceType::DRAGON),
_ => None,
}
}
impl Position {
#[must_use]
pub fn move32_from_move(&self, mv: Move) -> Move32 {
const MASK_7: u16 = 0x7f;
let raw = mv.raw();
if raw == Move::MOVE_NONE.raw() {
return MOVE_NONE;
}
if raw == Move::MOVE_NULL.raw() {
return MOVE_NULL;
}
if raw == Move::MOVE_RESIGN.raw() {
return MOVE_RESIGN;
}
if raw == Move::MOVE_WIN.raw() {
return MOVE_WIN;
}
if raw == Move::MOVE_END.raw() {
return MOVE_END;
}
let to_raw = raw & MASK_7;
if to_raw > 80 {
return MOVE_NONE;
}
if (raw & Move::MOVE_DROP) != 0 {
let pt_raw = (raw >> 7) & MASK_7;
if !(1..=7).contains(&pt_raw) {
return MOVE_NONE;
}
let piece_type = PieceType::new(pt_raw as i8);
return Move32::drop(piece_type, Square::new(to_raw as i8), self.turn());
}
let from_raw = (raw >> 7) & MASK_7;
if from_raw > 80 {
return MOVE_NONE;
}
let from = Square::new(from_raw as i8);
let to = Square::new(to_raw as i8);
let piece = unsafe { self.piece_on_unchecked(from) };
if piece == Piece::NONE || piece.color() != self.turn() {
return MOVE_NONE;
}
if (raw & Move::MOVE_PROMOTE) != 0 {
if !piece.piece_type().is_promotable() {
return MOVE_NONE;
}
return Move32::promotion(from, to, piece);
}
Move32::normal(from, to, piece)
}
#[must_use]
pub fn apery_move32_from_move(&self, mv: Move) -> AperyMove32 {
let apery = mv.to_apery();
if !mv.is_normal() {
return AperyMove32::from_raw(u32::from(apery.raw()));
}
if mv.is_drop() {
return AperyMove32::from_raw(u32::from(apery.raw()));
}
let from = mv.from_sq();
let to = mv.to_sq();
let piece_type_before = self.piece_on(from).piece_type();
let captured_piece_type = self.piece_on(to).piece_type();
let raw = u32::from(apery.raw())
| (u32::from(piece_type_before.raw() as u8 & 0xf) << 16)
| (u32::from(captured_piece_type.raw() as u8 & 0xf) << 20);
AperyMove32::from_raw(raw)
}
#[must_use]
pub fn apery_move32_from_move32(&self, mv: Move32) -> AperyMove32 {
self.apery_move32_from_move(mv.to_move())
}
#[must_use]
pub fn move32_from_apery_move32(&self, mv: AperyMove32) -> Move32 {
let base = mv.to_move();
if !base.is_normal() {
return Move32::from_raw(u32::from(base.raw()));
}
self.move32_from_move(base)
}
#[inline]
pub(crate) unsafe fn move32_from_move_fast(&self, mv: Move) -> Move32 {
unsafe {
const MASK_7: u16 = 0x7f;
let raw = mv.raw();
let to = Square::new((raw & MASK_7) as i8);
debug_assert!(to.is_on_board(), "move32_from_move_fast: to must be on board");
if (raw & Move::MOVE_DROP) != 0 {
let pt_raw = (raw >> 7) & MASK_7;
let piece_type = PieceType::new(pt_raw as i8);
return Move32::drop(piece_type, to, self.turn());
}
let from = Square::new(((raw >> 7) & MASK_7) as i8);
debug_assert!(from.is_on_board(), "move32_from_move_fast: from must be on board");
let piece = self.piece_on_unchecked(from);
if (raw & Move::MOVE_PROMOTE) != 0 {
Move32::promotion(from, to, piece)
} else {
Move32::normal(from, to, piece)
}
}
}
#[must_use]
pub fn move_from_csa(&self, csa: &str) -> Move32 {
let mut csa = csa;
if csa.len() == 7 {
let sign = match csa.chars().next() {
Some(sign) if sign == '+' || sign == '-' => sign,
_ => return MOVE_NONE,
};
let side = self.turn();
if (sign == '+' && side != Color::BLACK) || (sign == '-' && side != Color::WHITE) {
return MOVE_NONE;
}
csa = &csa[1..];
}
if csa.len() != 6 {
return MOVE_NONE;
}
let (Some(from_str), Some(to_str), Some(piece_str)) =
(csa.get(0..2), csa.get(2..4), csa.get(4..6))
else {
return MOVE_NONE;
};
let Some(to) = square_from_csa(to_str) else {
return MOVE_NONE;
};
let Some(piece_type_after) = piece_type_from_csa(piece_str) else {
return MOVE_NONE;
};
if from_str == "00" {
let drop_pt = piece_type_after.demote();
if drop_pt.raw() < PieceType::PAWN.raw() || drop_pt.raw() > PieceType::GOLD.raw() {
return MOVE_NONE;
}
return Move32::drop(drop_pt, to, self.turn());
}
let Some(from) = square_from_csa(from_str) else {
return MOVE_NONE;
};
let piece = self.piece_on(from);
if piece == Piece::NONE || piece.color() != self.turn() {
return MOVE_NONE;
}
let mover_pt = piece.piece_type();
let mover_base = mover_pt.demote();
let after_base = piece_type_after.demote();
if mover_base != after_base {
return MOVE_NONE;
}
if mover_pt.is_promoted() && !piece_type_after.is_promoted() {
return MOVE_NONE;
}
if !mover_pt.is_promoted() && piece_type_after.is_promoted() {
return Move32::promotion(from, to, piece);
}
Move32::normal(from, to, piece)
}
pub(in crate::board) fn rebuild_bitboards(&mut self) {
self.bitboards = BitboardSet::new();
let mut king_squares = [Square::NONE; Color::COUNT];
for (sq, piece) in self.board.iter() {
if !piece.is_empty() {
let piece_type = piece.piece_type();
let color = piece.color();
self.bitboards.set_piece(sq, piece_type, color);
if piece_type == PieceType::KING {
king_squares[color.to_index()] = sq;
}
}
}
for color in [Color::BLACK, Color::WHITE] {
self.set_king_square(color, king_squares[color.to_index()]);
}
self.recompute_caches();
}
}