Compare commits

...

6 commits

Author SHA1 Message Date
u1
77c8b82728
Update README.md
letting users know where the program will be stored and detailing cargo run
2024-08-15 22:54:16 -03:00
u1
44a021f0b0
Update README.md
added additional information for those who do not know how to use cargo or rust
2024-08-15 22:54:16 -03:00
u1
d73ce30692
Update README.md
forgot about markdown appending single newline strings to the end of the one before it
2024-08-15 22:54:16 -03:00
ef279ae4f4
final timings and logs done
finished the readme
tweaked a few other things
2024-08-15 22:54:16 -03:00
1709084904
fixed my horrible manual random impl, turns out the "old SEED^3 trick" isnt used anymore for *many* good reasons 2024-08-15 22:54:15 -03:00
c102a5e40a
it works, comitting now before i destroy it all 2024-08-15 22:54:11 -03:00
8 changed files with 5999 additions and 1 deletions

4707
1_billion.log Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

11
1_milion.log Normal file
View file

@ -0,0 +1,11 @@
HIGH_SCORE: 0
ROLL_COUNT: 0
HIGH_SCORE: 92
ROLL_COUNT: 423264
HIGH_SCORE: 92
ROLL_COUNT: 856018
HIGH_SCORE: 92
ROLL_COUNT: 1000000
HIGH_SCORE: 92
ROLL_COUNT: 1000000
realtime 0:04.40

View file

@ -0,0 +1,7 @@
HIGH_SCORE: 74
ROLL_COUNT: 337
HIGH_SCORE: 91
ROLL_COUNT: 1000000
HIGH_SCORE: 91
ROLL_COUNT: 1000000
realtime 0:01.00

23
Cargo.toml Normal file
View file

@ -0,0 +1,23 @@
[package]
name = "graveler-softlockpicking-years-wasted-calculator"
version = "0.1.0"
edition = "2021"
[profile.release]
opt-level = 3
codegen-units = 1
panic = "abort"
[profile.release.package."*"]
opt-level = 3
codegen-units = 1
#panic = "abort"
[dependencies]
rand = { version = "0.8.5", optional = true }
num_cpus = { version = "1.16.0", optional = true }
[features]
default = ["rust_random", "auto_thread_count"]
rust_random = ["dep:rand"]
auto_thread_count = ["dep:num_cpus"]

View file

