Skip to content

Commit ab8cd7d

Browse files
committed
Added the ability to set the seed of the RNG
1 parent 0a7b621 commit ab8cd7d

4 files changed

Lines changed: 54 additions & 26 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "glitch"
3-
version = "0.3.1"
3+
version = "0.4.0"
44
edition = "2021"
55

66
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

src/eval.rs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use image::{DynamicImage, GenericImageView, Rgba};
2-
use rand::prelude::ThreadRng;
3-
use rand::Rng;
2+
use rand::{Rng, RngCore};
43
use std::collections::HashMap;
54

65
use crate::parser::Token;
@@ -57,7 +56,7 @@ pub struct EvalContext {
5756
pub fn eval(
5857
ctx: EvalContext,
5958
input: &DynamicImage,
60-
mut rng: ThreadRng,
59+
rng: &mut Box<dyn RngCore>,
6160
) -> Result<Rgba<u8>, String> {
6261
let EvalContext {
6362
tokens,
@@ -261,7 +260,7 @@ pub fn eval(
261260
let v_r = if let Some(v) = v_r {
262261
*v
263262
} else {
264-
let colors = gen_random_position(neg as i32, num as i32, &mut rng);
263+
let colors = gen_random_position(neg as i32, num as i32, rng);
265264
let rgb = rgb_from_colors(&colors);
266265
if !ignore_state {
267266
saved.v_r.get_or_insert(HashMap::new()).insert(num, rgb);
@@ -304,8 +303,7 @@ pub fn eval(
304303
let yu = three_rule(y, height);
305304
stack.push(RgbSum::new(yu, yu, yu));
306305
}
307-
/*
308-
'r' => {
306+
/*'r' => {
309307
let v_r = match saved.v_r {
310308
Some(v_r) => v_r,
311309
None => {
@@ -326,7 +324,7 @@ pub fn eval(
326324
let v_t = match saved.v_t {
327325
Some(v_t) => v_t,
328326
None => {
329-
let colors = gen_random_position(-2, 2, &mut rng);
327+
let colors = gen_random_position(-2, 2, rng);
330328

331329
let rgb = rgb_from_colors(&colors);
332330
if !ignore_state {
@@ -342,7 +340,7 @@ pub fn eval(
342340
let v_g = match saved.v_g {
343341
Some(v_g) => v_g,
344342
None => {
345-
let colors = gen_random_position(0i32, width as i32, &mut rng);
343+
let colors = gen_random_position(0i32, width as i32, rng);
346344

347345
let rgb = rgb_from_colors(&colors);
348346
if !ignore_state {
@@ -594,7 +592,7 @@ fn min(vals: [u8; 8]) -> u8 {
594592
vals.iter().cloned().min().unwrap_or_default()
595593
}
596594

597-
fn gen_random_position(min: i32, max: i32, rng: &mut ThreadRng) -> [(i32, i32); 3] {
595+
fn gen_random_position(min: i32, max: i32, rng: &mut Box<dyn RngCore>) -> [(i32, i32); 3] {
598596
let mut positions = [(0, 0); 3];
599597
for i in positions.iter_mut() {
600598
i.0 = rng.gen_range(min..=max);

src/main.rs

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
clippy::cognitive_complexity
77
)]
88

9-
use std::fs;
9+
use crate::eval::EvalContext;
10+
use crate::parser::Token;
1011
use clap::Parser;
1112
use console::{style, Emoji};
1213
use gif::{Encoder, Repeat};
@@ -16,15 +17,15 @@ use image::{
1617
ImageFormat, Pixel,
1718
};
1819
use indicatif::{ProgressBar, ProgressStyle};
20+
use rand::prelude::StdRng;
21+
use rand::{RngCore, SeedableRng};
1922
use rayon::prelude::*;
23+
use std::fs;
2024
use std::io::{BufReader, BufWriter, Read};
2125
use std::path::{Path, PathBuf};
2226
use std::sync::Mutex;
2327
use std::time::Duration;
2428

25-
use crate::eval::EvalContext;
26-
use crate::parser::Token;
27-
2829
mod bounds;
2930
mod eval;
3031
mod parser;
@@ -54,6 +55,10 @@ struct Args {
5455
/// Enable verbose output
5556
#[arg(short, long, default_value = "false")]
5657
verbose: bool,
58+
59+
/// Seed for the random number generator (Default: Current time)
60+
#[arg(short, long)]
61+
seed: Option<u64>,
5762
}
5863

5964
static LOOKING_GLASS: Emoji<'_, '_> = Emoji("🔍 ", "");
@@ -62,9 +67,10 @@ static IMAGE: Emoji<'_, '_> = Emoji("🗃️ ", "");
6267
static ERROR: Emoji<'_, '_> = Emoji("❌ ", "");
6368
static OK: Emoji<'_, '_> = Emoji("✅ ", "");
6469
static EYE: Emoji<'_, '_> = Emoji("👁️ ", "");
70+
static SEED: Emoji<'_, '_> = Emoji("🌱 ", "");
6571

6672
fn main() -> anyhow::Result<()> {
67-
let args = Args::parse();
73+
let mut args = Args::parse();
6874
// If we want to pass the arguments to a function, we need to clone them
6975
if args.input.starts_with("http") {
7076
// use the writer
@@ -81,6 +87,17 @@ fn main() -> anyhow::Result<()> {
8187
);
8288
}
8389

90+
// Determine which RNG to use based on the provided seed
91+
let seed = get_random_seed(&args);
92+
args.seed = Some(seed);
93+
let mut rng: Box<dyn RngCore> = Box::new(StdRng::seed_from_u64(seed));
94+
95+
println!(
96+
"{} Using Seed: {}",
97+
SEED,
98+
style(seed).bold().cyan()
99+
);
100+
84101
println!(
85102
"{} Parsing {} Expression{}...",
86103
LOOKING_GLASS,
@@ -124,7 +141,7 @@ fn main() -> anyhow::Result<()> {
124141
parsed.push((e.to_string(), tokens));
125142
}
126143

127-
handle_image(&args, &parsed)?;
144+
handle_image(&args, &parsed, &mut rng)?;
128145
Ok(())
129146
}
130147

@@ -138,6 +155,7 @@ fn download_image(url: &str) -> anyhow::Result<Vec<u8>> {
138155
fn handle_image(
139156
args: &Args,
140157
parsed: &[(String, Vec<Token>)],
158+
rand: &mut Box<dyn RngCore>,
141159
) -> anyhow::Result<(), anyhow::Error> {
142160
let img = match &args.input {
143161
file if file.starts_with("http") => download_image(&args.input)?,
@@ -195,26 +213,23 @@ fn handle_image(
195213

196214
spinner.set_message(format!("{} Processing mode: 󰸭 {}", IMAGE, style("PNG").bold().cyan()));
197215

198-
let out = process(img, parsed, args.no_state)?;
216+
let out = process(img, parsed, args, rand)?;
199217
out.save_with_format(output.clone(), format)?;
200218
}
201219
ImageFormat::Jpeg => {
202220
let img = image::load_from_memory(&img)?;
203221

204222
spinner.set_message(format!("{} Processing mode: 󰸭 {}", IMAGE, style("JPEG").bold().cyan()));
205223

206-
let out = process(img, parsed, args.no_state)?;
224+
let out = process(img, parsed, args, rand)?;
207225
out.save_with_format(output.clone(), format)?;
208226
}
209227
ImageFormat::Gif => {
210-
211228
let mut reader = std::io::Cursor::new(img);
212229
let decoder = GifDecoder::new(&mut reader)?;
213230
let [w, h] = [decoder.dimensions().0, decoder.dimensions().1];
214231
let frames = decoder.into_frames().collect_frames()?;
215232

216-
217-
218233
let output = std::fs::File::create(output.clone())?;
219234
let mut img_writer = BufWriter::new(output);
220235
let mut encoder = Encoder::new(&mut img_writer, w as u16, h as u16, &[])?;
@@ -224,12 +239,15 @@ fn handle_image(
224239

225240
spinner.set_message(format!("{} Processing mode: 󰸭 {} with {} frames", IMAGE, style("GIF").bold().cyan(), style(frames.len()).bold().cyan()));
226241

242+
let seed = args.seed.unwrap();
227243
(0..frames.len()).into_par_iter().for_each(|i| {
244+
let mut rng: Box<dyn RngCore> = Box::new(StdRng::seed_from_u64(seed));
245+
228246
let frame = frames.get(i).expect("Failed to get frame").to_owned();
229247
let delay = frame.delay().numer_denom_ms().0 as u16;
230248
let img = frame.into_buffer();
231249
let out =
232-
process(img.into(), parsed, args.no_state).expect("Failed to process frame");
250+
process(img.into(), parsed, args, &mut rng).expect("Failed to process frame");
233251
let mut bytes = out.as_bytes().to_vec();
234252

235253
let mut new_frame = gif::Frame::from_rgba_speed(w as u16, h as u16, &mut bytes, 10);
@@ -278,7 +296,8 @@ fn handle_image(
278296
fn process(
279297
mut img: DynamicImage,
280298
expressions: &[(String, Vec<Token>)],
281-
no_state: bool,
299+
args: &Args,
300+
rand: &mut Box<dyn RngCore>, // Accept boxed RNG here
282301
) -> anyhow::Result<DynamicImage> {
283302
let mut output_image = DynamicImage::new(img.width(), img.height(), img.color());
284303

@@ -309,10 +328,10 @@ fn process(
309328
rgba: colors.0,
310329
saved_rgb: [sr, sg, sb],
311330
position: (x, y),
312-
ignore_state: no_state,
331+
ignore_state: args.no_state,
313332
},
314333
&img,
315-
rand::thread_rng(),
334+
rand,
316335
)
317336
.expect("Failed to evaluate");
318337

@@ -332,4 +351,15 @@ fn process(
332351

333352
fn strip_windows_prefix(path: &Path) -> PathBuf {
334353
path.to_str().and_then(|s| s.strip_prefix(r"\\?\")).map_or_else(|| path.to_path_buf(), PathBuf::from)
354+
}
355+
356+
fn get_random_seed(args: &Args) -> u64 {
357+
if args.seed.is_none() {
358+
return std::time::SystemTime::now()
359+
.duration_since(std::time::UNIX_EPOCH)
360+
.expect("Time went backwards")
361+
.as_nanos() as u64;
362+
}
363+
364+
args.seed.unwrap()
335365
}

0 commit comments

Comments
 (0)