diff --git a/src/.rules.rs.swp b/src/.rules.rs.swp deleted file mode 100644 index 72d6200..0000000 Binary files a/src/.rules.rs.swp and /dev/null differ diff --git a/src/board.rs b/src/board.rs index 6d06e48..323a1ec 100644 --- a/src/board.rs +++ b/src/board.rs @@ -10,8 +10,9 @@ pub const SQ_Q: u8 = 0b00010000; pub const SQ_K: u8 = 0b00100000; // Piece color flags. -pub const SQ_WH: u8 = 0b01000000; -pub const SQ_BL: u8 = 0b10000000; +pub const SQ_WH: u8 = 0b01000000; +pub const SQ_BL: u8 = 0b10000000; +pub const SQ_COLOR_MASK: u8 = 0b11000000; // Piece flags helpers. 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] 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_MAX: i8 = 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. /// /// `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] pub fn pos(s: &str) -> Pos { let chars = s.as_bytes(); @@ -86,31 +94,69 @@ pub fn new_empty() -> Board { } #[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] } #[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; } #[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); } #[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. 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) { for r in (0..8).rev() { let mut rank = String::with_capacity(8); for f in 0..8 { - let s = get_square(board, (f, r)); + let s = get_square(board, &(f, r)); let piece = if is_piece(s, SQ_P) { 'p' } else if is_piece(s, SQ_B) { 'b' } @@ -130,6 +176,12 @@ pub fn draw(board: &Board) { mod tests { use super::*; + #[test] + fn test_opposite() { + assert_eq!(opposite(SQ_WH), SQ_BL); + assert_eq!(opposite(SQ_BL), SQ_WH); + } + #[test] fn test_pos() { assert_eq!(pos("a1"), (0, 0)); @@ -142,24 +194,54 @@ mod tests { #[test] fn test_get_square() { let b = new(); - 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("a3")), SQ_E); + 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("a3")), SQ_E); - 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("a7")), SQ_BL_P); + 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("d8")), SQ_BL_Q); - 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("d1")), SQ_WH_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("e8")), SQ_BL_K); } #[test] fn test_is_empty() { let b = new(); - assert_eq!(is_empty(&b, pos("a1")), false); - assert_eq!(is_empty(&b, pos("a2")), false); - assert_eq!(is_empty(&b, pos("a3")), true); + assert_eq!(is_empty(&b, &pos("a1")), false); + assert_eq!(is_empty(&b, &pos("a2")), false); + 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); } } diff --git a/src/rules.rs b/src/rules.rs index 8aff838..54a5767 100644 --- a/src/rules.rs +++ b/src/rules.rs @@ -2,34 +2,38 @@ use crate::board::*; -/// Get a list of legal moves for all pieces of either white or black. -pub fn get_legal_player_moves(board: &Board, color: u8) -> Vec { +/// Get a list of moves for all pieces of either white or black. +pub fn get_player_moves(board: &Board, color: u8) -> Vec { let mut moves = vec!(); for r in 0..8 { for f in 0..8 { - if is_color(get_square(board, (f, r)), color) { - moves.append(&mut get_legal_piece_moves(board, (f, r))); + let p = (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 } -/// Get a list of legal moves for the piece at position `at`. -pub fn get_legal_piece_moves(board: &Board, at: Pos) -> Vec { +/// Get a list of moves for the piece at position `at`. +pub fn get_piece_moves(board: &Board, at: &Pos) -> Vec { 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_B) => get_legal_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_R) => get_legal_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_K) => get_legal_king_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_bishop_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_rook_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_king_moves(board, at, p), _ => vec!(), } } -fn get_legal_pawn_moves(board: &Board, at: Pos, piece: u8) -> Vec { - let (f, r) = at; +fn get_pawn_moves(board: &Board, at: &Pos, piece: u8) -> Vec { + let (f, r) = *at; let mut moves = vec!(); let movement: i8 = if is_white(piece) { 1 } else { -1 }; // Check 1 or 2 square forward. @@ -43,22 +47,22 @@ fn get_legal_pawn_moves(board: &Board, at: Pos, piece: u8) -> Vec { return moves } let forward: Pos = (f, forward_r); - if is_empty(board, forward) { - moves.push((at, forward)) + if is_empty(board, &forward) { + moves.push((*at, forward)) } // Check diagonals for pieces to attack. if i == 1 { let df = f - 1; if df >= POS_MIN { 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); } } let df = f + 1; if df <= POS_MAX { 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); } } @@ -68,7 +72,7 @@ fn get_legal_pawn_moves(board: &Board, at: Pos, piece: u8) -> Vec { moves } -fn get_legal_bishop_moves(board: &Board, at: Pos, piece: u8) -> Vec { +fn get_bishop_moves(board: &Board, at: &Pos, piece: u8) -> Vec { let (f, r) = at; let mut sight = [true; 4]; // Store diagonals where a piece blocks sight. let mut moves = vec!(); @@ -81,10 +85,10 @@ fn get_legal_bishop_moves(board: &Board, at: Pos, piece: u8) -> Vec { if !is_valid_pos(p) { continue } - if is_empty(board, p) { - moves.push((at, p)); + if is_empty(board, &p) { + moves.push((*at, p)); } 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); } sight[dir] = false; // Stop looking in that direction. @@ -94,7 +98,7 @@ fn get_legal_bishop_moves(board: &Board, at: Pos, piece: u8) -> Vec { moves } -fn get_legal_knight_moves(board: &Board, at: Pos, piece: u8) -> Vec { +fn get_knight_moves(board: &Board, at: &Pos, piece: u8) -> Vec { let (f, r) = at; let mut moves = vec!(); 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 { if !is_valid_pos(p) { continue } - if is_empty(board, p) { - moves.push((at, p)); - } else if let Some(m) = move_on_enemy(piece, at, get_square(board, p), p) { + if is_empty(board, &p) { + moves.push((*at, p)); + } else if let Some(m) = move_on_enemy(piece, at, get_square(board, &p), &p) { moves.push(m); } } moves } -fn get_legal_rook_moves(board: &Board, at: Pos, piece: u8) -> Vec { +fn get_rook_moves(board: &Board, at: &Pos, piece: u8) -> Vec { let (f, r) = at; let mut moves = vec!(); 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 { if !is_valid_pos(p) { continue } - if is_empty(board, p) { - moves.push((at, p)); + if is_empty(board, &p) { + moves.push((*at, p)); } 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); } sight[dir] = false; // Stop looking in that direction. @@ -137,15 +141,15 @@ fn get_legal_rook_moves(board: &Board, at: Pos, piece: u8) -> Vec { moves } -fn get_legal_queen_moves(board: &Board, at: Pos, piece: u8) -> Vec { +fn get_queen_moves(board: &Board, at: &Pos, piece: u8) -> Vec { let mut moves = vec!(); // 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_legal_rook_moves(board, at, piece)); + moves.append(&mut get_bishop_moves(board, at, piece)); + moves.append(&mut get_rook_moves(board, at, piece)); moves } -fn get_legal_king_moves(board: &Board, at: Pos, piece: u8) -> Vec { +fn get_king_moves(board: &Board, at: &Pos, piece: u8) -> Vec { let (f, r) = at; let mut moves = vec!(); 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 { if !is_valid_pos(p) { continue } - if is_empty(board, p) { - moves.push((at, p)); - } else if let Some(m) = move_on_enemy(piece, at, get_square(board, p), p) { + if is_empty(board, &p) { + moves.push((*at, p)); + } else if let Some(m) = move_on_enemy(piece, at, get_square(board, &p), &p) { moves.push(m); } } @@ -164,70 +168,100 @@ fn get_legal_king_moves(board: &Board, at: Pos, piece: u8) -> Vec { } /// 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 { - if (is_white(piece1) && is_black(piece2)) || (is_black(piece1) && is_white(piece2)) { - Some((pos1, pos2)) +fn move_on_enemy(piece1: u8, pos1: &Pos, piece2: u8, pos2: &Pos) -> Option { + let color1 = get_color(piece1); + if is_color(piece2, opposite(color1)) { + Some((*pos1, *pos2)) } else { 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) -> Vec { + 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)] mod tests { use super::*; #[test] - fn test_get_legal_player_moves() { + fn test_get_player_moves() { let b = new(); // 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); } #[test] - fn test_get_legal_pawn_moves() { + fn test_get_pawn_moves() { let mut b = new_empty(); // 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); - let moves = get_legal_piece_moves(&b, pos("d3")); + set_square(&mut b, &pos("d3"), SQ_WH_P); + let moves = get_piece_moves(&b, &pos("d3")); 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. - set_square(&mut b, pos("e2"), SQ_WH_P); - let moves = get_legal_piece_moves(&b, pos("e2")); + set_square(&mut b, &pos("e2"), SQ_WH_P); + let moves = get_piece_moves(&b, &pos("e2")); assert_eq!(moves.len(), 2); assert!(moves.contains( &(pos("e2"), pos("e3")) )); assert!(moves.contains( &(pos("e2"), pos("e4")) )); // Check that a pawn cannot move forward if a piece is blocking its path. // 1. black pawn 2 square forward: - set_square(&mut b, pos("e4"), SQ_BL_P); - let moves = get_legal_piece_moves(&b, pos("e2")); + set_square(&mut b, &pos("e4"), SQ_BL_P); + let moves = get_piece_moves(&b, &pos("e2")); assert!(moves.len() == 1 && moves.contains( &(pos("e2"), pos("e3")) )); // 2. black pawn 1 square forward: - set_square(&mut b, pos("e3"), SQ_BL_P); - let moves = get_legal_piece_moves(&b, pos("e2")); + set_square(&mut b, &pos("e3"), SQ_BL_P); + let moves = get_piece_moves(&b, &pos("e2")); assert_eq!(moves.len(), 0); // Check that a pawn can take a piece diagonally. - set_square(&mut b, pos("f3"), SQ_BL_P); - let moves = get_legal_piece_moves(&b, pos("e2")); + set_square(&mut b, &pos("f3"), SQ_BL_P); + let moves = get_piece_moves(&b, &pos("e2")); assert!(moves.len() == 1 && moves.contains( &(pos("e2"), pos("f3")) )); - set_square(&mut b, pos("d3"), SQ_BL_P); - let moves = get_legal_piece_moves(&b, pos("e2")); + set_square(&mut b, &pos("d3"), SQ_BL_P); + let moves = get_piece_moves(&b, &pos("e2")); assert_eq!(moves.len(), 2); assert!(moves.contains( &(pos("e2"), pos("f3")) )); assert!(moves.contains( &(pos("e2"), pos("d3")) )); } #[test] - fn test_get_legal_bishop_moves() { + fn test_get_bishop_moves() { let mut b = new_empty(); // A bishop has maximum range when it's in a center square. - set_square(&mut b, pos("d4"), SQ_WH_B); - let moves = get_legal_piece_moves(&b, pos("d4")); + set_square(&mut b, &pos("d4"), SQ_WH_B); + let moves = get_piece_moves(&b, &pos("d4")); assert_eq!(moves.len(), 13); // Going top-right. assert!(moves.contains( &(pos("d4"), pos("e5")) )); @@ -248,64 +282,90 @@ mod tests { assert!(moves.contains( &(pos("d4"), pos("a7")) )); // When blocking sight to one square with friendly piece, lose 2 moves. - set_square(&mut b, pos("b2"), SQ_WH_P); - assert_eq!(get_legal_piece_moves(&b, pos("d4")).len(), 11); + set_square(&mut b, &pos("b2"), SQ_WH_P); + assert_eq!(get_piece_moves(&b, &pos("d4")).len(), 11); // When blocking sight to one square with enemy piece, lose only 1 move. - set_square(&mut b, pos("b2"), SQ_BL_P); - assert_eq!(get_legal_piece_moves(&b, pos("d4")).len(), 12); + set_square(&mut b, &pos("b2"), SQ_BL_P); + assert_eq!(get_piece_moves(&b, &pos("d4")).len(), 12); } #[test] - fn test_get_legal_knight_moves() { + fn test_get_knight_moves() { 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 // 8 moves. - set_square(&mut b, pos("d4"), SQ_WH_N); - assert_eq!(get_legal_piece_moves(&b, pos("d4")).len(), 8); + set_square(&mut b, &pos("d4"), SQ_WH_N); + assert_eq!(get_piece_moves(&b, &pos("d4")).len(), 8); // If on a side if has only 4 moves. - set_square(&mut b, pos("a4"), SQ_WH_N); - assert_eq!(get_legal_piece_moves(&b, pos("a4")).len(), 4); + set_square(&mut b, &pos("a4"), SQ_WH_N); + assert_eq!(get_piece_moves(&b, &pos("a4")).len(), 4); // And in a corner, only 2 moves. - set_square(&mut b, pos("a1"), SQ_WH_N); - assert_eq!(get_legal_piece_moves(&b, pos("a1")).len(), 2); + set_square(&mut b, &pos("a1"), SQ_WH_N); + assert_eq!(get_piece_moves(&b, &pos("a1")).len(), 2); // Add 2 friendly pieces and it is totally blocked. - set_square(&mut b, pos("b3"), SQ_WH_P); - set_square(&mut b, pos("c2"), SQ_WH_P); - assert_eq!(get_legal_piece_moves(&b, pos("a1")).len(), 0); + set_square(&mut b, &pos("b3"), SQ_WH_P); + set_square(&mut b, &pos("c2"), SQ_WH_P); + assert_eq!(get_piece_moves(&b, &pos("a1")).len(), 0); } #[test] - fn test_get_legal_rook_moves() { + fn test_get_rook_moves() { let mut b = new_empty(); - set_square(&mut b, pos("d4"), SQ_WH_R); - assert_eq!(get_legal_piece_moves(&b, pos("d4")).len(), 14); - set_square(&mut b, pos("d6"), SQ_BL_P); - assert_eq!(get_legal_piece_moves(&b, pos("d4")).len(), 12); - set_square(&mut b, pos("d6"), SQ_WH_P); - assert_eq!(get_legal_piece_moves(&b, pos("d4")).len(), 11); + set_square(&mut b, &pos("d4"), SQ_WH_R); + assert_eq!(get_piece_moves(&b, &pos("d4")).len(), 14); + set_square(&mut b, &pos("d6"), SQ_BL_P); + assert_eq!(get_piece_moves(&b, &pos("d4")).len(), 12); + set_square(&mut b, &pos("d6"), SQ_WH_P); + assert_eq!(get_piece_moves(&b, &pos("d4")).len(), 11); } #[test] - fn test_get_legal_queen_moves() { + fn test_get_queen_moves() { let mut b = new_empty(); - set_square(&mut b, pos("d4"), SQ_WH_Q); - assert_eq!(get_legal_piece_moves(&b, pos("d4")).len(), 14 + 13); // Bishop + rook moves. + set_square(&mut b, &pos("d4"), SQ_WH_Q); + assert_eq!(get_piece_moves(&b, &pos("d4")).len(), 14 + 13); // Bishop + rook moves. } #[test] - fn test_get_legal_king_moves() { + fn test_get_king_moves() { let mut b = new_empty(); - set_square(&mut b, pos("d4"), SQ_WH_K); - assert_eq!(get_legal_piece_moves(&b, pos("d4")).len(), 8); - set_square(&mut b, pos("e5"), SQ_WH_P); - assert_eq!(get_legal_piece_moves(&b, pos("d4")).len(), 7); + set_square(&mut b, &pos("d4"), SQ_WH_K); + assert_eq!(get_piece_moves(&b, &pos("d4")).len(), 8); + set_square(&mut b, &pos("e5"), SQ_WH_P); + 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"))); } }