@ -1,3 +1,48 @@
# graveler-softlockpicking-years-wasted-calculator
This repo is a short and sweet response to Austins most recent video 'The Science of Soft Lock Picking Pokemon' available here https://www.youtube.com/watch?v=M8C8dHQE2Ro
This repo is a short and sweet response to Austins most recent video 'The Science of Soft Lock Picking Pokemon' available here https://www.youtube.com/watch?v=M8C8dHQE2Ro
## Programs
[`time`](https://www.gnu.org/software/time/) to record execution time
[`rustup`](https://rustup.rs/) to install cargo, and all other tools used by the rust ecosystem
[`cargo`](https://github.com/rust-lang/cargo) to compile the program
# Building
`cargo build --release`
the built program will be stored under `target/release` or `target/debug` if you forgot to use the `--release` flag (not recommended as there will be a significant performance hit due to additional debugging and status information, as well as additional runtime verification of the code)
you can also replace `build` with `run` to run the program after the compilation process is done, however this may interfere with any external timing software such as gnu time (see the Programs section) since it will include the compilation time and however long it takes for cargo to start the built program
# Features
You can disable default features to switch to less performant code like so
`cargo build --release --no-default-features`
however this will require you provide a seed to seed the manual random implementation
`cargo build --release --no-default-features -- 789124`
to enable specific features just use the -F flag like so
`cargo build --release --no-default-features -F auto_thread_count -- 789124`
or
`cargo build --release --no-default-features -F auto_thread_count,rust_random`
# Timings
### 1 billion
comparable to the python implementation: 39:12.67
multithreading and a better random library: 8:51.31
### 1 million
comparable to the python implementation: 0:04.40
multithreading and a better random library: 0:01.00

1
rustfmt.toml Normal file
View file

@ -0,0 +1 @@
hard_tabs=true

137
src/main.rs Normal file
View file

@ -0,0 +1,137 @@
use std::{
sync::atomic::{AtomicBool, AtomicUsize, Ordering},
thread,
time::Duration,
};
const ITER_COUNT: usize = 1_000_000_000;
//const ITER_COUNT: usize = 1_000_000;
static THREAD_STOP: AtomicBool = AtomicBool::new(false);
static ROLL_COUNT: AtomicUsize = AtomicUsize::new(0);
static HIGH_SCORE: AtomicUsize = AtomicUsize::new(0);
fn main() {
#[cfg(not(feature = "rust_random"))]
{
// forgive this mess, i just cant be arsed to make a proper command line interface
// in case youre wondering a usize is a non floating point number that can fit inside your cpus max bit size
let seed = std::env::args()
.collect::<Vec<String>>()
.get(1) // we use 1 here because 90% of platforms use the path/program name as the first argument
.expect("You must supply a usize argument, for example 75293")
.parse::<usize>()
.expect("First argument is not a valid usize, for example 926304");
SEED.store(seed, Ordering::SeqCst);
// give it some time to become a tad more "random"
for _ in 0..seed {
seeded_random();
}
}
// get the total thread count we wish to use
#[cfg(feature = "auto_thread_count")]
let thread_count: usize = num_cpus::get();
#[cfg(not(feature = "auto_thread_count"))]
let thread_count: usize = 1; // Change me to any number you want to use as the thread count, please note you must not be using the 'auto_thread_count' feature, see README.md for details
// divide the total iter count among multiple threads
let thread_dispatch = {
let mut vec = vec![0; thread_count];
for i in 0..(thread_count) {
vec[i] = ITER_COUNT / thread_count;
}
// add the remainder of the division to the last thread as not ever number is cleanly divisible by an unknown thread count
vec[thread_count - 1] = vec[thread_count - 1] + (ITER_COUNT % thread_count);
vec
};
// for verification purposes
//let mut adder = 0;
//for i in &thread_dispatch {
// adder += i;
//}
//println!("{adder}");
let mut thread_join_handles = vec![];
for i in thread_dispatch {
thread_join_handles.push(thread::spawn(move || number_cruncher(i)));
}
loop {
println!("HIGH_SCORE: {}", HIGH_SCORE.load(Ordering::SeqCst));
println!("ROLL_COUNT: {}", ROLL_COUNT.load(Ordering::SeqCst));
// if all threads are done break the loop
{
let mut any_thread_running = false;
for i in &thread_join_handles {
if !i.is_finished() {
any_thread_running = true;
}
}
if !any_thread_running {
break;
}
}
thread::sleep(Duration::from_secs(1));
}
println!("HIGH_SCORE: {}", HIGH_SCORE.load(Ordering::SeqCst));
println!("ROLL_COUNT: {}", ROLL_COUNT.load(Ordering::SeqCst));
}
fn number_cruncher(iter_count: usize) {
{
for _ in 0..iter_count {
if THREAD_STOP.load(Ordering::SeqCst) {
break;
}
ROLL_COUNT.fetch_add(1, Ordering::SeqCst);
let mut correct_count = 0;
// turns
for _ in 0..231 {
#[cfg(feature = "rust_random")]
let rand = rand::Rng::gen_range(&mut rand::thread_rng(), 0..4);
#[cfg(not(feature = "rust_random"))]
let rand = seeded_random() % 4;
match rand {
0 => {
correct_count += 1;
}
_ => { /* do nothing */ }
}
if correct_count > HIGH_SCORE.load(Ordering::SeqCst) {
HIGH_SCORE.swap(correct_count, Ordering::SeqCst);
}
if correct_count > 177 {
THREAD_STOP.store(true, Ordering::SeqCst);
println!("HIT: {correct_count}");
}
}
}
}
}
#[cfg(not(feature = "rust_random"))]
static SEED: AtomicUsize = AtomicUsize::new(0);
#[cfg(not(feature = "rust_random"))]
pub fn seeded_random() -> usize {
SEED.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |mut random| {
random ^= random << 13;
random ^= random >> 17;
random ^= random << 5;
Some(random)
})
.unwrap()
}