From e1b1642d373ca9eda858718804fed594fa9423fa Mon Sep 17 00:00:00 2001 From: dece Date: Sun, 14 Jun 2020 13:59:28 +0200 Subject: [PATCH] engine: use DashMap for sharing evaluated nodes --- Cargo.lock | 87 +++++++++++++++++++++++++++---------------------- Cargo.toml | 2 +- src/analysis.rs | 30 ++++++++++++++--- src/engine.rs | 9 +++-- src/rules.rs | 2 +- 5 files changed, 81 insertions(+), 49 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 243d1fc..3681676 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,13 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "ahash" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "const-random 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "ansi_term" version = "0.11.0" @@ -42,6 +50,34 @@ dependencies = [ "vec_map 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "const-random" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "const-random-macro 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "const-random-macro" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "dashmap" +version = "3.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ahash 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "getrandom" version = "0.1.14" @@ -66,46 +102,18 @@ version = "0.2.71" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "ppv-lite86" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "rand" -version = "0.7.3" +name = "num_cpus" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "hermit-abi 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "rand_chacha" -version = "0.2.2" +name = "proc-macro-hack" +version = "0.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "ppv-lite86 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "strsim" @@ -130,7 +138,7 @@ name = "vatu" version = "0.1.0" dependencies = [ "clap 2.33.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "dashmap 3.11.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -163,19 +171,20 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] +"checksum ahash 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" "checksum clap 2.33.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129" +"checksum const-random 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "2f1af9ac737b2dd2d577701e59fd09ba34822f6f2ebdb30a7647405d9e55e16a" +"checksum const-random-macro 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "25e4c606eb459dd29f7c57b2e0879f2b6f14ee130918c2b78ccb58a9624e6c7a" +"checksum dashmap 3.11.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8cfcd41ae02d60edded204341d2798ba519c336c51a37330aa4b98a1128def32" "checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" "checksum hermit-abi 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71" "checksum libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)" = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49" -"checksum ppv-lite86 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" -"checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -"checksum rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +"checksum num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +"checksum proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)" = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" "checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" diff --git a/Cargo.toml b/Cargo.toml index c22b3a5..cc40135 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,4 +6,4 @@ edition = "2018" [dependencies] clap = "2.33" -rand = "0.7" +dashmap = "3.11" diff --git a/src/analysis.rs b/src/analysis.rs index a48e588..6f4b2dd 100644 --- a/src/analysis.rs +++ b/src/analysis.rs @@ -4,6 +4,8 @@ use std::fmt; use std::hash::{Hash, Hasher}; use std::sync::{Arc, atomic, mpsc}; +use dashmap::DashMap; + use crate::board; use crate::engine; use crate::notation; @@ -71,6 +73,8 @@ impl Hash for Node { } } +pub type NodeEvalMap = Arc>; + /// Analysis parameters. #[derive(Clone)] pub struct AnalysisParams { @@ -88,6 +92,7 @@ const MAX_F32: f32 = std::f32::INFINITY; pub fn analyze( node: &mut Node, _args: &AnalysisParams, + score_map: &NodeEvalMap, working: Arc, tx: mpsc::Sender, debug: bool, @@ -102,7 +107,13 @@ pub fn analyze( tx.send(engine::Cmd::Log(moves_str)).unwrap(); } - let (max_score, best_move) = minimax(node, 0, 2, board::is_white(node.game_state.color)); + let (max_score, best_move) = minimax( + node, + 0, + 3, + board::is_white(node.game_state.color), + &score_map, + ); if best_move.is_some() { let log_str = format!( @@ -145,12 +156,21 @@ fn minimax( node: &mut Node, depth: u32, max_depth: u32, - maximizing: bool + maximizing: bool, + score_map: &NodeEvalMap, ) -> (f32, Option) { + // If the node has already been analysed before, return its previous evaluation. + if let Some(score) = score_map.get(node) { + return (*score.value(), None) + } + // If we reached max depth, evaluate score for this board, store score and stop recursion. if depth == max_depth { let stats = stats::compute_stats(&node.board, &node.game_state); - return (evaluate(&stats), None); + let score = evaluate(&stats); + score_map.insert(node.clone(), score); + return (score, None); } + // Else, get the minimax score. let mut minmax = if maximizing { MIN_F32 } else { MAX_F32 }; let mut minmax_move = None; let moves = rules::get_player_moves(&node.board, &node.game_state, true); @@ -158,13 +178,13 @@ fn minimax( let mut sub_node = node.clone(); rules::apply_move_to(&mut sub_node.board, &mut sub_node.game_state, &m); if maximizing { - let (score, _) = minimax(&mut sub_node, depth + 1, max_depth, false); + let (score, _) = minimax(&mut sub_node, depth + 1, max_depth, false, score_map); if score >= minmax { minmax = score; minmax_move = Some(m); } } else { - let (score, _) = minimax(&mut sub_node, depth + 1, max_depth, true); + let (score, _) = minimax(&mut sub_node, depth + 1, max_depth, true, score_map); if score <= minmax { minmax = score; minmax_move = Some(m); diff --git a/src/engine.rs b/src/engine.rs index c49cca5..65fb800 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -6,6 +6,8 @@ use std::sync::{Arc, atomic, mpsc}; use std::thread; +use dashmap::DashMap; + use crate::analysis; use crate::board; use crate::notation; @@ -19,7 +21,7 @@ pub struct Engine { /// Current game state, starting point of further analysis. node: analysis::Node, /// Store already evaluated nodes with their score. - // score_map: Arc>>, + score_map: Arc>, /// Communication mode. mode: Mode, /// If true, the engine is currently listening to incoming cmds. @@ -77,7 +79,7 @@ impl Engine { Engine { debug: false, node: analysis::Node::new(), - // score_map: HashMap::with_capacity(2usize.pow(10)), + score_map: Arc::new(DashMap::with_capacity(2usize.pow(10))), mode: Mode::No, listening: false, working: Arc::new(atomic::AtomicBool::new(false)), @@ -179,11 +181,12 @@ impl Engine { self.working.store(true, atomic::Ordering::Relaxed); let mut node = self.node.clone(); let args = args.clone(); + let score_map = self.score_map.clone(); let working = self.working.clone(); let tx = match &self.mode { Mode::Uci(_, _, tx) => tx.clone(), _ => return }; let debug = self.debug; thread::spawn(move || { - analysis::analyze(&mut node, &args, working, tx, debug); + analysis::analyze(&mut node, &args, &score_map, working, tx, debug); }); } diff --git a/src/rules.rs b/src/rules.rs index d334235..b77fb22 100644 --- a/src/rules.rs +++ b/src/rules.rs @@ -220,7 +220,7 @@ pub fn get_castle_move(castle: u8) -> Move { /// see if P's king can be taken. Consider a call with true `commit` as /// a collection of attacked squares instead of legal move collection. pub fn get_player_moves(board: &Board, game_state: &GameState, commit: bool) -> Vec { - let mut moves = vec!(); + let mut moves = Vec::with_capacity(256); for r in 0..8 { for f in 0..8 { let p = (f, r);