use super::helpers::apply_usi_move;
use super::*;
const fn is_promoted_piece(piece: Piece) -> bool {
matches!(
piece.piece_type(),
PieceType::PRO_PAWN
| PieceType::PRO_LANCE
| PieceType::PRO_KNIGHT
| PieceType::PRO_SILVER
| PieceType::HORSE
| PieceType::DRAGON
)
}
use crate::types::{
Color, File, Hand, MOVE_NONE, Move, Piece, PieceType, Rank, SQ_51, SQ_55, SQ_59, Square,
};
#[test]
fn test_packed_piece_encoding_roundtrip() {
assert_eq!(PackedPiece::EMPTY.raw(), 0);
assert!(PackedPiece::EMPTY.is_empty());
let black_pawn = PackedPiece::new(PieceType::PAWN, Color::BLACK, false);
assert_eq!(black_pawn.piece_type(), PieceType::PAWN);
assert_eq!(black_pawn.color(), Color::BLACK);
assert!(!black_pawn.is_promoted());
let white_pro_silver = PackedPiece::new(PieceType::SILVER, Color::WHITE, true);
assert_eq!(white_pro_silver.piece_type(), PieceType::SILVER);
assert_eq!(white_pro_silver.color(), Color::WHITE);
assert!(white_pro_silver.is_promoted());
let pieces = [
Piece::B_PAWN,
Piece::W_LANCE,
Piece::B_KNIGHT,
Piece::W_SILVER,
Piece::B_GOLD,
Piece::W_BISHOP,
Piece::B_ROOK,
Piece::W_KING,
];
for piece in pieces {
let packed = PackedPiece::from_piece(piece);
let converted = packed.to_piece();
assert_eq!(piece, converted);
}
}
#[test]
fn test_board_array_behaves_like_81_square_buffer() {
let mut board = BoardArray::empty();
board.set(SQ_55, Piece::B_KING);
assert!(!board.get(SQ_55).is_empty());
assert_eq!(board.get(SQ_55).piece_type(), PieceType::KING);
let non_empty_count = board.iter().filter(|(_, p)| !p.is_empty()).count();
assert_eq!(non_empty_count, 1);
}
#[test]
fn test_position_basic_state_and_startpos() {
let pos = Position::empty();
assert_eq!(pos.turn(), Color::BLACK);
assert_eq!(pos.game_ply(), 1);
assert_eq!(pos.hand(Color::BLACK), Hand::ZERO);
assert_eq!(pos.hand(Color::WHITE), Hand::ZERO);
let start = crate::board::hirate_position();
assert_eq!(start.piece_on(SQ_59).piece_type(), PieceType::KING);
assert_eq!(start.piece_on(SQ_59).color(), Color::BLACK);
assert_eq!(start.piece_on(SQ_51).piece_type(), PieceType::KING);
assert_eq!(start.piece_on(SQ_51).color(), Color::WHITE);
assert_eq!(start.turn(), Color::BLACK);
assert_eq!(start.game_ply(), 1);
}
#[test]
fn test_current_state_helpers_match_state_stack() {
let mut pos = crate::board::hirate_position();
apply_usi_move(&mut pos, "7g7f");
apply_usi_move(&mut pos, "3c3d");
let current = pos.current_state();
let stack_current = pos.state_stack().current();
assert_eq!(pos.state_stack_depth(), pos.state_stack().depth());
assert_eq!(current.board_key, stack_current.board_key);
assert_eq!(current.hand_key, stack_current.hand_key);
assert_eq!(current.repetition_counter, stack_current.repetition_counter);
assert_eq!(current.repetition_distance, stack_current.repetition_distance);
assert_eq!(pos.last_move(), stack_current.last_move());
assert_eq!(pos.last_moved_piece_type(), stack_current.last_moved_piece_type());
assert_eq!(pos.repetition_counter(), stack_current.repetition_counter);
assert_eq!(pos.repetition_distance(), stack_current.repetition_distance);
}
#[cfg(feature = "validation")]
#[test]
fn test_position_sfen_roundtrip_and_validation() {
let sfen = "lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL b - 1";
let pos = crate::board::position_from_sfen(sfen).unwrap();
let generated = pos.to_sfen(None);
assert_eq!(sfen, generated);
let mut invalid = Position::empty();
assert!(invalid.is_valid());
invalid.board.set(SQ_59, Piece::B_KING);
invalid.board.set(SQ_51, Piece::W_KING);
invalid.rebuild_bitboards();
assert!(invalid.is_valid());
}
#[test]
fn test_position_flipped_sfen_matches_yaneuraou() {
let sfen = "lnsgk1snl/1r4g2/p1ppppb1p/6pP1/7R1/2P6/P2PPPP1P/1SG6/LN2KGSNL b BP2p 21";
let expected = "lnsgk2nl/6gs1/p1pppp2p/6p2/1r7/1pP6/P1BPPPP1P/2G4R1/LNS1KGSNL w 2Pbp 21";
let pos = crate::board::position_from_sfen(sfen).unwrap();
assert_eq!(pos.to_sfen_flipped(None), expected);
}
#[test]
fn test_position_move_from_csa_and_move() {
let pos = crate::board::hirate_position();
let mv = pos.move_from_csa("7776FU");
assert_eq!(mv.to_usi(), "7g7f");
let mv = pos.move_from_csa("+7776FU");
assert_eq!(mv.to_usi(), "7g7f");
let mv16 = Move::normal("7g".parse().unwrap(), "7f".parse().unwrap());
let mv = pos.move32_from_move(mv16);
assert_eq!(mv.to_usi(), "7g7f");
let mut promo_pos = Position::empty();
let from = "2b".parse::<Square>().unwrap();
promo_pos.board.set(from, Piece::B_BISHOP);
let mv = promo_pos.move_from_csa("2233UM");
assert_eq!(mv.to_usi(), "2b3c+");
}
#[test]
fn test_position_move_from_csa_rejects_non_ascii_without_panic() {
let pos = crate::board::hirate_position();
assert_eq!(pos.move_from_csa("ああ"), MOVE_NONE);
assert_eq!(pos.move_from_csa("+ああ"), MOVE_NONE);
}
#[test]
fn test_apply_move_variants() {
let mut pos = crate::board::hirate_position();
let from = Square::from_file_rank(File::FILE_7, Rank::RANK_7);
let to = Square::from_file_rank(File::FILE_7, Rank::RANK_6);
let piece = pos.piece_on(from);
let mv = Move32::normal(from, to, piece);
let initial_ply = pos.game_ply();
pos.apply_move32(mv);
assert_eq!(pos.piece_on(from), Piece::NONE);
assert_eq!(pos.piece_on(to), piece);
assert_eq!(pos.turn(), Color::WHITE);
assert_eq!(pos.game_ply(), initial_ply + 1);
let sfen = "lnsgkgsnl/1r5b1/ppppppppp/9/4p4/4P4/PPPPPPPPP/1B5R1/LNSGKGSNL b - 1";
let mut capture_pos = crate::board::position_from_sfen(sfen).unwrap();
let from = Square::from_file_rank(File::FILE_5, Rank::RANK_6);
let to = Square::from_file_rank(File::FILE_5, Rank::RANK_5);
let piece = capture_pos.piece_on(from);
let captured = capture_pos.piece_on(to);
let mv = Move32::normal(from, to, piece);
capture_pos.apply_move32(mv);
let state_stack = capture_pos.state_stack();
let state = state_stack.current();
assert_eq!(state.cold.captured_piece(), captured);
assert_eq!(capture_pos.hand(Color::BLACK).pawn_count(), 1);
let sfen = "lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPP1PPPP/1B5R1/LNSGKGSNL b P 1";
let mut drop_pos = crate::board::position_from_sfen(sfen).unwrap();
let to = Square::from_file_rank(File::FILE_5, Rank::RANK_5);
let mv = Move32::drop(PieceType::PAWN, to, Color::BLACK);
let initial_hand = drop_pos.hand(Color::BLACK);
drop_pos.apply_move32(mv);
let placed_piece = drop_pos.piece_on(to);
assert_eq!(placed_piece, Piece::B_PAWN);
assert_eq!(drop_pos.hand(Color::BLACK).pawn_count(), initial_hand.pawn_count() - 1);
assert_eq!(drop_pos.state_stack().current().cold.captured_piece(), Piece::NONE);
let sfen = "lnsgkgsnl/1r5b1/2R6/ppppppppp/9/9/PPPPPPPPP/1B7/LNSGKGSNL b - 1";
let mut promote_pos = crate::board::position_from_sfen(sfen).unwrap();
let from = Square::from_file_rank(File::FILE_7, Rank::RANK_3);
let to = Square::from_file_rank(File::FILE_7, Rank::RANK_2);
let piece = promote_pos.piece_on(from);
let mv = Move32::promotion(from, to, piece);
promote_pos.apply_move32(mv);
assert!(is_promoted_piece(promote_pos.piece_on(to)));
let invalid_move_pos = crate::board::hirate_position();
let from = Square::from_file_rank(File::FILE_5, Rank::RANK_5);
let to = Square::from_file_rank(File::FILE_5, Rank::RANK_4);
let mv = Move32::normal(from, to, Piece::B_PAWN);
assert!(!invalid_move_pos.is_legal_move32(mv));
let to = Square::from_file_rank(File::FILE_5, Rank::RANK_5);
let mv = Move32::drop(PieceType::PAWN, to, Color::BLACK);
assert!(!invalid_move_pos.is_legal_move32(mv));
}
#[test]
fn test_move32_metadata_classifies_capture_drop_and_promotion() {
let start = crate::board::hirate_position();
let quiet = start.move32_from_move(Move::from_usi("7g7f").expect("valid move"));
let quiet_meta = start.move32_metadata(quiet);
assert_eq!(quiet_meta.piece_after_move(), Piece::B_PAWN);
assert!(!quiet_meta.is_capture());
assert!(!quiet_meta.is_drop());
assert!(!quiet_meta.is_promotion());
assert_eq!(quiet_meta.from_to_index(), quiet.from_to_index());
let capture_pos = crate::board::position_from_sfen(
"lnsgkgsnl/1r5b1/ppppppppp/9/4p4/4P4/PPPPPPPPP/1B5R1/LNSGKGSNL b - 1",
)
.expect("valid sfen");
let capture = capture_pos.move32_from_move(Move::from_usi("5f5e").expect("valid move"));
let capture_meta = capture_pos.classify_move32(capture);
assert!(capture_meta.is_capture());
assert_eq!(capture_meta.captured_piece(), Piece::W_PAWN);
assert_eq!(capture_meta.captured_piece_type(), PieceType::PAWN);
assert_eq!(capture_meta.from_to_index(), capture.from_to_index());
let drop_pos = crate::board::position_from_sfen(
"lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPP1PPPP/1B5R1/LNSGKGSNL b P 1",
)
.expect("valid sfen");
let drop = Move32::drop(PieceType::PAWN, "5e".parse().expect("valid square"), Color::BLACK);
let drop_meta = drop_pos.move32_metadata(drop);
assert!(drop_meta.is_drop());
assert_eq!(drop_meta.piece_after_move(), Piece::B_PAWN);
assert!(!drop_meta.is_capture());
assert_eq!(drop_meta.from_to_index(), drop.from_to_index());
let promo_pos = crate::board::position_from_sfen(
"lnsgkgsnl/1r5b1/2R6/ppppppppp/9/9/PPPPPPPPP/1B7/LNSGKGSNL b - 1",
)
.expect("valid sfen");
let promo = promo_pos.move32_from_move(Move::from_usi("7c7b+").expect("valid move"));
let promo_meta = promo_pos.move32_metadata(promo);
assert!(promo_meta.is_promotion());
assert_eq!(promo_meta.piece_after_move(), Piece::B_DRAGON);
assert_eq!(promo_meta.from_to_index(), promo.from_to_index());
}