diff --git a/simulator/Cargo.lock b/simulator/Cargo.lock index 1f2381e..54eab11 100644 --- a/simulator/Cargo.lock +++ b/simulator/Cargo.lock @@ -395,6 +395,16 @@ dependencies = [ "png", ] +[[package]] +name = "indexmap" +version = "2.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.2" @@ -661,6 +671,45 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "serde" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_spanned" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c187e0a369471c550af028cba56d4f4fa2a489ad28151ae95a4d340a2a8b782" +dependencies = [ + "serde_core", +] + [[package]] name = "simba" version = "0.10.2" @@ -689,6 +738,8 @@ dependencies = [ "parry2d", "rand", "rayon", + "serde", + "toml", ] [[package]] @@ -752,6 +803,45 @@ dependencies = [ "syn", ] +[[package]] +name = "toml" +version = "1.2.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5755c51863cdac711f1956baf8eb9588e060755f7d3c4c979e70ea1e1f32cd7" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow", +] + +[[package]] +name = "toml_datetime" +version = "1.1.3+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "173f25c883cd2176a2d8212155638c0a60d484a9d84546d270866f40f56b6e49" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_parser" +version = "1.1.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baa693a8032d7e1cada7d0041e96126df243179ff061456783ac7f12bda4744c" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_writer" +version = "1.2.0+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "009dbb7700f11c55ef83aebbe4c0e9cf1f1ac0844bd8d6f00dc38f302026e13a" + [[package]] name = "ttf-parser" version = "0.25.1" @@ -820,6 +910,12 @@ dependencies = [ "windows-link", ] +[[package]] +name = "winnow" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3b93199df0ab874cdf0c6a94f0a0dc3423fe41f1cafb62b8bd7b1deb277aef" + [[package]] name = "zlib-rs" version = "0.6.8" diff --git a/simulator/Cargo.toml b/simulator/Cargo.toml index afa3347..82ce93b 100644 --- a/simulator/Cargo.toml +++ b/simulator/Cargo.toml @@ -10,3 +10,5 @@ macroquad = "0.4.16" parry2d = "0.31.1" rand_gen = { package = "rand", version = "0.10.3" } rayon = "1.12.0" +serde = { version = "1.0.229", features = ["derive"] } +toml = "1.2.1" diff --git a/simulator/conf.toml b/simulator/conf.toml new file mode 100644 index 0000000..58fcffe --- /dev/null +++ b/simulator/conf.toml @@ -0,0 +1,19 @@ +food_amount = 120 +red_count = 10 +blu_count = 10 +kill_radius = 140 +kill_self = false + +[red_policy] +aggressive = true +food = [1,0] +kin = [-0.3,0.02] +nonkin = [0.3,-0.02] +none= [0.3, 0.01] + +[blu_policy] +aggressive = false +food = [1,0] +kin = [-0.3,0.02] +nonkin = [0.3,-0.02] +none= [0.3, 0.01] diff --git a/simulator/src/main.rs b/simulator/src/main.rs index 4ea0295..8d54387 100644 --- a/simulator/src/main.rs +++ b/simulator/src/main.rs @@ -1,6 +1,8 @@ use std::cmp::min; use std::collections::HashMap; use std::f32::consts::TAU; +use std::fs; +use std::path::PathBuf; use std::ptr; use std::sync::atomic::AtomicBool; use std::sync::atomic::Ordering; @@ -25,6 +27,8 @@ use rayon::iter::ParallelIterator; use clap::Parser; +use serde::Deserialize; + const VIEW_DIST: f32 = 180.0; const BOT_RADIUS: f32 = 16.0; const KILL_RADIUS: f32 = 140.0; @@ -43,6 +47,7 @@ struct Robot { collider: Ball, sensor: Sensor, team: Color, + policy: Policy, } impl Mortal for Robot { @@ -67,11 +72,12 @@ impl Mortal for Robot { } impl Robot { - fn new(pose: Pose, team: Color) -> Self { - Self { pose, alive: AtomicBool::new(true), collider: Ball::new(BOT_RADIUS), sensor: Sensor::None, team} + fn new(pose: Pose, team: Color, policy: Policy) -> Self { + Self { pose, alive: AtomicBool::new(true), collider: Ball::new(BOT_RADIUS), sensor: Sensor::None, team, policy} } - fn move_step(&mut self, forward: f32, right: f32) { + fn move_step(&mut self, forward_right: [f32;2]) { + let [forward, right] = forward_right; self.pose.translation += self.ahead() * forward; self.pose.rotation *= Rot2::from_angle(right); } @@ -117,13 +123,13 @@ impl Robot { pub fn policy(&mut self, sensor: &Sensor) -> bool{ match sensor { - Sensor::Food => self.move_step(1.0, 0.0), - Sensor::Kin => self.move_step(-0.3, 0.02), - Sensor::NonKin => self.move_step(0.3, -0.02), - Sensor::None => self.move_step(0.3, 0.01), + Sensor::Food => self.move_step(self.policy.food), + Sensor::Kin => self.move_step(self.policy.kin), + Sensor::NonKin => self.move_step(self.policy.nonkin), + Sensor::None => self.move_step(self.policy.none), } - return *sensor == Sensor::NonKin; + return *sensor == Sensor::NonKin && self.policy.aggressive; } } @@ -175,6 +181,26 @@ impl Food { } } +#[derive(Deserialize, Clone)] +struct Policy { + aggressive: bool, + food: [f32;2], + kin: [f32;2], + nonkin: [f32;2], + none: [f32;2], +} + +#[derive(Deserialize)] +struct SimConfig { + food_amount: usize, + red_count: usize, + red_policy: Policy, + blu_count: usize, + blu_policy: Policy, + kill_radius: f32, + kill_self: bool, +} + struct Simulation { robots: Vec, food: Vec, @@ -185,7 +211,7 @@ struct Simulation { } impl Simulation { - fn new(seed: u64) -> Self { + fn new(seed: u64, config: &SimConfig) -> Self { let mut rng = SmallRng::seed_from_u64(seed); let watchdog = 0; // iterations since something interesting let mut robots = Vec::new(); @@ -195,20 +221,24 @@ impl Simulation { let score = HashMap::new(); + let angle = Uniform::new(0.0, TAU).unwrap(); + // blue robots for i in 5..20 { - let angle = Uniform::new(0.0, TAU).unwrap(); let rand_angle = angle.sample(&mut rng); let pose:Pose = Pose::from_parts(Vec2 { x: 100.0, y: i as f32*50.0 }, Rot2::from_angle(rand_angle)); - robots.push(Robot::new(pose, BLUE)); + robots.push(Robot::new(pose, BLUE, config.blu_policy.clone())); + } + //red robots + for i in 5..20 { let rand_angle = angle.sample(&mut rng); let pose:Pose = Pose::from_parts(Vec2 { x: 1200.0, y: i as f32*50.0 }, Rot2::from_angle(rand_angle)); - robots.push(Robot::new(pose, RED)); + robots.push(Robot::new(pose, RED, config.red_policy.clone())); } let mut food = Vec::new(); - for _ in 0..120 { + for _ in 0..config.food_amount { let x = rng.random_range(200.0 .. 1100.0); let y = rng.random_range(200.0 .. 900.0); food.push(Food::new(Vec2 { x, y })); @@ -310,12 +340,12 @@ impl Simulation { } } -async fn run_simulation(seed: u64) -> (i32, i32) { - let mut sim = Simulation::new(seed); +async fn run_simulation(seed: u64, speedup: usize, config: &SimConfig) -> (i32, i32) { + let mut sim = Simulation::new(seed, &config); let mut loo = 1; loop { - let rendered = loo % 20 == 0; + let rendered = loo % speedup == 0; let result = sim.sim_step(); if rendered { @@ -330,8 +360,8 @@ async fn run_simulation(seed: u64) -> (i32, i32) { } } -fn run_headless_simulation(seed: u64) -> (i32, i32) { - let mut sim = Simulation::new(seed); +fn run_headless_simulation(seed: u64, config: &SimConfig) -> (i32, i32) { + let mut sim = Simulation::new(seed, config); loop { if let Some(result) = sim.sim_step() { @@ -352,9 +382,13 @@ struct Cli { #[arg(short, long)] run_count: Option, - ///// Sets a custom config file - //#[arg(short, long, value_name = "FILE")] - //config: Option, + /// how many simulation steps to skip between frames (default 1) + #[arg(short, long)] + gui_speedup: Option, + + /// Sets a custom config file + #[arg(short, long, value_name = "FILE")] + config: PathBuf, /// Turn debugging information on #[arg(short, long, action = clap::ArgAction::Count)] @@ -365,13 +399,17 @@ struct Cli { async fn main() { let cli = Cli::parse(); let iters = cli.run_count.unwrap_or(1); + let speedup = cli.gui_speedup.unwrap_or(5); + + let config = fs::read_to_string(cli.config).unwrap(); + let config: SimConfig = toml::from_str(&config).unwrap(); let (red, blu) = if cli.no_gui { - (0..iters).into_par_iter().map(run_headless_simulation).reduce(|| (0,0), |a,b| (a.0 + b.0, a.1 + b.1)) + (0..iters).into_par_iter().map(|s |run_headless_simulation(s,&config)).reduce(|| (0,0), |a,b| (a.0 + b.0, a.1 + b.1)) } else { let (mut rtot, mut btot) = (0, 0); for i in 0..iters { - let (red, blu) = run_simulation(i).await; + let (red, blu) = run_simulation(i, speedup, &config).await; rtot += red; btot += blu;