|
@@ -189,8 +189,7 @@ pub fn generate_possattacking_moves(game: &Game, side: Side) -> Vec<Move> {
|
|
return moves;
|
|
return moves;
|
|
}
|
|
}
|
|
|
|
|
|
-
|
|
|
|
-pub fn generate_legal_moves(game: &mut Game, side: Side) -> Vec<Move> {
|
|
|
|
|
|
+pub fn generate_legal_moves_old(game: &mut Game, side: Side) -> Vec<Move> {
|
|
let moves = generate_moves(game, side);
|
|
let moves = generate_moves(game, side);
|
|
|
|
|
|
let check_legality = |mov: &Move| {
|
|
let check_legality = |mov: &Move| {
|
|
@@ -203,34 +202,76 @@ pub fn generate_legal_moves(game: &mut Game, side: Side) -> Vec<Move> {
|
|
moves.into_iter().filter(check_legality).collect::<Vec<Move>>()
|
|
moves.into_iter().filter(check_legality).collect::<Vec<Move>>()
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+fn generate_all_slides(origin: Bitboard) -> Bitboard {
|
|
|
|
+ let mut result = populate_files(origin) | populate_rows(origin);
|
|
|
|
|
|
-pub fn generate_legal_sorted_moves(game: &mut Game, _hash: &mut Cache, killers: &[Move], ce: Option<CacheEntry>, side: Side) -> Vec<Move> {
|
|
|
|
- let moves = generate_moves(game, side);
|
|
|
|
|
|
+ let mut ne = origin;
|
|
|
|
+ let mut se = origin;
|
|
|
|
+ let mut nw = origin;
|
|
|
|
+ let mut sw = origin;
|
|
|
|
+ for i in 1..7 {
|
|
|
|
+ result |= ne;
|
|
|
|
+ result |= se;
|
|
|
|
+ result |= nw;
|
|
|
|
+ result |= sw;
|
|
|
|
+ ne = northeast_one(ne);
|
|
|
|
+ se = southeast_one(se);
|
|
|
|
+ nw = northwest_one(nw);
|
|
|
|
+ sw = southwest_one(sw);
|
|
|
|
+ }
|
|
|
|
+ return result;
|
|
|
|
+}
|
|
|
|
|
|
- let illegal_value = crate::evaluate::MAX_VALUE;
|
|
|
|
|
|
|
|
- let mut mov_val_and_legality = |mov: &Move| {
|
|
|
|
- let undo = game.apply(*mov);
|
|
|
|
- let illegal = is_check(game, side);
|
|
|
|
|
|
+pub fn generate_legal_moves(game: &mut Game, side: Side) -> Vec<Move> {
|
|
|
|
+ let moves = generate_moves(game, side);
|
|
|
|
|
|
- if illegal {
|
|
|
|
- game.undo_move(undo);
|
|
|
|
- return illegal_value;
|
|
|
|
|
|
+ let check = is_check(game, side);
|
|
|
|
+ let king = game.kings(side);
|
|
|
|
+ let possible_pins = generate_all_slides(king);
|
|
|
|
+
|
|
|
|
+ let check_legality = |m: &Move| {
|
|
|
|
+ if !check {
|
|
|
|
+ match m {
|
|
|
|
+ Move::Default { mov, piece_type, captured: _ } => {
|
|
|
|
+ // check whether piece is not pinned and is not a king
|
|
|
|
+ if from_square(mov.from) & possible_pins == 0 && *piece_type != KING {
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ _ => {}
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
+ let undo = game.apply(*m);
|
|
|
|
+ let legal = !is_check(game, side);
|
|
|
|
+ game.undo_move(undo);
|
|
|
|
+ return legal;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ moves.into_iter().filter(check_legality).collect::<Vec<Move>>()
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+pub fn generate_legal_sorted_moves(game: &mut Game, _hash: &mut Cache, killers: &[Move], ce: Option<CacheEntry>, side: Side) -> Vec<Move> {
|
|
|
|
+ let mut moves = generate_legal_moves(game, side);
|
|
|
|
+
|
|
|
|
+ let mov_val= |mov: &Move| {
|
|
|
|
+ // if its a pv move from previously
|
|
|
|
+ if let Some(c) = &ce {
|
|
|
|
+ if &c.mov == mov {
|
|
|
|
+ return -2000;
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
|
|
- let pv_bonus = if let Some(c) = &ce { if &c.mov == mov { 1000 } else { 0 } } else { 0 };
|
|
|
|
let killer_bonus = if killers.contains(mov) { 300 } else { 0 };
|
|
let killer_bonus = if killers.contains(mov) { 300 } else { 0 };
|
|
let capture_bonus: i32 = if mov.is_capture() { 400 } else { 0 } - mvv_lva_score(mov) / 16;
|
|
let capture_bonus: i32 = if mov.is_capture() { 400 } else { 0 } - mvv_lva_score(mov) / 16;
|
|
-
|
|
|
|
- let eval = crate::evaluate::evaluate(game) - killer_bonus - capture_bonus - pv_bonus;
|
|
|
|
- game.undo_move(undo);
|
|
|
|
|
|
+ let eval = -killer_bonus - capture_bonus;
|
|
return eval;
|
|
return eval;
|
|
};
|
|
};
|
|
|
|
|
|
- let mut amovs: Vec<_> = moves.iter().map(|m| (*m, mov_val_and_legality(m))).collect();
|
|
|
|
- amovs.sort_unstable_by_key(|x| x.1);
|
|
|
|
|
|
+ moves.sort_by_cached_key(|m| mov_val(m));
|
|
|
|
|
|
- amovs.into_iter().filter(|x| x.1 != illegal_value).map(|(m, _v)| m).collect::<Vec<Move>>()
|
|
|
|
|
|
+ return moves;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -726,50 +767,86 @@ pub fn is_check(game: &Game, side: Side) -> bool {
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
-/**
|
|
|
|
- * checks if side's king is in check
|
|
|
|
- */
|
|
|
|
-pub fn is_check_old(game: &Game, side: Side) -> bool {
|
|
|
|
- let king = game.get_piece(KING, side);
|
|
|
|
- let king_square = square(king);
|
|
|
|
- let possible_attacks = generate_possattacking_moves(game, !side);
|
|
|
|
- for mov in possible_attacks {
|
|
|
|
- if let Move::Default{ mov, piece_type: _, captured: _ } = mov {
|
|
|
|
- if mov.to == king_square {
|
|
|
|
- if !is_check(game, side) {
|
|
|
|
- panic!("incorrect non-check {}", game.beautiful_print());
|
|
|
|
- }
|
|
|
|
- return true;
|
|
|
|
- }
|
|
|
|
|
|
+///
|
|
|
|
+/// checks wether the square sq is attacked by the specified side
|
|
|
|
+///
|
|
|
|
+pub fn is_attacked(game: &Game, sq: Square, side: Side) -> bool {
|
|
|
|
+ let others = game.get_all_side(!side);
|
|
|
|
+ let friends = game.get_all_side(side);
|
|
|
|
+
|
|
|
|
+ let sq_bb = from_square(sq);
|
|
|
|
+
|
|
|
|
+ let knights = get_knight_targets(sq);
|
|
|
|
+ if knights & game.get_piece(KNIGHT, side) != 0 {
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ let diagonal = generate_sliding_destinations(others, friends, sq_bb, false, true);
|
|
|
|
+ let straight = generate_sliding_destinations(others, friends, sq_bb, true, false);
|
|
|
|
+
|
|
|
|
+ if diagonal & game.get_piece(BISHOP, side) != 0 {
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+ if diagonal & game.get_piece(QUEEN, side) != 0 {
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+ if straight & game.get_piece(ROOK, side) != 0 {
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+ if straight & game.get_piece(QUEEN, side) != 0 {
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if side == WHITE {
|
|
|
|
+ if ((southeast_one(sq_bb) | southwest_one(sq_bb)) & game.get_piece(PAWN, side)) != 0 {
|
|
|
|
+ return true;
|
|
}
|
|
}
|
|
- else if let Move::Promotion{ mov, promote_to: _, captured: _ } = mov {
|
|
|
|
- if mov.to == king_square {
|
|
|
|
- if !is_check(game, side) {
|
|
|
|
- panic!("incorrect non-check {}", game.beautiful_print());
|
|
|
|
- }
|
|
|
|
- return true;
|
|
|
|
- }
|
|
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ if ((northeast_one(sq_bb) | northwest_one(sq_bb)) & game.get_piece(PAWN, side)) != 0 {
|
|
|
|
+ return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- if is_check(game, side) {
|
|
|
|
- panic!("incorrect check {}", game.beautiful_print());
|
|
|
|
|
|
+
|
|
|
|
+ let sq_lr = east_one(sq_bb) | sq_bb | west_one(sq_bb);
|
|
|
|
+ let sq_area = north_one(sq_lr) | sq_lr | south_one(sq_lr);
|
|
|
|
+ if sq_area & game.get_piece(KING, side) != 0 {
|
|
|
|
+ return true;
|
|
}
|
|
}
|
|
- false
|
|
|
|
|
|
+
|
|
|
|
+ return false;
|
|
}
|
|
}
|
|
-/*
|
|
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
#[cfg(test)]
|
|
#[cfg(test)]
|
|
mod tests {
|
|
mod tests {
|
|
- use search::Game;
|
|
|
|
use movegen::*;
|
|
use movegen::*;
|
|
|
|
|
|
- #[test]
|
|
|
|
- fn pawn_pushes() {
|
|
|
|
- let mut game: Game = Game::default();
|
|
|
|
- let pawn_moves = generate_pawn_moves(&game, WHITE);
|
|
|
|
- assert_eq!(pawn_moves, ());
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-*/
|
|
|
|
|
|
+ use crate::{search::{perft, SearchControl}, hash::RepetitionTable};
|
|
|
|
|
|
|
|
+ ///
|
|
|
|
+ /// tests the move generation from some positions to depth 4 and checks wether the correct
|
|
|
|
+ /// amount of moves has been generated
|
|
|
|
+ ///
|
|
|
|
+ #[test]
|
|
|
|
+ fn some_positions_perft() {
|
|
|
|
+ assert_eq!(nodes_from_pos("5bk1/5p2/7p/q2r1p2/2Q5/P4N2/3prPPP/1R3RK1 b - - 1 34", 4), 3773096);
|
|
|
|
+ assert_eq!(nodes_from_pos("r1bqkbnr/1p1p1ppp/p1N1p3/8/4P3/2N5/PPP2PPP/R1BQKB1R b KQkq - 0 6", 4), 2008894);
|
|
|
|
+ assert_eq!(nodes_from_pos("1r5r/p2b2p1/1p1k1bp1/2pBpp2/1P1n1P2/P1NP3P/2PB2P1/R2KR3 w - - 1 23", 4), 1971751);
|
|
|
|
|
|
|
|
+ // some positions from https://www.chessprogramming.org/Perft_Results
|
|
|
|
+ assert_eq!(nodes_from_pos("rnbq1k1r/pp1Pbppp/2p5/8/2B5/8/PPP1NnPP/RNBQK2R w KQ - 1 8", 4), 2103487);
|
|
|
|
+ assert_eq!(nodes_from_pos("r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1", 5), 15833292);
|
|
|
|
+ assert_eq!(nodes_from_pos("r2q1rk1/pP1p2pp/Q4n2/bbp1p3/Np6/1B3NBn/pPPP1PPP/R3K2R b KQ - 0 1", 5), 15833292);
|
|
|
|
+ }
|
|
|
|
|
|
|
|
+ fn nodes_from_pos(fen: &str, depth: i32) -> usize {
|
|
|
|
+ let mut game: Game = Game::from_fen_str(fen).unwrap();
|
|
|
|
+ let mut check = || false;
|
|
|
|
+ let mut rt = &mut RepetitionTable::new();
|
|
|
|
+ let mut sc = SearchControl::new(&mut check, &mut rt, depth);
|
|
|
|
+ perft(&mut game, &mut sc, depth);
|
|
|
|
+ return sc.nodes;
|
|
|
|
+ }
|
|
|
|
+}
|