From f0156ccec40646b10ac19bf98098f162705bec02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Max=20K=C3=A4nner?= Date: Tue, 24 Jun 2025 20:59:32 +0200 Subject: [PATCH] add multithreading for seed cracking --- Cargo.lock | 1 + battlesnake/Cargo.toml | 1 + battlesnake/src/bin/seed-cracker.rs | 60 +++++++++++++++++++++++------ 3 files changed, 51 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1e7e7c6..135bce6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -224,6 +224,7 @@ dependencies = [ "lru", "memmap2", "rand 0.9.1", + "rayon", "serde", "serde_json", "sqlx", diff --git a/battlesnake/Cargo.toml b/battlesnake/Cargo.toml index fc00ac4..5aa0af1 100644 --- a/battlesnake/Cargo.toml +++ b/battlesnake/Cargo.toml @@ -55,6 +55,7 @@ memmap2 = "0.9.5" bytemuck = "1.23.1" flame = "0.2.2" flamer = "0.5.0" +rayon = "1.10.0" [dev-dependencies] criterion = "0.5" diff --git a/battlesnake/src/bin/seed-cracker.rs b/battlesnake/src/bin/seed-cracker.rs index e008e3b..9bfee59 100644 --- a/battlesnake/src/bin/seed-cracker.rs +++ b/battlesnake/src/bin/seed-cracker.rs @@ -1,4 +1,13 @@ -use std::{fs::File, path::PathBuf, sync::LazyLock, time::Instant}; +use std::{ + cell::RefCell, + fs::File, + path::PathBuf, + sync::{ + LazyLock, + atomic::{AtomicU32, Ordering}, + }, + time::Instant, +}; use az::{Az, WrappingAs}; use battlesnake::types::{Coord, Direction, wire::Request}; @@ -8,6 +17,7 @@ use clap::Parser; use hashbrown::HashMap; use log::{debug, error, info, trace}; use memmap2::{Mmap, MmapOptions}; +use rayon::iter::{IntoParallelIterator, ParallelIterator}; type DynError = Box; @@ -21,6 +31,9 @@ struct Args { #[arg()] /// The file to read the requests from file: PathBuf, + /// Use multiple cores to speed up search + #[arg(long, short)] + parallel: bool, } //#[flame] @@ -65,18 +78,43 @@ fn main() -> Result<(), DynError> { } else { info!("Trying every seed from 1 to {}", i32::MAX); let start = Instant::now(); - for seed in 1..i32::MAX { - if seed % 1_000_000 == 0 { - debug!("Checking {seed:>10}"); + if args.parallel { + thread_local! { + static RAND: RefCell = RefCell::new(SeedRand::new(0)); } - if prepared.validate(&mut rand, i64::from(seed), false) { - let elapsed = start.elapsed(); - info!( - "Trying {seed} seeds took {elapsed:?} ({:?} / seed)", - elapsed / seed.az() - ); + let tried = AtomicU32::new(0); + let seed = (1..i32::MAX).into_par_iter().find_any(|seed| { + tried.fetch_add(1, Ordering::Relaxed); + RAND.with_borrow_mut(|rand| prepared.validate(rand, i64::from(*seed), false)) + }); + let elapsed = start.elapsed(); + let tried = tried.load(Ordering::Relaxed); + info!( + "Trying {tried} seeds took {elapsed:?} ({:?} / seed)", + elapsed / tried.az() + ); + if let Some(seed) = seed { println!("The correct seed is {seed}"); - break; + } else { + println!("No valid seed found"); + } + } else { + let seed = (1..i32::MAX).find(|seed| { + if seed % 1_000_000 == 0 { + debug!("Checking {seed:>10}"); + } + prepared.validate(&mut rand, i64::from(*seed), false) + }); + let elapsed = start.elapsed(); + let tried = seed.unwrap_or(i32::MAX); + info!( + "Trying {tried} seeds took {elapsed:?} ({:?} / seed)", + elapsed / tried.az() + ); + if let Some(seed) = seed { + println!("The correct seed is {seed}"); + } else { + println!("No valid seed found"); } } }