Upload folder using huggingface_hub
Browse files
neural_engine/Cargo.toml
CHANGED
|
@@ -9,3 +9,4 @@ shakmaty = "0.26.0" # Fast chess library
|
|
| 9 |
serde = { version = "1.0", features = ["derive"] }
|
| 10 |
serde_json = "1.0"
|
| 11 |
rand = "0.8.5"
|
|
|
|
|
|
| 9 |
serde = { version = "1.0", features = ["derive"] }
|
| 10 |
serde_json = "1.0"
|
| 11 |
rand = "0.8.5"
|
| 12 |
+
rand_distr = "0.4.3"
|
neural_engine/src/bin/test_zobrist.rs
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
use shakmaty::{Chess, Position};
|
| 2 |
+
use shakmaty::zobrist::{Zobrist64, ZobristHash};
|
| 3 |
+
|
| 4 |
+
fn main() {
|
| 5 |
+
let board = Chess::default();
|
| 6 |
+
let hash = board.zobrist_hash::<Zobrist64>(shakmaty::EnPassantMode::Legal);
|
| 7 |
+
println!("Hash: {}", hash.0);
|
| 8 |
+
}
|
neural_engine/src/main.rs
CHANGED
|
@@ -3,7 +3,9 @@ use std::collections::HashMap;
|
|
| 3 |
use std::fs;
|
| 4 |
use std::path::Path;
|
| 5 |
use shakmaty::{Chess, Position, CastlingMode};
|
|
|
|
| 6 |
use ort::session::Session;
|
|
|
|
| 7 |
|
| 8 |
#[derive(Clone)]
|
| 9 |
struct MCTSNode {
|
|
@@ -24,6 +26,15 @@ impl MCTSNode {
|
|
| 24 |
}
|
| 25 |
}
|
| 26 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
fn load_vocab() -> (HashMap<String, i64>, HashMap<i64, String>) {
|
| 28 |
let data = fs::read_to_string("vocab.json").expect("Unable to read vocab.json. Please run build_engine.bat first to generate it!");
|
| 29 |
let vocab: HashMap<String, i64> = serde_json::from_str(&data).expect("JSON format error");
|
|
@@ -96,7 +107,7 @@ fn mcts_simulate(
|
|
| 96 |
session: &mut Session,
|
| 97 |
vocab: &HashMap<String, i64>,
|
| 98 |
inv_vocab: &HashMap<i64, String>,
|
| 99 |
-
nn_cache: &mut HashMap<
|
| 100 |
) -> f32 {
|
| 101 |
if board.is_game_over() {
|
| 102 |
if board.is_checkmate() {
|
|
@@ -123,7 +134,7 @@ fn mcts_simulate(
|
|
| 123 |
}
|
| 124 |
|
| 125 |
// Evaluate with ONNX or Cache
|
| 126 |
-
let cache_key =
|
| 127 |
let (policy, value) = if let Some(cached) = nn_cache.get(&cache_key) {
|
| 128 |
cached.clone()
|
| 129 |
} else {
|
|
@@ -201,7 +212,8 @@ fn main() {
|
|
| 201 |
|
| 202 |
let (vocab, inv_vocab) = load_vocab();
|
| 203 |
|
| 204 |
-
let mut nn_cache = HashMap::new();
|
|
|
|
| 205 |
|
| 206 |
for line in stdin.lock().lines() {
|
| 207 |
let line = line.expect("Failed to read line");
|
|
@@ -220,6 +232,7 @@ fn main() {
|
|
| 220 |
"position" => {
|
| 221 |
// position startpos moves e2e4 e7e5
|
| 222 |
board = Chess::default();
|
|
|
|
| 223 |
history_moves.clear();
|
| 224 |
history_moves.push("<bos>".to_string());
|
| 225 |
|
|
@@ -234,6 +247,33 @@ fn main() {
|
|
| 234 |
}
|
| 235 |
}
|
| 236 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 237 |
}
|
| 238 |
"go" => {
|
| 239 |
let mut movetime_ms = 5000;
|
|
@@ -290,17 +330,22 @@ fn main() {
|
|
| 290 |
}
|
| 291 |
|
| 292 |
if let Some(ref mut sess) = session {
|
| 293 |
-
let mut root = MCTSNode::new(1.0);
|
| 294 |
let start_time = std::time::Instant::now();
|
| 295 |
// Leave a small buffer (5% or minimum 50ms) to ensure we reply in time
|
| 296 |
let buffer = (movetime_ms as f64 * 0.05) as u128;
|
| 297 |
let safe_movetime = if movetime_ms > buffer { movetime_ms - buffer } else { movetime_ms };
|
| 298 |
let limit = std::time::Duration::from_millis(safe_movetime as u64);
|
| 299 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 300 |
let mut iter_count = 0;
|
|
|
|
| 301 |
// Loop continuously until time expires
|
| 302 |
while start_time.elapsed() < limit {
|
| 303 |
-
mcts_simulate(&mut
|
| 304 |
iter_count += 1;
|
| 305 |
|
| 306 |
// Stream UCI info every 100 iterations so !eval works
|
|
@@ -308,7 +353,7 @@ fn main() {
|
|
| 308 |
let mut best_eval = 0.0;
|
| 309 |
let mut best_visits = -1;
|
| 310 |
let mut best_action = String::new();
|
| 311 |
-
for (action, child) in &
|
| 312 |
if (child.visit_count as i32) > best_visits {
|
| 313 |
best_visits = child.visit_count as i32;
|
| 314 |
best_action = action.clone();
|
|
@@ -327,7 +372,7 @@ fn main() {
|
|
| 327 |
let mut best_action = String::new();
|
| 328 |
let mut best_visits = -1;
|
| 329 |
let mut best_eval = 0.0;
|
| 330 |
-
for (action, child) in &
|
| 331 |
if (child.visit_count as i32) > best_visits {
|
| 332 |
best_visits = child.visit_count as i32;
|
| 333 |
best_action = action.clone();
|
|
|
|
| 3 |
use std::fs;
|
| 4 |
use std::path::Path;
|
| 5 |
use shakmaty::{Chess, Position, CastlingMode};
|
| 6 |
+
use shakmaty::zobrist::{Zobrist64, ZobristHash};
|
| 7 |
use ort::session::Session;
|
| 8 |
+
use rand_distr::{Dirichlet, Distribution};
|
| 9 |
|
| 10 |
#[derive(Clone)]
|
| 11 |
struct MCTSNode {
|
|
|
|
| 26 |
}
|
| 27 |
}
|
| 28 |
|
| 29 |
+
fn add_dirichlet_noise(node: &mut MCTSNode, alpha: f32, epsilon: f32) {
|
| 30 |
+
if node.children.is_empty() { return; }
|
| 31 |
+
let dirichlet = Dirichlet::new(&vec![alpha; node.children.len()]).unwrap();
|
| 32 |
+
let noise = dirichlet.sample(&mut rand::thread_rng());
|
| 33 |
+
for (i, child) in node.children.values_mut().enumerate() {
|
| 34 |
+
child.prior = (1.0 - epsilon) * child.prior + epsilon * noise[i];
|
| 35 |
+
}
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
fn load_vocab() -> (HashMap<String, i64>, HashMap<i64, String>) {
|
| 39 |
let data = fs::read_to_string("vocab.json").expect("Unable to read vocab.json. Please run build_engine.bat first to generate it!");
|
| 40 |
let vocab: HashMap<String, i64> = serde_json::from_str(&data).expect("JSON format error");
|
|
|
|
| 107 |
session: &mut Session,
|
| 108 |
vocab: &HashMap<String, i64>,
|
| 109 |
inv_vocab: &HashMap<i64, String>,
|
| 110 |
+
nn_cache: &mut HashMap<u64, (HashMap<String, f32>, f32)>,
|
| 111 |
) -> f32 {
|
| 112 |
if board.is_game_over() {
|
| 113 |
if board.is_checkmate() {
|
|
|
|
| 134 |
}
|
| 135 |
|
| 136 |
// Evaluate with ONNX or Cache
|
| 137 |
+
let cache_key = board.zobrist_hash::<Zobrist64>(shakmaty::EnPassantMode::Legal).0;
|
| 138 |
let (policy, value) = if let Some(cached) = nn_cache.get(&cache_key) {
|
| 139 |
cached.clone()
|
| 140 |
} else {
|
|
|
|
| 212 |
|
| 213 |
let (vocab, inv_vocab) = load_vocab();
|
| 214 |
|
| 215 |
+
let mut nn_cache: HashMap<u64, (HashMap<String, f32>, f32)> = HashMap::new();
|
| 216 |
+
let mut global_root = MCTSNode::new(1.0);
|
| 217 |
|
| 218 |
for line in stdin.lock().lines() {
|
| 219 |
let line = line.expect("Failed to read line");
|
|
|
|
| 232 |
"position" => {
|
| 233 |
// position startpos moves e2e4 e7e5
|
| 234 |
board = Chess::default();
|
| 235 |
+
let old_history = history_moves.clone();
|
| 236 |
history_moves.clear();
|
| 237 |
history_moves.push("<bos>".to_string());
|
| 238 |
|
|
|
|
| 247 |
}
|
| 248 |
}
|
| 249 |
}
|
| 250 |
+
|
| 251 |
+
// Persist the tree if the history matches
|
| 252 |
+
let mut matches = true;
|
| 253 |
+
if old_history.len() <= history_moves.len() {
|
| 254 |
+
for i in 0..old_history.len() {
|
| 255 |
+
if old_history[i] != history_moves[i] {
|
| 256 |
+
matches = false;
|
| 257 |
+
break;
|
| 258 |
+
}
|
| 259 |
+
}
|
| 260 |
+
} else {
|
| 261 |
+
matches = false;
|
| 262 |
+
}
|
| 263 |
+
|
| 264 |
+
if matches {
|
| 265 |
+
for i in old_history.len()..history_moves.len() {
|
| 266 |
+
let m = &history_moves[i];
|
| 267 |
+
if let Some(child) = global_root.children.remove(m) {
|
| 268 |
+
global_root = child;
|
| 269 |
+
} else {
|
| 270 |
+
global_root = MCTSNode::new(1.0);
|
| 271 |
+
break;
|
| 272 |
+
}
|
| 273 |
+
}
|
| 274 |
+
} else {
|
| 275 |
+
global_root = MCTSNode::new(1.0);
|
| 276 |
+
}
|
| 277 |
}
|
| 278 |
"go" => {
|
| 279 |
let mut movetime_ms = 5000;
|
|
|
|
| 330 |
}
|
| 331 |
|
| 332 |
if let Some(ref mut sess) = session {
|
|
|
|
| 333 |
let start_time = std::time::Instant::now();
|
| 334 |
// Leave a small buffer (5% or minimum 50ms) to ensure we reply in time
|
| 335 |
let buffer = (movetime_ms as f64 * 0.05) as u128;
|
| 336 |
let safe_movetime = if movetime_ms > buffer { movetime_ms - buffer } else { movetime_ms };
|
| 337 |
let limit = std::time::Duration::from_millis(safe_movetime as u64);
|
| 338 |
|
| 339 |
+
if global_root.children.is_empty() {
|
| 340 |
+
mcts_simulate(&mut global_root, board.clone(), &mut history_moves, sess, &vocab, &inv_vocab, &mut nn_cache);
|
| 341 |
+
}
|
| 342 |
+
add_dirichlet_noise(&mut global_root, 0.3, 0.25);
|
| 343 |
+
|
| 344 |
let mut iter_count = 0;
|
| 345 |
+
|
| 346 |
// Loop continuously until time expires
|
| 347 |
while start_time.elapsed() < limit {
|
| 348 |
+
mcts_simulate(&mut global_root, board.clone(), &mut history_moves, sess, &vocab, &inv_vocab, &mut nn_cache);
|
| 349 |
iter_count += 1;
|
| 350 |
|
| 351 |
// Stream UCI info every 100 iterations so !eval works
|
|
|
|
| 353 |
let mut best_eval = 0.0;
|
| 354 |
let mut best_visits = -1;
|
| 355 |
let mut best_action = String::new();
|
| 356 |
+
for (action, child) in &global_root.children {
|
| 357 |
if (child.visit_count as i32) > best_visits {
|
| 358 |
best_visits = child.visit_count as i32;
|
| 359 |
best_action = action.clone();
|
|
|
|
| 372 |
let mut best_action = String::new();
|
| 373 |
let mut best_visits = -1;
|
| 374 |
let mut best_eval = 0.0;
|
| 375 |
+
for (action, child) in &global_root.children {
|
| 376 |
if (child.visit_count as i32) > best_visits {
|
| 377 |
best_visits = child.visit_count as i32;
|
| 378 |
best_action = action.clone();
|