rules: find basic illegal moves
This commit is contained in:
parent
ce16748258
commit
2a4f46e5aa
Binary file not shown.
122
src/board.rs
122
src/board.rs
|
@ -10,8 +10,9 @@ pub const SQ_Q: u8 = 0b00010000;
|
||||||
pub const SQ_K: u8 = 0b00100000;
|
pub const SQ_K: u8 = 0b00100000;
|
||||||
|
|
||||||
// Piece color flags.
|
// Piece color flags.
|
||||||
pub const SQ_WH: u8 = 0b01000000;
|
pub const SQ_WH: u8 = 0b01000000;
|
||||||
pub const SQ_BL: u8 = 0b10000000;
|
pub const SQ_BL: u8 = 0b10000000;
|
||||||
|
pub const SQ_COLOR_MASK: u8 = 0b11000000;
|
||||||
|
|
||||||
// Piece flags helpers.
|
// Piece flags helpers.
|
||||||
pub const SQ_WH_P: u8 = SQ_WH|SQ_P;
|
pub const SQ_WH_P: u8 = SQ_WH|SQ_P;
|
||||||
|
@ -40,6 +41,13 @@ pub fn is_white(square: u8) -> bool { is_color(square, SQ_WH) }
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_black(square: u8) -> bool { is_color(square, SQ_BL) }
|
pub fn is_black(square: u8) -> bool { is_color(square, SQ_BL) }
|
||||||
|
|
||||||
|
/// Get piece color.
|
||||||
|
#[inline]
|
||||||
|
pub fn get_color(square: u8) -> u8 { square & SQ_COLOR_MASK }
|
||||||
|
/// Get opposite color.
|
||||||
|
#[inline]
|
||||||
|
pub fn opposite(color: u8) -> u8 { color ^ SQ_COLOR_MASK }
|
||||||
|
|
||||||
pub const POS_MIN: i8 = 0;
|
pub const POS_MIN: i8 = 0;
|
||||||
pub const POS_MAX: i8 = 7;
|
pub const POS_MAX: i8 = 7;
|
||||||
/// Coords (file, rank) of a square on a board, both components are in [0, 7].
|
/// Coords (file, rank) of a square on a board, both components are in [0, 7].
|
||||||
|
@ -54,7 +62,7 @@ pub fn is_valid_pos(pos: Pos) -> bool { is_valid_pos_c(pos.0) && is_valid_pos_c(
|
||||||
/// Convert string coordinates to Pos.
|
/// Convert string coordinates to Pos.
|
||||||
///
|
///
|
||||||
/// `s` has to be valid UTF8, or the very least ASCII because chars
|
/// `s` has to be valid UTF8, or the very least ASCII because chars
|
||||||
/// are interpreted as raw bytes.
|
/// are interpreted as raw bytes, and lowercase.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn pos(s: &str) -> Pos {
|
pub fn pos(s: &str) -> Pos {
|
||||||
let chars = s.as_bytes();
|
let chars = s.as_bytes();
|
||||||
|
@ -86,31 +94,69 @@ pub fn new_empty() -> Board {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_square(board: &Board, coords: Pos) -> u8 {
|
pub fn get_square(board: &Board, coords: &Pos) -> u8 {
|
||||||
board[(coords.0 * 8 + coords.1) as usize]
|
board[(coords.0 * 8 + coords.1) as usize]
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_square(board: &mut Board, coords: Pos, piece: u8) {
|
pub fn set_square(board: &mut Board, coords: &Pos, piece: u8) {
|
||||||
board[(coords.0 * 8 + coords.1) as usize] = piece;
|
board[(coords.0 * 8 + coords.1) as usize] = piece;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn clear_square(board: &mut Board, coords: Pos) {
|
pub fn clear_square(board: &mut Board, coords: &Pos) {
|
||||||
set_square(board, coords, SQ_E);
|
set_square(board, coords, SQ_E);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_empty(board: &Board, coords: Pos) -> bool { get_square(board, coords) == SQ_E }
|
pub fn is_empty(board: &Board, coords: &Pos) -> bool { get_square(board, coords) == SQ_E }
|
||||||
|
|
||||||
|
/// Count number of pieces on board
|
||||||
|
pub fn num_pieces(board: &Board) -> u8 {
|
||||||
|
let mut count = 0;
|
||||||
|
for i in board.iter() {
|
||||||
|
if *i != SQ_E {
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
count
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find the king of `color`.
|
||||||
|
pub fn find_king(board: &Board, color: u8) -> Pos {
|
||||||
|
for f in 0..8 {
|
||||||
|
for r in 0..8 {
|
||||||
|
let s = get_square(board, &(f, r));
|
||||||
|
if is_color(s, color) && is_piece(s, SQ_K) {
|
||||||
|
return (f, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
eprintln!("No king on board!");
|
||||||
|
(0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
/// A movement, with before/after positions.
|
/// A movement, with before/after positions.
|
||||||
pub type Move = (Pos, Pos);
|
pub type Move = (Pos, Pos);
|
||||||
|
|
||||||
|
/// Apply a move `m` to a copy of `board`.
|
||||||
|
pub fn apply(board: &Board, m: &Move) -> Board {
|
||||||
|
let mut new_board = board.clone();
|
||||||
|
apply_into(&mut new_board, m);
|
||||||
|
new_board
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Apply a move `m` into `board`.
|
||||||
|
pub fn apply_into(board: &mut Board, m: &Move) {
|
||||||
|
set_square(board, &m.1, get_square(board, &m.0));
|
||||||
|
clear_square(board, &m.0)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn draw(board: &Board) {
|
pub fn draw(board: &Board) {
|
||||||
for r in (0..8).rev() {
|
for r in (0..8).rev() {
|
||||||
let mut rank = String::with_capacity(8);
|
let mut rank = String::with_capacity(8);
|
||||||
for f in 0..8 {
|
for f in 0..8 {
|
||||||
let s = get_square(board, (f, r));
|
let s = get_square(board, &(f, r));
|
||||||
let piece =
|
let piece =
|
||||||
if is_piece(s, SQ_P) { 'p' }
|
if is_piece(s, SQ_P) { 'p' }
|
||||||
else if is_piece(s, SQ_B) { 'b' }
|
else if is_piece(s, SQ_B) { 'b' }
|
||||||
|
@ -130,6 +176,12 @@ pub fn draw(board: &Board) {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_opposite() {
|
||||||
|
assert_eq!(opposite(SQ_WH), SQ_BL);
|
||||||
|
assert_eq!(opposite(SQ_BL), SQ_WH);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_pos() {
|
fn test_pos() {
|
||||||
assert_eq!(pos("a1"), (0, 0));
|
assert_eq!(pos("a1"), (0, 0));
|
||||||
|
@ -142,24 +194,54 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_square() {
|
fn test_get_square() {
|
||||||
let b = new();
|
let b = new();
|
||||||
assert_eq!(get_square(&b, pos("a1")), SQ_WH_R);
|
assert_eq!(get_square(&b, &pos("a1")), SQ_WH_R);
|
||||||
assert_eq!(get_square(&b, pos("a2")), SQ_WH_P);
|
assert_eq!(get_square(&b, &pos("a2")), SQ_WH_P);
|
||||||
assert_eq!(get_square(&b, pos("a3")), SQ_E);
|
assert_eq!(get_square(&b, &pos("a3")), SQ_E);
|
||||||
|
|
||||||
assert_eq!(get_square(&b, pos("a7")), SQ_BL_P);
|
assert_eq!(get_square(&b, &pos("a7")), SQ_BL_P);
|
||||||
assert_eq!(get_square(&b, pos("a8")), SQ_BL_R);
|
assert_eq!(get_square(&b, &pos("a8")), SQ_BL_R);
|
||||||
|
|
||||||
assert_eq!(get_square(&b, pos("d1")), SQ_WH_Q);
|
assert_eq!(get_square(&b, &pos("d1")), SQ_WH_Q);
|
||||||
assert_eq!(get_square(&b, pos("d8")), SQ_BL_Q);
|
assert_eq!(get_square(&b, &pos("d8")), SQ_BL_Q);
|
||||||
assert_eq!(get_square(&b, pos("e1")), SQ_WH_K);
|
assert_eq!(get_square(&b, &pos("e1")), SQ_WH_K);
|
||||||
assert_eq!(get_square(&b, pos("e8")), SQ_BL_K);
|
assert_eq!(get_square(&b, &pos("e8")), SQ_BL_K);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_is_empty() {
|
fn test_is_empty() {
|
||||||
let b = new();
|
let b = new();
|
||||||
assert_eq!(is_empty(&b, pos("a1")), false);
|
assert_eq!(is_empty(&b, &pos("a1")), false);
|
||||||
assert_eq!(is_empty(&b, pos("a2")), false);
|
assert_eq!(is_empty(&b, &pos("a2")), false);
|
||||||
assert_eq!(is_empty(&b, pos("a3")), true);
|
assert_eq!(is_empty(&b, &pos("a3")), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_num_pieces() {
|
||||||
|
assert_eq!(num_pieces(&new_empty()), 0);
|
||||||
|
assert_eq!(num_pieces(&new()), 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_find_king() {
|
||||||
|
let b = new();
|
||||||
|
assert_eq!(find_king(&b, SQ_WH), pos("e1"));
|
||||||
|
assert_eq!(find_king(&b, SQ_BL), pos("e8"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_apply_into() {
|
||||||
|
let mut b = new_empty();
|
||||||
|
// Put 2 enemy knights on board.
|
||||||
|
set_square(&mut b, &pos("d4"), SQ_WH_N);
|
||||||
|
set_square(&mut b, &pos("f4"), SQ_BL_N);
|
||||||
|
// Move white knight in a position attacked by black knight.
|
||||||
|
apply_into(&mut b, &((pos("d4"), pos("e6"))));
|
||||||
|
assert_eq!(get_square(&b, &pos("d4")), SQ_E);
|
||||||
|
assert_eq!(get_square(&b, &pos("e6")), SQ_WH_N);
|
||||||
|
assert_eq!(num_pieces(&b), 2);
|
||||||
|
// Sack it with black knight
|
||||||
|
apply_into(&mut b, &((pos("f4"), pos("e6"))));
|
||||||
|
assert_eq!(get_square(&b, &pos("e6")), SQ_BL_N);
|
||||||
|
assert_eq!(num_pieces(&b), 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
234
src/rules.rs
234
src/rules.rs
|
@ -2,34 +2,38 @@
|
||||||
|
|
||||||
use crate::board::*;
|
use crate::board::*;
|
||||||
|
|
||||||
/// Get a list of legal moves for all pieces of either white or black.
|
/// Get a list of moves for all pieces of either white or black.
|
||||||
pub fn get_legal_player_moves(board: &Board, color: u8) -> Vec<Move> {
|
pub fn get_player_moves(board: &Board, color: u8) -> Vec<Move> {
|
||||||
let mut moves = vec!();
|
let mut moves = vec!();
|
||||||
for r in 0..8 {
|
for r in 0..8 {
|
||||||
for f in 0..8 {
|
for f in 0..8 {
|
||||||
if is_color(get_square(board, (f, r)), color) {
|
let p = (f, r);
|
||||||
moves.append(&mut get_legal_piece_moves(board, (f, r)));
|
if is_empty(board, &p) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if is_color(get_square(board, &p), color) {
|
||||||
|
moves.append(&mut get_piece_moves(board, &p));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
moves
|
moves
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a list of legal moves for the piece at position `at`.
|
/// Get a list of moves for the piece at position `at`.
|
||||||
pub fn get_legal_piece_moves(board: &Board, at: Pos) -> Vec<Move> {
|
pub fn get_piece_moves(board: &Board, at: &Pos) -> Vec<Move> {
|
||||||
match get_square(board, at) {
|
match get_square(board, at) {
|
||||||
p if is_piece(p, SQ_P) => get_legal_pawn_moves(board, at, p),
|
p if is_piece(p, SQ_P) => get_pawn_moves(board, at, p),
|
||||||
p if is_piece(p, SQ_B) => get_legal_bishop_moves(board, at, p),
|
p if is_piece(p, SQ_B) => get_bishop_moves(board, at, p),
|
||||||
p if is_piece(p, SQ_N) => get_legal_knight_moves(board, at, p),
|
p if is_piece(p, SQ_N) => get_knight_moves(board, at, p),
|
||||||
p if is_piece(p, SQ_R) => get_legal_rook_moves(board, at, p),
|
p if is_piece(p, SQ_R) => get_rook_moves(board, at, p),
|
||||||
p if is_piece(p, SQ_Q) => get_legal_queen_moves(board, at, p),
|
p if is_piece(p, SQ_Q) => get_queen_moves(board, at, p),
|
||||||
p if is_piece(p, SQ_K) => get_legal_king_moves(board, at, p),
|
p if is_piece(p, SQ_K) => get_king_moves(board, at, p),
|
||||||
_ => vec!(),
|
_ => vec!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_legal_pawn_moves(board: &Board, at: Pos, piece: u8) -> Vec<Move> {
|
fn get_pawn_moves(board: &Board, at: &Pos, piece: u8) -> Vec<Move> {
|
||||||
let (f, r) = at;
|
let (f, r) = *at;
|
||||||
let mut moves = vec!();
|
let mut moves = vec!();
|
||||||
let movement: i8 = if is_white(piece) { 1 } else { -1 };
|
let movement: i8 = if is_white(piece) { 1 } else { -1 };
|
||||||
// Check 1 or 2 square forward.
|
// Check 1 or 2 square forward.
|
||||||
|
@ -43,22 +47,22 @@ fn get_legal_pawn_moves(board: &Board, at: Pos, piece: u8) -> Vec<Move> {
|
||||||
return moves
|
return moves
|
||||||
}
|
}
|
||||||
let forward: Pos = (f, forward_r);
|
let forward: Pos = (f, forward_r);
|
||||||
if is_empty(board, forward) {
|
if is_empty(board, &forward) {
|
||||||
moves.push((at, forward))
|
moves.push((*at, forward))
|
||||||
}
|
}
|
||||||
// Check diagonals for pieces to attack.
|
// Check diagonals for pieces to attack.
|
||||||
if i == 1 {
|
if i == 1 {
|
||||||
let df = f - 1;
|
let df = f - 1;
|
||||||
if df >= POS_MIN {
|
if df >= POS_MIN {
|
||||||
let diag: Pos = (df, forward_r);
|
let diag: Pos = (df, forward_r);
|
||||||
if let Some(m) = move_on_enemy(piece, at, get_square(board, diag), diag) {
|
if let Some(m) = move_on_enemy(piece, at, get_square(board, &diag), &diag) {
|
||||||
moves.push(m);
|
moves.push(m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let df = f + 1;
|
let df = f + 1;
|
||||||
if df <= POS_MAX {
|
if df <= POS_MAX {
|
||||||
let diag: Pos = (df, forward_r);
|
let diag: Pos = (df, forward_r);
|
||||||
if let Some(m) = move_on_enemy(piece, at, get_square(board, diag), diag) {
|
if let Some(m) = move_on_enemy(piece, at, get_square(board, &diag), &diag) {
|
||||||
moves.push(m);
|
moves.push(m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,7 +72,7 @@ fn get_legal_pawn_moves(board: &Board, at: Pos, piece: u8) -> Vec<Move> {
|
||||||
moves
|
moves
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_legal_bishop_moves(board: &Board, at: Pos, piece: u8) -> Vec<Move> {
|
fn get_bishop_moves(board: &Board, at: &Pos, piece: u8) -> Vec<Move> {
|
||||||
let (f, r) = at;
|
let (f, r) = at;
|
||||||
let mut sight = [true; 4]; // Store diagonals where a piece blocks sight.
|
let mut sight = [true; 4]; // Store diagonals where a piece blocks sight.
|
||||||
let mut moves = vec!();
|
let mut moves = vec!();
|
||||||
|
@ -81,10 +85,10 @@ fn get_legal_bishop_moves(board: &Board, at: Pos, piece: u8) -> Vec<Move> {
|
||||||
if !is_valid_pos(p) {
|
if !is_valid_pos(p) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if is_empty(board, p) {
|
if is_empty(board, &p) {
|
||||||
moves.push((at, p));
|
moves.push((*at, p));
|
||||||
} else {
|
} else {
|
||||||
if let Some(m) = move_on_enemy(piece, at, get_square(board, p), p) {
|
if let Some(m) = move_on_enemy(piece, at, get_square(board, &p), &p) {
|
||||||
moves.push(m);
|
moves.push(m);
|
||||||
}
|
}
|
||||||
sight[dir] = false; // Stop looking in that direction.
|
sight[dir] = false; // Stop looking in that direction.
|
||||||
|
@ -94,7 +98,7 @@ fn get_legal_bishop_moves(board: &Board, at: Pos, piece: u8) -> Vec<Move> {
|
||||||
moves
|
moves
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_legal_knight_moves(board: &Board, at: Pos, piece: u8) -> Vec<Move> {
|
fn get_knight_moves(board: &Board, at: &Pos, piece: u8) -> Vec<Move> {
|
||||||
let (f, r) = at;
|
let (f, r) = at;
|
||||||
let mut moves = vec!();
|
let mut moves = vec!();
|
||||||
for offset in [(1, 2), (2, 1), (2, -1), (1, -2), (-1, -2), (-2, -1), (-2, 1), (-1, 2)].iter() {
|
for offset in [(1, 2), (2, 1), (2, -1), (1, -2), (-1, -2), (-2, -1), (-2, 1), (-1, 2)].iter() {
|
||||||
|
@ -102,16 +106,16 @@ fn get_legal_knight_moves(board: &Board, at: Pos, piece: u8) -> Vec<Move> {
|
||||||
if !is_valid_pos(p) {
|
if !is_valid_pos(p) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if is_empty(board, p) {
|
if is_empty(board, &p) {
|
||||||
moves.push((at, p));
|
moves.push((*at, p));
|
||||||
} else if let Some(m) = move_on_enemy(piece, at, get_square(board, p), p) {
|
} else if let Some(m) = move_on_enemy(piece, at, get_square(board, &p), &p) {
|
||||||
moves.push(m);
|
moves.push(m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
moves
|
moves
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_legal_rook_moves(board: &Board, at: Pos, piece: u8) -> Vec<Move> {
|
fn get_rook_moves(board: &Board, at: &Pos, piece: u8) -> Vec<Move> {
|
||||||
let (f, r) = at;
|
let (f, r) = at;
|
||||||
let mut moves = vec!();
|
let mut moves = vec!();
|
||||||
let mut sight = [true; 4]; // Store lines where a piece blocks sight.
|
let mut sight = [true; 4]; // Store lines where a piece blocks sight.
|
||||||
|
@ -124,10 +128,10 @@ fn get_legal_rook_moves(board: &Board, at: Pos, piece: u8) -> Vec<Move> {
|
||||||
if !is_valid_pos(p) {
|
if !is_valid_pos(p) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if is_empty(board, p) {
|
if is_empty(board, &p) {
|
||||||
moves.push((at, p));
|
moves.push((*at, p));
|
||||||
} else {
|
} else {
|
||||||
if let Some(m) = move_on_enemy(piece, at, get_square(board, p), p) {
|
if let Some(m) = move_on_enemy(piece, at, get_square(board, &p), &p) {
|
||||||
moves.push(m);
|
moves.push(m);
|
||||||
}
|
}
|
||||||
sight[dir] = false; // Stop looking in that direction.
|
sight[dir] = false; // Stop looking in that direction.
|
||||||
|
@ -137,15 +141,15 @@ fn get_legal_rook_moves(board: &Board, at: Pos, piece: u8) -> Vec<Move> {
|
||||||
moves
|
moves
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_legal_queen_moves(board: &Board, at: Pos, piece: u8) -> Vec<Move> {
|
fn get_queen_moves(board: &Board, at: &Pos, piece: u8) -> Vec<Move> {
|
||||||
let mut moves = vec!();
|
let mut moves = vec!();
|
||||||
// Easy way to get queen moves, but may be a bit quicker if everything was rewritten here.
|
// Easy way to get queen moves, but may be a bit quicker if everything was rewritten here.
|
||||||
moves.append(&mut get_legal_bishop_moves(board, at, piece));
|
moves.append(&mut get_bishop_moves(board, at, piece));
|
||||||
moves.append(&mut get_legal_rook_moves(board, at, piece));
|
moves.append(&mut get_rook_moves(board, at, piece));
|
||||||
moves
|
moves
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_legal_king_moves(board: &Board, at: Pos, piece: u8) -> Vec<Move> {
|
fn get_king_moves(board: &Board, at: &Pos, piece: u8) -> Vec<Move> {
|
||||||
let (f, r) = at;
|
let (f, r) = at;
|
||||||
let mut moves = vec!();
|
let mut moves = vec!();
|
||||||
for offset in [(-1, 1), (0, 1), (1, 1), (-1, 0), (1, 0), (-1, -1), (0, -1), (1, -1)].iter() {
|
for offset in [(-1, 1), (0, 1), (1, 1), (-1, 0), (1, 0), (-1, -1), (0, -1), (1, -1)].iter() {
|
||||||
|
@ -153,9 +157,9 @@ fn get_legal_king_moves(board: &Board, at: Pos, piece: u8) -> Vec<Move> {
|
||||||
if !is_valid_pos(p) {
|
if !is_valid_pos(p) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if is_empty(board, p) {
|
if is_empty(board, &p) {
|
||||||
moves.push((at, p));
|
moves.push((*at, p));
|
||||||
} else if let Some(m) = move_on_enemy(piece, at, get_square(board, p), p) {
|
} else if let Some(m) = move_on_enemy(piece, at, get_square(board, &p), &p) {
|
||||||
moves.push(m);
|
moves.push(m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -164,70 +168,100 @@ fn get_legal_king_moves(board: &Board, at: Pos, piece: u8) -> Vec<Move> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a move from pos1 to pos2 if piece1 & piece2 are enemies.
|
/// Return a move from pos1 to pos2 if piece1 & piece2 are enemies.
|
||||||
fn move_on_enemy(piece1: u8, pos1: Pos, piece2: u8, pos2: Pos) -> Option<Move> {
|
fn move_on_enemy(piece1: u8, pos1: &Pos, piece2: u8, pos2: &Pos) -> Option<Move> {
|
||||||
if (is_white(piece1) && is_black(piece2)) || (is_black(piece1) && is_white(piece2)) {
|
let color1 = get_color(piece1);
|
||||||
Some((pos1, pos2))
|
if is_color(piece2, opposite(color1)) {
|
||||||
|
Some((*pos1, *pos2))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return an iterator filtering out illegal moves from given list.
|
||||||
|
///
|
||||||
|
/// Pass color of moving player to avoid checking it for every move.
|
||||||
|
fn filter_illegal_moves(board: &Board, color: u8, moves: Vec<Move>) -> Vec<Move> {
|
||||||
|
let king_p = find_king(board, color);
|
||||||
|
moves.into_iter().filter(|m| {
|
||||||
|
// If king moved, use its new position.
|
||||||
|
let king_p = if m.0 == king_p { m.1 } else { king_p };
|
||||||
|
let new_board = apply(board, m);
|
||||||
|
// Check if the move makes the player king in check.
|
||||||
|
if is_attacked(&new_board, &king_p) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return true if the piece at position `at` is attacked.
|
||||||
|
fn is_attacked(board: &Board, at: &Pos) -> bool {
|
||||||
|
let color = get_color(get_square(board, at));
|
||||||
|
let enemy_moves = get_player_moves(board, opposite(color));
|
||||||
|
for m in enemy_moves.iter() {
|
||||||
|
if *at == m.1 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_legal_player_moves() {
|
fn test_get_player_moves() {
|
||||||
let b = new();
|
let b = new();
|
||||||
// At first move, white has 16 pawn moves and 4 knight moves.
|
// At first move, white has 16 pawn moves and 4 knight moves.
|
||||||
let moves = get_legal_player_moves(&b, SQ_WH);
|
let moves = get_player_moves(&b, SQ_WH);
|
||||||
assert_eq!(moves.len(), 20);
|
assert_eq!(moves.len(), 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_legal_pawn_moves() {
|
fn test_get_pawn_moves() {
|
||||||
let mut b = new_empty();
|
let mut b = new_empty();
|
||||||
|
|
||||||
// Check that a pawn (here white queen's pawn) can move forward if the road is free.
|
// Check that a pawn (here white queen's pawn) can move forward if the road is free.
|
||||||
set_square(&mut b, pos("d3"), SQ_WH_P);
|
set_square(&mut b, &pos("d3"), SQ_WH_P);
|
||||||
let moves = get_legal_piece_moves(&b, pos("d3"));
|
let moves = get_piece_moves(&b, &pos("d3"));
|
||||||
assert!(moves.len() == 1 && moves.contains( &(pos("d3"), pos("d4")) ));
|
assert!(moves.len() == 1 && moves.contains( &(pos("d3"), pos("d4")) ));
|
||||||
|
|
||||||
// Check that a pawn (here white king's pawn) can move 2 square forward on first move.
|
// Check that a pawn (here white king's pawn) can move 2 square forward on first move.
|
||||||
set_square(&mut b, pos("e2"), SQ_WH_P);
|
set_square(&mut b, &pos("e2"), SQ_WH_P);
|
||||||
let moves = get_legal_piece_moves(&b, pos("e2"));
|
let moves = get_piece_moves(&b, &pos("e2"));
|
||||||
assert_eq!(moves.len(), 2);
|
assert_eq!(moves.len(), 2);
|
||||||
assert!(moves.contains( &(pos("e2"), pos("e3")) ));
|
assert!(moves.contains( &(pos("e2"), pos("e3")) ));
|
||||||
assert!(moves.contains( &(pos("e2"), pos("e4")) ));
|
assert!(moves.contains( &(pos("e2"), pos("e4")) ));
|
||||||
|
|
||||||
// Check that a pawn cannot move forward if a piece is blocking its path.
|
// Check that a pawn cannot move forward if a piece is blocking its path.
|
||||||
// 1. black pawn 2 square forward:
|
// 1. black pawn 2 square forward:
|
||||||
set_square(&mut b, pos("e4"), SQ_BL_P);
|
set_square(&mut b, &pos("e4"), SQ_BL_P);
|
||||||
let moves = get_legal_piece_moves(&b, pos("e2"));
|
let moves = get_piece_moves(&b, &pos("e2"));
|
||||||
assert!(moves.len() == 1 && moves.contains( &(pos("e2"), pos("e3")) ));
|
assert!(moves.len() == 1 && moves.contains( &(pos("e2"), pos("e3")) ));
|
||||||
// 2. black pawn 1 square forward:
|
// 2. black pawn 1 square forward:
|
||||||
set_square(&mut b, pos("e3"), SQ_BL_P);
|
set_square(&mut b, &pos("e3"), SQ_BL_P);
|
||||||
let moves = get_legal_piece_moves(&b, pos("e2"));
|
let moves = get_piece_moves(&b, &pos("e2"));
|
||||||
assert_eq!(moves.len(), 0);
|
assert_eq!(moves.len(), 0);
|
||||||
|
|
||||||
// Check that a pawn can take a piece diagonally.
|
// Check that a pawn can take a piece diagonally.
|
||||||
set_square(&mut b, pos("f3"), SQ_BL_P);
|
set_square(&mut b, &pos("f3"), SQ_BL_P);
|
||||||
let moves = get_legal_piece_moves(&b, pos("e2"));
|
let moves = get_piece_moves(&b, &pos("e2"));
|
||||||
assert!(moves.len() == 1 && moves.contains( &(pos("e2"), pos("f3")) ));
|
assert!(moves.len() == 1 && moves.contains( &(pos("e2"), pos("f3")) ));
|
||||||
set_square(&mut b, pos("d3"), SQ_BL_P);
|
set_square(&mut b, &pos("d3"), SQ_BL_P);
|
||||||
let moves = get_legal_piece_moves(&b, pos("e2"));
|
let moves = get_piece_moves(&b, &pos("e2"));
|
||||||
assert_eq!(moves.len(), 2);
|
assert_eq!(moves.len(), 2);
|
||||||
assert!(moves.contains( &(pos("e2"), pos("f3")) ));
|
assert!(moves.contains( &(pos("e2"), pos("f3")) ));
|
||||||
assert!(moves.contains( &(pos("e2"), pos("d3")) ));
|
assert!(moves.contains( &(pos("e2"), pos("d3")) ));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_legal_bishop_moves() {
|
fn test_get_bishop_moves() {
|
||||||
let mut b = new_empty();
|
let mut b = new_empty();
|
||||||
|
|
||||||
// A bishop has maximum range when it's in a center square.
|
// A bishop has maximum range when it's in a center square.
|
||||||
set_square(&mut b, pos("d4"), SQ_WH_B);
|
set_square(&mut b, &pos("d4"), SQ_WH_B);
|
||||||
let moves = get_legal_piece_moves(&b, pos("d4"));
|
let moves = get_piece_moves(&b, &pos("d4"));
|
||||||
assert_eq!(moves.len(), 13);
|
assert_eq!(moves.len(), 13);
|
||||||
// Going top-right.
|
// Going top-right.
|
||||||
assert!(moves.contains( &(pos("d4"), pos("e5")) ));
|
assert!(moves.contains( &(pos("d4"), pos("e5")) ));
|
||||||
|
@ -248,64 +282,90 @@ mod tests {
|
||||||
assert!(moves.contains( &(pos("d4"), pos("a7")) ));
|
assert!(moves.contains( &(pos("d4"), pos("a7")) ));
|
||||||
|
|
||||||
// When blocking sight to one square with friendly piece, lose 2 moves.
|
// When blocking sight to one square with friendly piece, lose 2 moves.
|
||||||
set_square(&mut b, pos("b2"), SQ_WH_P);
|
set_square(&mut b, &pos("b2"), SQ_WH_P);
|
||||||
assert_eq!(get_legal_piece_moves(&b, pos("d4")).len(), 11);
|
assert_eq!(get_piece_moves(&b, &pos("d4")).len(), 11);
|
||||||
|
|
||||||
// When blocking sight to one square with enemy piece, lose only 1 move.
|
// When blocking sight to one square with enemy piece, lose only 1 move.
|
||||||
set_square(&mut b, pos("b2"), SQ_BL_P);
|
set_square(&mut b, &pos("b2"), SQ_BL_P);
|
||||||
assert_eq!(get_legal_piece_moves(&b, pos("d4")).len(), 12);
|
assert_eq!(get_piece_moves(&b, &pos("d4")).len(), 12);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_legal_knight_moves() {
|
fn test_get_knight_moves() {
|
||||||
let mut b = new_empty();
|
let mut b = new_empty();
|
||||||
|
|
||||||
// A knight never has blocked sight; if it's in the center of the board, it can have up to
|
// A knight never has blocked sight; if it's in the center of the board, it can have up to
|
||||||
// 8 moves.
|
// 8 moves.
|
||||||
set_square(&mut b, pos("d4"), SQ_WH_N);
|
set_square(&mut b, &pos("d4"), SQ_WH_N);
|
||||||
assert_eq!(get_legal_piece_moves(&b, pos("d4")).len(), 8);
|
assert_eq!(get_piece_moves(&b, &pos("d4")).len(), 8);
|
||||||
|
|
||||||
// If on a side if has only 4 moves.
|
// If on a side if has only 4 moves.
|
||||||
set_square(&mut b, pos("a4"), SQ_WH_N);
|
set_square(&mut b, &pos("a4"), SQ_WH_N);
|
||||||
assert_eq!(get_legal_piece_moves(&b, pos("a4")).len(), 4);
|
assert_eq!(get_piece_moves(&b, &pos("a4")).len(), 4);
|
||||||
|
|
||||||
// And in a corner, only 2 moves.
|
// And in a corner, only 2 moves.
|
||||||
set_square(&mut b, pos("a1"), SQ_WH_N);
|
set_square(&mut b, &pos("a1"), SQ_WH_N);
|
||||||
assert_eq!(get_legal_piece_moves(&b, pos("a1")).len(), 2);
|
assert_eq!(get_piece_moves(&b, &pos("a1")).len(), 2);
|
||||||
|
|
||||||
// Add 2 friendly pieces and it is totally blocked.
|
// Add 2 friendly pieces and it is totally blocked.
|
||||||
set_square(&mut b, pos("b3"), SQ_WH_P);
|
set_square(&mut b, &pos("b3"), SQ_WH_P);
|
||||||
set_square(&mut b, pos("c2"), SQ_WH_P);
|
set_square(&mut b, &pos("c2"), SQ_WH_P);
|
||||||
assert_eq!(get_legal_piece_moves(&b, pos("a1")).len(), 0);
|
assert_eq!(get_piece_moves(&b, &pos("a1")).len(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_legal_rook_moves() {
|
fn test_get_rook_moves() {
|
||||||
let mut b = new_empty();
|
let mut b = new_empty();
|
||||||
|
|
||||||
set_square(&mut b, pos("d4"), SQ_WH_R);
|
set_square(&mut b, &pos("d4"), SQ_WH_R);
|
||||||
assert_eq!(get_legal_piece_moves(&b, pos("d4")).len(), 14);
|
assert_eq!(get_piece_moves(&b, &pos("d4")).len(), 14);
|
||||||
set_square(&mut b, pos("d6"), SQ_BL_P);
|
set_square(&mut b, &pos("d6"), SQ_BL_P);
|
||||||
assert_eq!(get_legal_piece_moves(&b, pos("d4")).len(), 12);
|
assert_eq!(get_piece_moves(&b, &pos("d4")).len(), 12);
|
||||||
set_square(&mut b, pos("d6"), SQ_WH_P);
|
set_square(&mut b, &pos("d6"), SQ_WH_P);
|
||||||
assert_eq!(get_legal_piece_moves(&b, pos("d4")).len(), 11);
|
assert_eq!(get_piece_moves(&b, &pos("d4")).len(), 11);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_legal_queen_moves() {
|
fn test_get_queen_moves() {
|
||||||
let mut b = new_empty();
|
let mut b = new_empty();
|
||||||
|
|
||||||
set_square(&mut b, pos("d4"), SQ_WH_Q);
|
set_square(&mut b, &pos("d4"), SQ_WH_Q);
|
||||||
assert_eq!(get_legal_piece_moves(&b, pos("d4")).len(), 14 + 13); // Bishop + rook moves.
|
assert_eq!(get_piece_moves(&b, &pos("d4")).len(), 14 + 13); // Bishop + rook moves.
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_legal_king_moves() {
|
fn test_get_king_moves() {
|
||||||
let mut b = new_empty();
|
let mut b = new_empty();
|
||||||
|
|
||||||
set_square(&mut b, pos("d4"), SQ_WH_K);
|
set_square(&mut b, &pos("d4"), SQ_WH_K);
|
||||||
assert_eq!(get_legal_piece_moves(&b, pos("d4")).len(), 8);
|
assert_eq!(get_piece_moves(&b, &pos("d4")).len(), 8);
|
||||||
set_square(&mut b, pos("e5"), SQ_WH_P);
|
set_square(&mut b, &pos("e5"), SQ_WH_P);
|
||||||
assert_eq!(get_legal_piece_moves(&b, pos("d4")).len(), 7);
|
assert_eq!(get_piece_moves(&b, &pos("d4")).len(), 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_filter_illegal_moves() {
|
||||||
|
let mut b = new_empty();
|
||||||
|
|
||||||
|
// Place white's king on first rank.
|
||||||
|
set_square(&mut b, &pos("e1"), SQ_WH_K);
|
||||||
|
// Place black rook in second rank: king can only move left or right.
|
||||||
|
set_square(&mut b, &pos("h2"), SQ_BL_R);
|
||||||
|
let all_wh_moves = get_piece_moves(&b, &pos("e1"));
|
||||||
|
assert_eq!(all_wh_moves.len(), 5);
|
||||||
|
assert_eq!(filter_illegal_moves(&b, SQ_WH, all_wh_moves).len(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_is_attacked() {
|
||||||
|
let mut b = new_empty();
|
||||||
|
|
||||||
|
// Place a black rook in white pawn's file.
|
||||||
|
set_square(&mut b, &pos("d4"), SQ_WH_P);
|
||||||
|
set_square(&mut b, &pos("d6"), SQ_BL_R);
|
||||||
|
assert!(is_attacked(&b, &pos("d4")));
|
||||||
|
// Move the rook on another file, no more attack.
|
||||||
|
apply_into(&mut b, &(pos("d6"), pos("e6")));
|
||||||
|
assert!(!is_attacked(&b, &pos("d4")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue