|
@@ -2,6 +2,7 @@ use bitboard::Bitboard;
|
|
use bitboard::Square;
|
|
use bitboard::Square;
|
|
use bitboard::*;
|
|
use bitboard::*;
|
|
use game::Game;
|
|
use game::Game;
|
|
|
|
+use log::info;
|
|
|
|
|
|
pub type Side = bool;
|
|
pub type Side = bool;
|
|
|
|
|
|
@@ -28,7 +29,7 @@ pub struct SimpleMove {
|
|
pub enum Move {
|
|
pub enum Move {
|
|
Default { mov: SimpleMove, piece_type: PieceType },
|
|
Default { mov: SimpleMove, piece_type: PieceType },
|
|
Castling { side: Side, left: bool },
|
|
Castling { side: Side, left: bool },
|
|
- EnPassant { side: Side, column: u8 },
|
|
|
|
|
|
+ EnPassant { mov: SimpleMove, beaten: Square },
|
|
Promotion { mov: SimpleMove, promote_to: PieceType },
|
|
Promotion { mov: SimpleMove, promote_to: PieceType },
|
|
}
|
|
}
|
|
|
|
|
|
@@ -65,8 +66,25 @@ impl Move {
|
|
Move::Default{ mov, piece_type } => {
|
|
Move::Default{ mov, piece_type } => {
|
|
Self::square_to_string(mov.from) + &Self::square_to_string(mov.to)
|
|
Self::square_to_string(mov.from) + &Self::square_to_string(mov.to)
|
|
},
|
|
},
|
|
- Move::Castling{ side, left } => { "castling not implemented".to_owned() },
|
|
|
|
- Move::EnPassant{ side, column } => { "en passant not implemented".to_owned() },
|
|
|
|
|
|
+ Move::Castling{ side, left } => {
|
|
|
|
+ match (side, left) {
|
|
|
|
+ (&WHITE, false) => {
|
|
|
|
+ Self::square_to_string(3) + &Self::square_to_string(1)
|
|
|
|
+ },
|
|
|
|
+ (&WHITE, true) => {
|
|
|
|
+ Self::square_to_string(3) + &Self::square_to_string(5)
|
|
|
|
+ },
|
|
|
|
+ (&BLACK, false) => {
|
|
|
|
+ Self::square_to_string(56 + 3) + &Self::square_to_string(56 + 1)
|
|
|
|
+ },
|
|
|
|
+ (&BLACK, true) => {
|
|
|
|
+ Self::square_to_string(56 + 3) + &Self::square_to_string(56 + 5)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ Move::EnPassant{ mov, beaten } => {
|
|
|
|
+ Self::square_to_string(mov.from) + &Self::square_to_string(mov.to)
|
|
|
|
+ },
|
|
Move::Promotion{ mov, promote_to } => {
|
|
Move::Promotion{ mov, promote_to } => {
|
|
Self::square_to_string(mov.from) + &Self::square_to_string(mov.to) +
|
|
Self::square_to_string(mov.from) + &Self::square_to_string(mov.to) +
|
|
if *promote_to == QUEEN { "Q" }
|
|
if *promote_to == QUEEN { "Q" }
|
|
@@ -109,21 +127,42 @@ impl SimpleMove {
|
|
|
|
|
|
|
|
|
|
pub fn generate_moves(game: &Game, side: Side) -> Vec<Move> {
|
|
pub fn generate_moves(game: &Game, side: Side) -> Vec<Move> {
|
|
- let mut moves: Vec<Move> = Vec::new();
|
|
|
|
|
|
+
|
|
|
|
+ let mut moves: Vec<Move> = Vec::with_capacity(128);
|
|
generate_pawn_pushes(game, side, &mut moves);
|
|
generate_pawn_pushes(game, side, &mut moves);
|
|
generate_pawn_doublepushes(game, side, &mut moves);
|
|
generate_pawn_doublepushes(game, side, &mut moves);
|
|
generate_pawn_captures(game, side, &mut moves);
|
|
generate_pawn_captures(game, side, &mut moves);
|
|
- generate_knight_moves(game, side, &mut moves);
|
|
|
|
- generate_bishop_moves(game, side, &mut moves);
|
|
|
|
- generate_rook_moves(game, side, &mut moves);
|
|
|
|
- generate_queen_moves(game, side, &mut moves);
|
|
|
|
- generate_king_moves(game, side, &mut moves);
|
|
|
|
|
|
+ generate_promotions(game, side, &mut moves);
|
|
|
|
+ generate_en_passant(game, side, &mut moves);
|
|
|
|
+ generate_knight_moves(game, side, &mut moves, false);
|
|
|
|
+ generate_bishop_moves(game, side, &mut moves, false);
|
|
|
|
+ generate_rook_moves(game, side, &mut moves, false);
|
|
|
|
+ generate_queen_moves(game, side, &mut moves, false);
|
|
|
|
+ generate_king_moves(game, side, &mut moves, false);
|
|
|
|
+ generate_castling_moves(game, side, &mut moves);
|
|
|
|
+ return moves;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * generates moves that could possibly attack a king,
|
|
|
|
+ * this function is used to check if the king is in check
|
|
|
|
+ */
|
|
|
|
+pub fn generate_possattacking_moves(game: &Game, side: Side) -> Vec<Move> {
|
|
|
|
+
|
|
|
|
+ let mut moves: Vec<Move> = Vec::with_capacity(32);
|
|
|
|
+ generate_pawn_captures(game, side, &mut moves);
|
|
|
|
+ generate_promotions(game, side, &mut moves);
|
|
|
|
+ generate_knight_moves(game, side, &mut moves, true);
|
|
|
|
+ generate_bishop_moves(game, side, &mut moves, true);
|
|
|
|
+ generate_rook_moves(game, side, &mut moves, true);
|
|
|
|
+ generate_queen_moves(game, side, &mut moves, true);
|
|
|
|
+ generate_king_moves(game, side, &mut moves, true);
|
|
return moves;
|
|
return moves;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn generate_legal_moves(game: &Game, side: Side) -> Vec<Move> {
|
|
pub fn generate_legal_moves(game: &Game, side: Side) -> Vec<Move> {
|
|
- let mut moves = generate_moves(game, side);
|
|
|
|
|
|
+ let moves = generate_moves(game, side);
|
|
|
|
|
|
moves.into_iter().filter(|mov| {
|
|
moves.into_iter().filter(|mov| {
|
|
let tryout = super::search::apply_move(game, *mov);
|
|
let tryout = super::search::apply_move(game, *mov);
|
|
@@ -132,6 +171,32 @@ pub fn generate_legal_moves(game: &Game, side: Side) -> Vec<Move> {
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
+pub fn generate_attacking_moves(game: &Game, side: Side) -> Vec<Move> {
|
|
|
|
+ let mut moves: Vec<Move> = Vec::with_capacity(32);
|
|
|
|
+ generate_pawn_captures(game, side, &mut moves);
|
|
|
|
+ generate_knight_moves(game, side, &mut moves, true);
|
|
|
|
+ generate_bishop_moves(game, side, &mut moves, true);
|
|
|
|
+ generate_rook_moves(game, side, &mut moves, true);
|
|
|
|
+ generate_queen_moves(game, side, &mut moves, true);
|
|
|
|
+ generate_king_moves(game, side, &mut moves, true);
|
|
|
|
+ return moves;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+pub fn sort_moves(game: &Game, move_list: &mut Vec<Move>) {
|
|
|
|
+ let all_pieces = game.get_all_side(WHITE) | game.get_all_side(BLACK);
|
|
|
|
+
|
|
|
|
+ move_list.sort_by_key(|mov| {
|
|
|
|
+ match mov {
|
|
|
|
+ Move::Default { mov, piece_type } => if (from_square(mov.to) & all_pieces) != 0 { -10 } else { 0 },
|
|
|
|
+ Move::Castling { side: _, left: _ } => 0,
|
|
|
|
+ Move::Promotion { mov, promote_to } => -5,
|
|
|
|
+ Move::EnPassant { mov: _, beaten: _ } => -10,
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
/*
|
|
/*
|
|
pub fn generate_pawn_moves(game: &Game, side: Side) -> Bitboard {
|
|
pub fn generate_pawn_moves(game: &Game, side: Side) -> Bitboard {
|
|
let pushed = generate_pawn_pushes(game, side);
|
|
let pushed = generate_pawn_pushes(game, side);
|
|
@@ -219,7 +284,68 @@ fn generate_pawn_captures(game: &Game, side: Side, move_list: &mut Vec<Move>) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-fn generate_knight_moves(game: &Game, side: Side, move_list: &mut Vec<Move>) {
|
|
|
|
|
|
+fn generate_promotions(game: &Game, side: Side, move_list: &mut Vec<Move>) {
|
|
|
|
+ let pawns = game.pawns(side);
|
|
|
|
+ let others = game.get_all_side(!side);
|
|
|
|
+ let pieces = game.get_all_side(side);
|
|
|
|
+
|
|
|
|
+ let moved_pawns =
|
|
|
|
+ match side {
|
|
|
|
+ WHITE => north_one(pawns),
|
|
|
|
+ BLACK => south_one(pawns)
|
|
|
|
+ } & (ROW_1 | ROW_8) & !(others | pieces);
|
|
|
|
+
|
|
|
|
+ let iter = BitboardIterator { board: moved_pawns };
|
|
|
|
+ for p in iter {
|
|
|
|
+ let origin = match side {
|
|
|
|
+ WHITE => south_one(p),
|
|
|
|
+ BLACK => north_one(p)
|
|
|
|
+ };
|
|
|
|
+ let movement = SimpleMove { from: square(origin), to: square(p) };
|
|
|
|
+ //move_list.push(Move::Default { mov: SimpleMove { from: square(origin), to: square(p) }, piece_type: PAWN });
|
|
|
|
+ move_list.push(Move::Promotion { mov: movement, promote_to: QUEEN });
|
|
|
|
+ move_list.push(Move::Promotion { mov: movement, promote_to: ROOK });
|
|
|
|
+ move_list.push(Move::Promotion { mov: movement, promote_to: BISHOP });
|
|
|
|
+ move_list.push(Move::Promotion { mov: movement, promote_to: KNIGHT });
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+fn generate_en_passant(game: &Game, side: Side, move_list: &mut Vec<Move>) {
|
|
|
|
+ if let Some(ep) = game.en_passant {
|
|
|
|
+ let pawncolumn = A_FILE >> ep;
|
|
|
|
+
|
|
|
|
+ let pawnrow: Bitboard = match side {
|
|
|
|
+ WHITE => ROW_5,
|
|
|
|
+ BLACK => ROW_4
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ let beaten_pawn = pawncolumn & pawnrow;
|
|
|
|
+ if beaten_pawn & game.get_piece(PAWN, !side) == 0 {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ let pawn_left = (beaten_pawn << 1) & pawnrow;
|
|
|
|
+ let pawn_right = (beaten_pawn >> 1) & pawnrow;
|
|
|
|
+
|
|
|
|
+ let attacking_pawns = game.get_piece(PAWN, side);
|
|
|
|
+ let target_square = pawncolumn & match side {
|
|
|
|
+ WHITE => ROW_6,
|
|
|
|
+ BLACK => ROW_3
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ if pawn_left & attacking_pawns != 0 {
|
|
|
|
+ let mov = Move::EnPassant{ mov: SimpleMove{ from: square(pawn_left), to: square(target_square) }, beaten: square(beaten_pawn) };
|
|
|
|
+ move_list.push(mov);
|
|
|
|
+ }
|
|
|
|
+ if pawn_right & attacking_pawns != 0 {
|
|
|
|
+ let mov = Move::EnPassant{ mov: SimpleMove{ from: square(pawn_right), to: square(target_square) }, beaten: square(beaten_pawn) };
|
|
|
|
+ move_list.push(mov);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+fn generate_knight_moves(game: &Game, side: Side, move_list: &mut Vec<Move>, captures_only: bool) {
|
|
let friends = game.get_all_side(side);
|
|
let friends = game.get_all_side(side);
|
|
let others = game.get_all_side(!side);
|
|
let others = game.get_all_side(!side);
|
|
|
|
|
|
@@ -227,7 +353,12 @@ fn generate_knight_moves(game: &Game, side: Side, move_list: &mut Vec<Move>) {
|
|
let knight_iter = BitboardIterator{ board: knights };
|
|
let knight_iter = BitboardIterator{ board: knights };
|
|
|
|
|
|
for k in knight_iter {
|
|
for k in knight_iter {
|
|
- let targets = BitboardIterator { board: get_knight_targets(square(k)) & !friends };
|
|
|
|
|
|
+ let target_mask = if captures_only {
|
|
|
|
+ !friends & others
|
|
|
|
+ } else {
|
|
|
|
+ !friends
|
|
|
|
+ };
|
|
|
|
+ let targets = BitboardIterator { board: get_knight_targets(square(k)) & target_mask };
|
|
for target in targets {
|
|
for target in targets {
|
|
let simple_move = SimpleMove { from: square(k), to: square(target) };
|
|
let simple_move = SimpleMove { from: square(k), to: square(target) };
|
|
move_list.push(Move::Default{ mov: simple_move, piece_type: KNIGHT });
|
|
move_list.push(Move::Default{ mov: simple_move, piece_type: KNIGHT });
|
|
@@ -254,34 +385,36 @@ fn get_knight_targets(square: Square) -> Bitboard {
|
|
TARGETS[square as usize]
|
|
TARGETS[square as usize]
|
|
}
|
|
}
|
|
|
|
|
|
-fn generate_bishop_moves(game: &Game, side: Side, move_list: &mut Vec<Move>) {
|
|
|
|
|
|
+fn generate_bishop_moves(game: &Game, side: Side, move_list: &mut Vec<Move>, captures_only: bool) {
|
|
let friends = game.get_all_side(side);
|
|
let friends = game.get_all_side(side);
|
|
let others = game.get_all_side(!side);
|
|
let others = game.get_all_side(!side);
|
|
|
|
|
|
- generate_sliding_moves(friends, others, game.bishops(side), BISHOP, false, true, move_list);
|
|
|
|
|
|
+ generate_sliding_moves(friends, others, game.bishops(side), BISHOP, false, true, move_list, captures_only);
|
|
}
|
|
}
|
|
|
|
|
|
-fn generate_rook_moves(game: &Game, side: Side, move_list: &mut Vec<Move>) {
|
|
|
|
|
|
+fn generate_rook_moves(game: &Game, side: Side, move_list: &mut Vec<Move>, captures_only: bool) {
|
|
let friends = game.get_all_side(side);
|
|
let friends = game.get_all_side(side);
|
|
let others = game.get_all_side(!side);
|
|
let others = game.get_all_side(!side);
|
|
|
|
|
|
- generate_sliding_moves(friends, others, game.rooks(side), ROOK, true, false, move_list);
|
|
|
|
|
|
+ generate_sliding_moves(friends, others, game.rooks(side), ROOK, true, false, move_list, captures_only);
|
|
}
|
|
}
|
|
|
|
|
|
-fn generate_queen_moves(game: &Game, side: Side, move_list: &mut Vec<Move>) {
|
|
|
|
|
|
+fn generate_queen_moves(game: &Game, side: Side, move_list: &mut Vec<Move>, captures_only: bool) {
|
|
let friends = game.get_all_side(side);
|
|
let friends = game.get_all_side(side);
|
|
let others = game.get_all_side(!side);
|
|
let others = game.get_all_side(!side);
|
|
|
|
|
|
- generate_sliding_moves(friends, others, game.queens(side), QUEEN, true, true, move_list);
|
|
|
|
|
|
+ generate_sliding_moves(friends, others, game.queens(side), QUEEN, true, true, move_list, captures_only);
|
|
}
|
|
}
|
|
|
|
|
|
fn generate_sliding_moves(friends: Bitboard, others: Bitboard, pieces: Bitboard, piece_type: PieceType,
|
|
fn generate_sliding_moves(friends: Bitboard, others: Bitboard, pieces: Bitboard, piece_type: PieceType,
|
|
- straight: bool, diagonal: bool, move_list: &mut Vec<Move>) {
|
|
|
|
|
|
+ straight: bool, diagonal: bool, move_list: &mut Vec<Move>, captures_only: bool) {
|
|
let pieces_iter = BitboardIterator { board: pieces };
|
|
let pieces_iter = BitboardIterator { board: pieces };
|
|
|
|
|
|
|
|
+ let mask = if captures_only { others } else { !(0 as Bitboard) };
|
|
|
|
+
|
|
for piece in pieces_iter {
|
|
for piece in pieces_iter {
|
|
let destinations = generate_sliding_destinations(friends, others, piece, straight, diagonal);
|
|
let destinations = generate_sliding_destinations(friends, others, piece, straight, diagonal);
|
|
- let dest_iter = BitboardIterator { board: destinations };
|
|
|
|
|
|
+ let dest_iter = BitboardIterator { board: destinations & mask };
|
|
for dest in dest_iter {
|
|
for dest in dest_iter {
|
|
let smove = SimpleMove{ from: square(piece), to: square(dest) };
|
|
let smove = SimpleMove{ from: square(piece), to: square(dest) };
|
|
move_list.push(Move::Default { mov: smove, piece_type: piece_type});
|
|
move_list.push(Move::Default { mov: smove, piece_type: piece_type});
|
|
@@ -326,7 +459,7 @@ fn generate_direction(piece: Bitboard, direction: fn(Bitboard) -> Bitboard, occu
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
-fn generate_king_moves(game: &Game, side: Side, move_list: &mut Vec<Move>) {
|
|
|
|
|
|
+fn generate_king_moves(game: &Game, side: Side, move_list: &mut Vec<Move>, captures_only: bool) {
|
|
let friends = game.get_all_side(side);
|
|
let friends = game.get_all_side(side);
|
|
let others = game.get_all_side(!side);
|
|
let others = game.get_all_side(!side);
|
|
|
|
|
|
@@ -336,6 +469,14 @@ fn generate_king_moves(game: &Game, side: Side, move_list: &mut Vec<Move>) {
|
|
|
|
|
|
if king.count_ones() != 1 { return; }
|
|
if king.count_ones() != 1 { return; }
|
|
|
|
|
|
|
|
+ let mask =
|
|
|
|
+ if captures_only {
|
|
|
|
+ !friends & others
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ !friends
|
|
|
|
+ };
|
|
|
|
+
|
|
let area = (north_one(king)
|
|
let area = (north_one(king)
|
|
| south_one(king)
|
|
| south_one(king)
|
|
| east_one(king)
|
|
| east_one(king)
|
|
@@ -344,7 +485,7 @@ fn generate_king_moves(game: &Game, side: Side, move_list: &mut Vec<Move>) {
|
|
| northwest_one(king)
|
|
| northwest_one(king)
|
|
| southwest_one(king)
|
|
| southwest_one(king)
|
|
| southeast_one(king))
|
|
| southeast_one(king))
|
|
- & !friends;
|
|
|
|
|
|
+ & mask;
|
|
|
|
|
|
let area_iter = BitboardIterator { board: area };
|
|
let area_iter = BitboardIterator { board: area };
|
|
for destination in area_iter {
|
|
for destination in area_iter {
|
|
@@ -353,12 +494,57 @@ fn generate_king_moves(game: &Game, side: Side, move_list: &mut Vec<Move>) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+
|
|
|
|
+fn generate_castling_moves(game: &Game, side: Side, move_list: &mut Vec<Move>) {
|
|
|
|
+ let c1 = Move::Castling{ side: side, left: false };
|
|
|
|
+ let c2 = Move::Castling{ side: side, left: true };
|
|
|
|
+
|
|
|
|
+ let legal_castlings: &[bool] = match side {
|
|
|
|
+ WHITE => &game.castling_rights[0..2],
|
|
|
|
+ BLACK => &game.castling_rights[2..4],
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ let all_pieces = game.get_all_side(WHITE) | game.get_all_side(BLACK);
|
|
|
|
+
|
|
|
|
+ // kingside castling
|
|
|
|
+ if legal_castlings[0] {
|
|
|
|
+ //info!("possible castling? {} {} {}", game.get_piece(KING, side), game.get_piece(ROOK, side), ((game.get_piece(KING, side) >> 3) & game.get_piece(ROOK, side)));
|
|
|
|
+ if ((game.get_piece(KING, side) >> 3) & game.get_piece(ROOK, side)) != 0 &&
|
|
|
|
+ ((game.get_piece(KING, side) >> 1) | (game.get_piece(KING, side) >> 2)) & all_pieces == 0 {
|
|
|
|
+ let mut tg1 = game.clone();
|
|
|
|
+
|
|
|
|
+ *tg1.get_piece_mut(KING, side) = game.get_piece(KING, side) >> 1;
|
|
|
|
+
|
|
|
|
+ if !is_check(game, side) && !is_check(&tg1, side) {
|
|
|
|
+ move_list.push(c1);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ // queenside
|
|
|
|
+ if legal_castlings[1] {
|
|
|
|
+ //info!("possible castling? {} {} {}", game.get_piece(KING, side), game.get_piece(ROOK, side), ((game.get_piece(KING, side) >> 3) & game.get_piece(ROOK, side)));
|
|
|
|
+ if ((game.get_piece(KING, side) << 4) & game.get_piece(ROOK, side)) != 0 &&
|
|
|
|
+ ((game.get_piece(KING, side) << 1) | (game.get_piece(KING, side) << 2) | (game.get_piece(KING, side) << 3)) & all_pieces == 0 {
|
|
|
|
+
|
|
|
|
+ let mut tg1 = game.clone();
|
|
|
|
+ let mut tg2 = game.clone();
|
|
|
|
+
|
|
|
|
+ *tg1.get_piece_mut(KING, side) = game.get_piece(KING, side) << 1;
|
|
|
|
+ *tg2.get_piece_mut(KING, side) = game.get_piece(KING, side) << 2;
|
|
|
|
+
|
|
|
|
+ if !is_check(game, side) && !is_check(&tg1, side) && !is_check(&tg2, side) {
|
|
|
|
+ move_list.push(c2);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* checks if side's king is in check
|
|
* checks if side's king is in check
|
|
*/
|
|
*/
|
|
pub fn is_check(game: &Game, side: Side) -> bool {
|
|
pub fn is_check(game: &Game, side: Side) -> bool {
|
|
let king_square = square(game.get_piece(KING, side));
|
|
let king_square = square(game.get_piece(KING, side));
|
|
- let possible_attacks = generate_moves(game, !side);
|
|
|
|
|
|
+ let possible_attacks = generate_possattacking_moves(game, !side);
|
|
for mov in possible_attacks {
|
|
for mov in possible_attacks {
|
|
if let Move::Default{ mov, piece_type: _ } = mov {
|
|
if let Move::Default{ mov, piece_type: _ } = mov {
|
|
if mov.to == king_square {
|
|
if mov.to == king_square {
|