|
@@ -4,7 +4,6 @@ use game::Game;
|
|
|
use evaluate::*;
|
|
|
use log::info;
|
|
|
use rand::prelude::*;
|
|
|
-use zobrist;
|
|
|
use hash::*;
|
|
|
|
|
|
|
|
@@ -381,3 +380,59 @@ pub fn apply_move(game: &Game, mov: Move) -> Game {
|
|
|
}
|
|
|
return new_game;
|
|
|
}
|
|
|
+
|
|
|
+
|
|
|
+pub fn perft(game: &mut Game, sc: &mut SearchControl, depth: i32) -> bool {
|
|
|
+ let moves = generate_legal_moves(game, game.turn);
|
|
|
+
|
|
|
+ if depth <= 1 {
|
|
|
+ sc.nodes += moves.len();
|
|
|
+ if sc.nodes % 1024 < moves.len() {
|
|
|
+ if (sc.check)() {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ for mov in moves {
|
|
|
+ let undo = game.apply(mov);
|
|
|
+ let do_return = perft(game, sc, depth - 1);
|
|
|
+ if do_return {
|
|
|
+ game.undo_move(undo);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ game.undo_move(undo);
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+#[cfg(test)]
|
|
|
+mod tests {
|
|
|
+
|
|
|
+ use super::*;
|
|
|
+ #[test]
|
|
|
+ fn test_move_generation() {
|
|
|
+ let positions = [
|
|
|
+ "rnbq1k1r/pp1Pbppp/2p5/8/2B5/8/PPP1NnPP/RNBQK2R w KQ - 1 8",
|
|
|
+ "r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10"
|
|
|
+ ];
|
|
|
+ let perft_results: [Vec<usize>; 2] = [
|
|
|
+ vec![44, 1486, 62379, 2103487],
|
|
|
+ vec![46, 2079, 89890, 3894594]
|
|
|
+ ];
|
|
|
+
|
|
|
+ for (i, &position) in positions.iter().enumerate() {
|
|
|
+ let mut game = Game::from_fen_str(position).unwrap();
|
|
|
+ for (j, &p_res) in perft_results[i].iter().enumerate() {
|
|
|
+ let depth = j + 1;
|
|
|
+ let mut sc = SearchControl{ nodes: 0, check: &mut (|| false), move_history: &mut RepetitionTable::new(), initial_depth: depth as _ };
|
|
|
+ perft(&mut game, &mut sc, depth as _);
|
|
|
+ assert_eq!(sc.nodes, p_res);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|