diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..6e6d3ed --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,27 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in library 'yourprogram'", + "cargo": { + "args": [ + "test", + "--no-run", + "--lib", + "--package=yourprogram" + ], + "filter": { + "name": "yourprogram", + "kind": "lib" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + } + ] +} \ No newline at end of file diff --git a/src/asteroids.rs b/src/asteroids.rs index 937cfb4..1cb7e7d 100644 --- a/src/asteroids.rs +++ b/src/asteroids.rs @@ -89,7 +89,7 @@ impl Asteroid { AsteroidSize::Medium => 1.2, AsteroidSize::Small => 0.8, }, - Color::new(1., 1., 1., 0.5), + Color::new(1., 1., 1., 0.4), ); } } diff --git a/src/main.rs b/src/main.rs index 53cb979..07fd409 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,8 +16,11 @@ async fn main() { ..Default::default() }; set_camera(&cam); - let mut pop = Population::new(100); + let mut pop = Population::new(10); let mut speedup = false; + // for _ in 0..100000 * 5 { + // pop.update(); + // } loop { clear_background(BLACK); if is_key_pressed(KeyCode::S) { diff --git a/src/nn.rs b/src/nn.rs index be59630..bb2b7c9 100644 --- a/src/nn.rs +++ b/src/nn.rs @@ -1,3 +1,4 @@ +use macroquad::rand::gen_range; use nalgebra::*; use r::Rng; use rand_distr::StandardNormal; @@ -10,10 +11,10 @@ enum ActivationFunc { ReLU, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct NN { pub config: Vec, - weights: Vec>, + pub weights: Vec>, activ_func: ActivationFunc, mut_rate: f32, } @@ -63,8 +64,8 @@ impl NN { pub fn mutate(&mut self) { for weight in &mut self.weights { for ele in weight { - if r::random() { - *ele = r::thread_rng().sample::(StandardNormal) * 0.05; + if gen_range(0., 1.) < 0.05 { + *ele = r::thread_rng().sample::(StandardNormal); } } } diff --git a/src/player.rs b/src/player.rs index 8e8657b..33a6aac 100644 --- a/src/player.rs +++ b/src/player.rs @@ -40,7 +40,7 @@ impl Player { pub fn simulate(brain: NN, max_asteroids: usize) -> Self { assert_eq!( brain.config[0] - 1, - max_asteroids + 5, + max_asteroids * 3 + 5, "NN input size must match max_asteroids" ); let mut p = Player::new(); @@ -77,8 +77,7 @@ impl Player { self.lifespan += 1; let mut mag = 0.; let mut keys = vec![false, false, false, false]; - - self.asteroids_data.resize(self.max_asteroids, 0.); + self.asteroids_data.resize(self.max_asteroids * 3, 0.); let mut inputs = vec![ self.pos.x / screen_width() + 0.5, self.pos.y / screen_height() + 0.5, @@ -124,10 +123,10 @@ impl Player { self.vel += mag * self.dir - self.drag * self.vel.length() * self.vel; self.pos += self.vel; - if self.pos.x.abs() > screen_width() / 2. + 10. { + if self.pos.x.abs() > screen_width() * 0.5 + 10. { self.pos.x *= -1.; } - if self.pos.y.abs() > screen_height() / 2. + 10. { + if self.pos.y.abs() > screen_height() * 0.5 + 10. { self.pos.y *= -1.; } diff --git a/src/population.rs b/src/population.rs index 618178b..483f375 100644 --- a/src/population.rs +++ b/src/population.rs @@ -14,7 +14,7 @@ impl Population { Self { size, worlds: (0..size) - .map(|_| World::simulate(NN::new(vec![33, 16, 4]))) + .map(|_| World::simulate(NN::new(vec![89, 16, 4]))) .collect(), ..Default::default() } @@ -52,26 +52,38 @@ impl Population { pub fn next_gen(&mut self) { let total = self.worlds.iter().fold(0., |acc, x| acc + x.fitness()); - // self.worlds.sort_by(|a, b| b.fitness().cmp(&a.fitness())); - let mut new_worlds = Vec::new(); - // (0..self.size / 10) - // .map(|i| World::simulate(self.worlds[i].see_brain().to_owned())) - // .collect::>(); + self.worlds + .sort_by(|a, b| b.fitness().partial_cmp(&a.fitness()).unwrap()); + let mut new_worlds = (0..self.size / 10) + .map(|i| World::simulate(self.worlds[i].see_brain().to_owned())) + .collect::>(); + // println!( + // "Total fitness: {} {} {}", + // total, + // self.worlds[0].fitness(), + // self.worlds[1].fitness() + // ); while new_worlds.len() < self.size { let rands = (gen_range(0., total), gen_range(0., total)); + // println!("rands: {} {}", rands.0, rands.1); let mut sum = 0.; let (mut a, mut b) = (None, None); for world in &self.worlds { sum += world.fitness(); - if sum >= rands.0 { + if a.is_none() && sum >= rands.0 { a = Some(world.see_brain()); } - if sum >= rands.1 { + if b.is_none() && sum >= rands.1 { b = Some(world.see_brain()); } } + // println!("{}", &a.unwrap().weights[0]); + // println!("{}", &b.unwrap().weights[0]); let mut new_brain = NN::crossover(a.unwrap(), b.unwrap()); + // println!("{}", &a.unwrap().weights[0]); + // println!("{}", &b.unwrap().weights[0]); + // println!("{}", &new_brain.weights[0]); new_brain.mutate(); new_worlds.push(World::simulate(new_brain)); } diff --git a/src/world.rs b/src/world.rs index f1487ff..aa17faf 100644 --- a/src/world.rs +++ b/src/world.rs @@ -36,12 +36,24 @@ impl World { } pub fn fitness(&self) -> f32 { - self.score as f32 - + self.player.lifespan as f32 * 0.01 - + if self.player.shots > 0 { - self.score as f32 / self.player.shots as f32 * 10. + // println!( + // "{} {} {}", + // self.score as f32, + // self.player.lifespan as f32 * 0.001, + // if self.player.shots > 0 { + // self.score as f32 / self.player.shots as f32 * 5. + // } else { + // 0. + // } + // ); + (self.score + 1) as f32 + * 10. + * self.player.lifespan as f32 + * if self.player.shots > 0 { + (self.score as f32 / self.player.shots as f32) + * (self.score as f32 / self.player.shots as f32) } else { - 0. + 1. } }