From 3ce1fdb9574d32625d6912d0f8bce366060cb90d Mon Sep 17 00:00:00 2001 From: sparshg <43041139+sparshg@users.noreply.github.com> Date: Sat, 7 Jan 2023 04:17:57 +0530 Subject: [PATCH] visuals --- models/brain.json | 2 +- src/asteroids.rs | 18 ++++++------- src/main.rs | 66 ++++++++++++++++++++++++++++++++++++++++++++--- src/nn.rs | 59 ++++++++++++++++++++++++++++++++++++++---- src/player.rs | 23 +++++++++-------- src/population.rs | 28 +++++++++++++++++--- src/world.rs | 2 +- 7 files changed, 163 insertions(+), 35 deletions(-) diff --git a/models/brain.json b/models/brain.json index 7bd34da..e6d5137 100644 --- a/models/brain.json +++ b/models/brain.json @@ -1 +1 @@ -{"config":[5,9,9,4],"weights":[[[-0.14426763,0.30192375,-0.33760154,0.56687576,0.5898287,-0.074536264,-0.6543682,-0.8446751,0.044597864,0.4060799,0.09555614,0.13340633,0.22922766,0.67137265,0.9608935,0.6129314,-0.24052018,-0.38112265,-0.5038841,-0.12603956,-0.4028914,0.18523669,0.07864928,0.06674278,0.2717425,0.6340071,0.61368537,0.8703921,-0.38861758,0.5550103,-0.26274413,0.5074116,-0.7821254,0.049322072,-0.2819178,-0.54028326,-0.4513356,-0.5652218,0.50929916,-0.42176604],8,5],[[-0.8103107,0.8446312,-0.40203577,0.54887617,-0.44233263,0.31166792,-0.33348525,0.40876722,0.26672268,-0.712453,-0.21693909,-0.35327882,-0.7854557,0.43809295,0.59911966,-0.071552694,0.3788359,-0.4907685,0.7868428,0.63808143,-0.50622714,0.08628023,-0.8824939,0.24896917,0.63522625,-0.50140214,0.9587381,0.5064759,0.040097475,0.24041378,-0.12401252,0.10650039,-0.8819831,0.29062068,-0.26787168,0.45351043,-0.2870677,-0.24404618,0.8434694,0.30426964,0.31458378,-0.8161984,-0.12195361,0.8177855,-0.57765794,0.89029014,0.75471187,0.5454526,-0.2778288,0.5250077,-0.71220773,0.58331454,0.368407,-1.0235562,-0.057651043,0.6541481,0.49730933,0.23280966,0.9288963,0.42811918,-0.45282865,0.22473057,0.9463216,0.5010747,-0.30762756,-0.5731837,0.8379146,0.06879502,-0.23158616,-0.6971844,0.4713143,0.1294421],8,9],[[0.45169508,-0.31192523,-0.74151075,-0.40177822,0.9622052,0.47277915,-0.76076436,0.00037801266,-0.36617398,0.89722514,0.89538515,0.99763775,1.3664851,-0.09313524,-0.5705385,0.059523106,-0.4970879,0.06191349,0.61592185,0.63810134,-0.97980994,-0.673075,0.025918722,0.5754194,1.2249401,-0.1614248,0.8438246,-0.6717344,0.83650386,-2.2065213,-0.051995337,0.19302869,-0.9978042,0.5303961,-1.4841839,0.118329406],4,9]],"activ_func":"ReLU","mut_rate":0.04} \ No newline at end of file +{"config":[6,9,9,4],"weights":[[[0.3488082,-0.09199099,-0.11929926,-1.0631673,-0.23543529,0.12705088,-1.0842022,-1.1801648,1.3452269,-0.105297334,0.7070266,-0.49821422,-0.9919794,-0.4586555,0.38327622,1.2620807,-0.8927275,0.72946376,0.36548716,0.3453985,0.24704376,1.1178607,-0.73745847,-0.36780706,0.5647091,-0.29108286,1.710524,1.0728852,-0.8066526,-0.28913066,0.14346941,-1.0912626,0.36901304,0.7923526,-0.51800287,-0.72875357,0.8539478,-1.473583,0.68293977,0.18473642,0.0003245327,-0.58371824,0.48150238,0.3494165,-0.23288698,-1.0439657,-0.26875693,0.5452296],8,6],[[-2.3292994,-0.26192483,-0.90176463,2.324304,-1.9353858,-0.14891693,-0.52935755,0.76884156,0.1082592,-0.9176799,0.6898619,0.7002196,0.19165382,-0.00026388586,-2.0727108,-0.43361717,0.4825783,0.5469626,1.5679779,1.6802235,0.5569048,0.2322176,-1.2066526,0.7200245,-0.54737276,-0.15166411,0.19801892,0.040810376,1.3895321,-0.08859847,-1.2233515,-0.063391574,0.10386248,-1.1793425,0.47050527,-1.7380185,-1.7678633,0.42901033,-0.017297065,0.4843002,-1.3651237,-0.24331652,0.2636839,0.7167474,-1.2047871,2.1309357,-1.3384317,2.6571567,0.044456493,-1.8444118,-0.52083886,0.32806322,0.088446766,-0.009452653,0.20716749,-0.911177,-0.74860054,0.16590308,0.46789008,0.9035914,0.64725244,-0.30468005,0.27064824,0.39474502,2.02378,-1.4614658,-0.84020156,0.69931465,-0.51446456,0.24423209,1.2651527,-0.45960972],8,9],[[-0.585348,0.5383882,-0.97360545,0.032165576,-0.049561385,0.24148509,-0.8100511,-0.6426556,1.059696,0.85932785,0.6353762,-0.9978614,1.9525132,-0.10112733,-0.5321224,0.443344,-0.37738746,-0.99622214,-1.3957877,0.6381588,-0.47502336,1.0980062,0.047556207,-0.008916257,-0.8662841,-0.45772696,1.6571641,0.9795985,0.098027825,-1.6333683,0.58037895,1.2487193,-1.0500598,-0.44345784,-2.4462621,0.5654464],4,9]],"activ_func":"ReLU","mut_rate":0.05} \ No newline at end of file diff --git a/src/asteroids.rs b/src/asteroids.rs index 2eb9fef..ec23b4e 100644 --- a/src/asteroids.rs +++ b/src/asteroids.rs @@ -1,5 +1,5 @@ +use crate::{HEIGHT, WIDTH}; use macroquad::{prelude::*, rand::gen_range}; - #[derive(Clone)] pub enum AsteroidSize { Large, @@ -22,9 +22,9 @@ pub struct Asteroid { impl Asteroid { pub fn new(size: AsteroidSize) -> Self { let (sides, radius) = match size { - AsteroidSize::Large => (gen_range(6, 10), gen_range(40., 50.)), - AsteroidSize::Medium => (gen_range(5, 6), gen_range(30., 40.)), - AsteroidSize::Small => (gen_range(3, 5), 20.), + AsteroidSize::Large => (gen_range(6, 10), gen_range(50., 65.)), + AsteroidSize::Medium => (gen_range(5, 6), gen_range(35., 50.)), + AsteroidSize::Small => (gen_range(3, 5), 25.), }; let mut r = vec2( if gen_range(0., 1.) > 0.5 { -1. } else { 1. }, @@ -33,10 +33,7 @@ impl Asteroid { if gen_range(0., 1.) > 0.5 { r = vec2(r.y, r.x); } - r *= vec2( - screen_width() * 0.5 + radius, - screen_height() * 0.5 + radius, - ); + r *= vec2(WIDTH * 0.5 + radius, HEIGHT * 0.5 + radius); Self { pos: r, vel: 0.001 * -r @@ -74,10 +71,10 @@ impl Asteroid { pub fn update(&mut self) { self.pos += self.vel; self.rot += self.omega; - if self.pos.x.abs() > screen_width() * 0.5 + self.radius { + if self.pos.x.abs() > WIDTH * 0.5 + self.radius { self.pos.x *= -1.; } - if self.pos.y.abs() > screen_height() * 0.5 + self.radius { + if self.pos.y.abs() > HEIGHT * 0.5 + self.radius { self.pos.y *= -1.; } } @@ -94,6 +91,7 @@ impl Asteroid { AsteroidSize::Medium => 1.2, AsteroidSize::Small => 0.8, }, + // WHITE, Color::new(1., 1., 1., 0.4), ); } diff --git a/src/main.rs b/src/main.rs index 71b77a2..70dd8c9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,17 +7,52 @@ mod world; use macroquad::prelude::*; use population::Population; -#[macroquad::main("Camera")] +pub const WIDTH: f32 = 800.; +pub const HEIGHT: f32 = 780.; + +fn window_conf() -> Conf { + Conf { + window_title: "Asteroids".to_string(), + // fullscreen: true, + window_width: 1400, + window_height: 800, + ..Default::default() + } +} +#[macroquad::main(window_conf)] async fn main() { rand::srand(macroquad::miniquad::date::now() as _); - let cam = Camera2D { + let th = (screen_height() - HEIGHT) * 0.5; + + let gamecam = Camera2D { zoom: vec2(2. / screen_width(), -2. / screen_height()), + offset: vec2((2. * th + WIDTH) / screen_width() - 1., 0.), ..Default::default() }; - set_camera(&cam); + let maincam = Camera2D { + zoom: vec2(2. / screen_width(), -2. / screen_height()), + offset: vec2( + (th + WIDTH) / screen_width(), + -((th + HEIGHT) * 0.5) / screen_height(), + ), + ..Default::default() + }; + // let mut cam = Camera2D::from_display_rect(Rect { + // x: 0., + // y: 0., + // w: 1600., + // h: 1200., + // }); + // cam.offset = vec2(1., -1.); + // // { + // zoom: vec2(2. / 800., -2. / 600.), + // // offset: vec2(-19. / 60., 0.), + // ..Default::default() + // }; let mut pop = Population::new(100); let mut speedup = false; loop { + // set_camera(&cam); clear_background(BLACK); if is_key_pressed(KeyCode::S) { speedup = !speedup; @@ -30,6 +65,29 @@ async fn main() { pop.update(); pop.draw(); } - next_frame().await + draw_rectangle_lines(-WIDTH * 0.5, -HEIGHT * 0.5, WIDTH, HEIGHT, 2., WHITE); + + set_camera(&maincam); + // draw_circle(0., 0., 20., RED); + pop.worlds[0].see_brain().draw( + screen_width() - WIDTH - 3. * th, + (screen_height() - 3. * th) * 0.5, + ); + set_camera(&gamecam); + + // set_camera(&maincam); + // draw_texture_ex( + // target.texture, + // 0., + // 0., + // Color::new(1., 1., 1., 0.3), + // DrawTextureParams { + // flip_y: true, + // ..Default::default() + // }, + // ); + // set_camera(&cam); + + next_frame().await; } } diff --git a/src/nn.rs b/src/nn.rs index a901bc9..58fec5d 100644 --- a/src/nn.rs +++ b/src/nn.rs @@ -1,4 +1,4 @@ -use macroquad::rand::gen_range; +use macroquad::{prelude::*, rand::gen_range}; use nalgebra::*; use r::Rng; use rand_distr::StandardNormal; @@ -17,7 +17,7 @@ enum ActivationFunc { #[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct NN { pub config: Vec, - weights: Vec>, + pub weights: Vec>, activ_func: ActivationFunc, mut_rate: f32, } @@ -45,7 +45,7 @@ impl NN { }) .collect(), - mut_rate: 0.04, + mut_rate: 0.05, ..Default::default() } } @@ -71,8 +71,8 @@ impl NN { for ele in weight { if gen_range(0., 1.) < self.mut_rate { // *ele += gen_range(-1., 1.); - *ele = gen_range(-1., 1.); - // *ele = r::thread_rng().sample::(StandardNormal); + // *ele = gen_range(-1., 1.); + *ele = r::thread_rng().sample::(StandardNormal); } } } @@ -95,6 +95,55 @@ impl NN { y.column(0).data.into_slice().to_vec() } + pub fn draw(&self, width: f32, height: f32) { + draw_rectangle_lines(-width * 0.5, -height * 0.5, width, height, 2., WHITE); + + let width = width * 0.8; + let height = height * 0.8; + let vspace = height / (self.config.iter().max().unwrap() - 1) as f32; + let mut p1s: Vec<(f32, f32)> = Vec::new(); + let mut p2s: Vec<(f32, f32)> = Vec::new(); + for (i, layer) in self + .config + .iter() + .take(self.config.len() - 1) + .map(|x| x - 1) + .chain(self.config.last().map(|&x| x)) + .enumerate() + { + p1s = p2s; + p2s = Vec::new(); + for neuron in 0..layer { + p2s.push(( + i as f32 * width / (self.config.len() - 1) as f32 - width * 0.5, + neuron as f32 * vspace - (vspace * (layer - 1) as f32) * 0.5, + )); + } + for (k, j, p1, p2) in p1s + .iter() + .enumerate() + .flat_map(|(k, x)| p2s.iter().enumerate().map(move |(j, y)| (k, j, *x, *y))) + { + draw_line( + p1.0, + p1.1, + p2.0, + p2.1, + 1., + Color::new(1., 1., 1., (self.weights[i - 1].index((j, k))).abs()), + ); + } + for p in &p1s { + draw_circle(p.0, p.1, 10., WHITE); + draw_circle(p.0, p.1, 9., BLACK); + } + } + for p in &p2s { + draw_circle(p.0, p.1, 10., WHITE); + draw_circle(p.0, p.1, 9., BLACK); + } + } + pub fn export(&self) -> String { serde_json::to_string(self).unwrap() } diff --git a/src/player.rs b/src/player.rs index fa5bf19..c8b4cd3 100644 --- a/src/player.rs +++ b/src/player.rs @@ -2,7 +2,7 @@ use std::{f32::consts::PI, f64::consts::TAU, iter}; use macroquad::{prelude::*, rand::gen_range}; -use crate::{asteroids::Asteroid, nn::NN}; +use crate::{asteroids::Asteroid, nn::NN, HEIGHT, WIDTH}; #[derive(Default)] pub struct Player { pub pos: Vec2, @@ -51,7 +51,7 @@ impl Player { // ); p.brain = Some(brain); } else { - p.brain = Some(NN::new(vec![4, 8, 8, 4])); + p.brain = Some(NN::new(vec![5, 8, 8, 4])); } p } @@ -60,7 +60,7 @@ impl Player { // To give more near asteroids data: // self.asteroid_data.push(( - // ((asteroid.pos - self.pos).length() - asteroid.radius) / screen_width(), + // ((asteroid.pos - self.pos).length() - asteroid.radius) / WIDTH, // self.dir.angle_between(asteroid.pos - self.pos), // (asteroid.vel - self.vel).length(), // )); @@ -120,9 +120,10 @@ impl Player { let mut keys = vec![false, false, false, false]; if let Some(ast) = self.asteroid.as_ref() { let inputs = vec![ - (ast.pos - self.pos).length() / screen_width(), + (ast.pos - self.pos).length() / WIDTH, self.dir.angle_between(ast.pos - self.pos), - (ast.vel - self.vel).length(), + (ast.vel - self.vel).x / 11., + (ast.vel - self.vel).y / 11., self.rot / TAU as f32, ]; @@ -174,19 +175,18 @@ impl Player { self.vel += self.acc * self.dir - self.drag * self.vel.length() * self.vel; self.pos += self.vel; - if self.pos.x.abs() > screen_width() * 0.5 + 10. { + if self.pos.x.abs() > WIDTH * 0.5 + 10. { self.pos.x *= -1.; } - if self.pos.y.abs() > screen_height() * 0.5 + 10. { + if self.pos.y.abs() > HEIGHT * 0.5 + 10. { self.pos.y *= -1.; } for bullet in &mut self.bullets { bullet.update(); } - self.bullets.retain(|b| { - b.alive && b.pos.x.abs() * 2. < screen_width() && b.pos.y.abs() * 2. < screen_height() - }); + self.bullets + .retain(|b| b.alive && b.pos.x.abs() * 2. < WIDTH && b.pos.y.abs() * 2. < HEIGHT); if self.debug { self.draw(); } @@ -197,6 +197,7 @@ impl Player { pub fn draw(&self) { let color = match self.color { Some(c) => c, + // None => WHITE, None => Color::new(1., 1., 1., 0.3), }; let p1 = self.pos + self.dir * 20.; @@ -219,7 +220,7 @@ impl Player { // let p = self.pos // + self.dir.rotate(Vec2::from_angle(self.asteroid_data[0].1)) // * self.asteroid_data[0].0 - // * screen_width(); + // * WIDTH; draw_line(self.pos.x, self.pos.y, ast.pos.x, ast.pos.y, 1., GRAY); } diff --git a/src/population.rs b/src/population.rs index 2296a31..4ba6fab 100644 --- a/src/population.rs +++ b/src/population.rs @@ -1,6 +1,6 @@ use macroquad::{prelude::*, rand::gen_range}; -use crate::{nn::NN, world::World}; +use crate::{nn::NN, world::World, HEIGHT, WIDTH}; #[derive(Default)] pub struct Population { @@ -34,6 +34,9 @@ impl Population { if is_key_pressed(KeyCode::Z) { self.best = !self.best; } + if is_key_pressed(KeyCode::Space) { + self.worlds[0].export_brain(); + } } pub fn draw(&self) { @@ -48,11 +51,30 @@ impl Population { } draw_text( &format!("Gen: {}", self.gen), - -150. + screen_width() * 0.5, - 30. - screen_height() * 0.5, + -150. + WIDTH * 0.5, + 30. - HEIGHT * 0.5, 32., WHITE, ); + + // draw black background outside the screen + let th = (screen_height() - HEIGHT) * 0.5; + draw_rectangle(-WIDTH * 0.5, -screen_height() * 0.5, WIDTH, th, BLACK); + draw_rectangle(-WIDTH * 0.5, screen_height() * 0.5 - th, WIDTH, th, BLACK); + draw_rectangle( + -WIDTH * 0.5 - th, + -screen_height() * 0.5, + th, + screen_height(), + BLACK, + ); + draw_rectangle( + WIDTH * 0.5, + -screen_height() * 0.5, + screen_width() - WIDTH, + screen_height(), + BLACK, + ); } pub fn next_gen(&mut self) { diff --git a/src/world.rs b/src/world.rs index 09ac4f6..844fd45 100644 --- a/src/world.rs +++ b/src/world.rs @@ -38,7 +38,7 @@ impl World { self.player.brain.as_ref().unwrap() } - pub fn export_brian(&self) { + pub fn export_brain(&self) { let json = self.player.brain.as_ref().unwrap().export(); std::fs::create_dir_all("models").expect("Unable to create directory"); std::fs::write("models/brain.json", json).expect("Unable to write file");