This commit is contained in:
sparshg 2023-01-08 03:29:59 +05:30
parent 3ce1fdb957
commit 7dea9e0406
8 changed files with 192 additions and 40 deletions

BIN
fast.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 B

BIN
pause.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

BIN
play.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 B

BIN
restart.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 B

View File

@ -4,7 +4,12 @@ mod player;
mod population;
mod world;
use macroquad::prelude::*;
use std::borrow::BorrowMut;
use macroquad::{
prelude::*,
ui::{hash, root_ui, widgets, Skin},
};
use population::Population;
pub const WIDTH: f32 = 800.;
@ -22,6 +27,10 @@ fn window_conf() -> Conf {
#[macroquad::main(window_conf)]
async fn main() {
rand::srand(macroquad::miniquad::date::now() as _);
let pause = load_texture("pause.png").await.unwrap();
let play = load_texture("play.png").await.unwrap();
let fast = load_texture("fast.png").await.unwrap();
let restart = load_texture("restart.png").await.unwrap();
let th = (screen_height() - HEIGHT) * 0.5;
let gamecam = Camera2D {
@ -51,29 +60,151 @@ async fn main() {
// };
let mut pop = Population::new(100);
let mut speedup = false;
let mut paused = false;
let mut checkbox = false;
let mut combobox = 0;
let mut text = String::new();
let mut number = 0.0;
let skin = {
let boxed = root_ui()
.style_builder()
.background(Image {
width: 3,
height: 3,
bytes: vec![
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255,
],
})
.background_margin(RectOffset::new(1., 1., 1., 1.));
let window_style = root_ui()
.style_builder()
.background(Image {
width: 1,
height: 1,
bytes: vec![0; 4],
})
.background_margin(RectOffset::new(0., 0., 0., 0.))
.build();
let button_style = boxed
.color_hovered(RED)
.color_clicked(BLUE)
.text_color(WHITE)
.text_color_hovered(WHITE)
.text_color_clicked(WHITE)
.margin(RectOffset::new(10., 10., 8., 8.))
.build();
let label_style = root_ui()
.style_builder()
.text_color(WHITE)
.font_size(24)
.margin(RectOffset::new(5., 5., 4., 4.))
.build();
Skin {
window_style,
button_style,
label_style,
margin: 0.,
..root_ui().default_skin()
}
};
root_ui().push_skin(&skin);
loop {
// set_camera(&cam);
clear_background(BLACK);
if is_key_pressed(KeyCode::S) {
speedup = !speedup;
}
if speedup {
if !paused {
for _ in 0..1000 {
pop.update();
}
}
} else {
if !paused {
pop.update();
}
pop.draw();
}
draw_rectangle_lines(-WIDTH * 0.5, -HEIGHT * 0.5, WIDTH, HEIGHT, 2., WHITE);
draw_rectangle_lines(
WIDTH * 0.5 + th,
-HEIGHT * 0.5,
screen_width() - WIDTH - 3. * th,
34.,
2.,
WHITE,
);
draw_rectangle_lines(
WIDTH * 0.5 + th,
-HEIGHT * 0.5 + (screen_height() - 3. * th) * 0.5 - 34.,
screen_width() - WIDTH - 3. * th,
34.,
2.,
WHITE,
);
set_camera(&maincam);
// draw_circle(0., 0., 20., RED);
pop.worlds[0].see_brain().draw(
pop.worlds[0].player.draw_brain(
screen_width() - WIDTH - 3. * th,
(screen_height() - 3. * th) * 0.5,
);
set_camera(&gamecam);
root_ui().window(
hash!(),
vec2(WIDTH + 2. * th, th),
vec2(screen_width() - WIDTH - 3. * th + 1., 34.),
|ui| {
ui.label(None, &format!("Generation: {}", pop.gen));
ui.same_line(278.);
widgets::Button::new("Load Model").ui(ui);
ui.same_line(0.);
widgets::Button::new("Save Model").ui(ui);
ui.same_line(0.);
if widgets::Button::new(fast).ui(ui) {
speedup = !speedup;
};
ui.same_line(0.);
if widgets::Button::new(restart).ui(ui) {
pop = Population::new(100);
};
ui.same_line(0.);
if widgets::Button::new(if paused { play } else { pause }).ui(ui) {
paused = !paused;
};
},
);
root_ui().window(
hash!(),
vec2(WIDTH + 2. * th, (screen_height() - th) * 0.5 - 34.),
vec2(screen_width() - WIDTH - 3. * th + 1., 34.),
|ui| {
ui.label(None, &format!("Generation: {}", pop.gen));
ui.same_line(278.);
widgets::Button::new("Load Model").ui(ui);
ui.same_line(0.);
widgets::Button::new("Save Model").ui(ui);
ui.same_line(0.);
if widgets::Button::new(fast).ui(ui) {
speedup = !speedup;
};
ui.same_line(0.);
if widgets::Button::new(restart).ui(ui) {
pop = Population::new(100);
};
ui.same_line(0.);
if widgets::Button::new(if paused { play } else { pause }).ui(ui) {
paused = !paused;
};
},
);
// set_camera(&maincam);
// draw_texture_ex(

View File

@ -78,10 +78,10 @@ impl NN {
}
}
pub fn feed_forward(&self, inputs: Vec<f32>) -> Vec<f32> {
pub fn feed_forward(&self, inputs: &Vec<f32>) -> Vec<f32> {
// println!("inputs: {:?}", inputs);
let mut y = DMatrix::from_vec(inputs.len(), 1, inputs);
for i in 0..self.config.len() - 1 {
let mut y = DMatrix::from_vec(inputs.len(), 1, inputs.to_vec());
for i in 0..self.config.len() - 2 {
y = (&self.weights[i] * y.insert_row(self.config[i] - 1, 1.)).map(|x| {
match self.activ_func {
ActivationFunc::ReLU => x.max(0.),
@ -89,13 +89,14 @@ impl NN {
ActivationFunc::Tanh => x.tanh(),
}
});
// println!("w{}: {}", i, self.weights[i]);
// println!("y: {}", y);
}
let i = self.config.len() - 2;
y = (&self.weights[i] * y.insert_row(self.config[i] - 1, 1.))
.map(|x| 1. / (1. + (-x).exp()));
y.column(0).data.into_slice().to_vec()
}
pub fn draw(&self, width: f32, height: f32) {
pub fn draw(&self, width: f32, height: f32, inputs: &Vec<f32>) {
draw_rectangle_lines(-width * 0.5, -height * 0.5, width, height, 2., WHITE);
let width = width * 0.8;
@ -106,31 +107,34 @@ impl NN {
for (i, layer) in self
.config
.iter()
.take(self.config.len() - 1)
.map(|x| x - 1)
.chain(self.config.last().map(|&x| x))
// .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 {
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()
for (k, j, p1, p2) in p1s.iter().enumerate().flat_map(|(k, x)| {
p2s.iter()
.take(p2s.len() - if i == self.config.len() - 1 { 0 } else { 1 })
.enumerate()
.flat_map(|(k, x)| p2s.iter().enumerate().map(move |(j, y)| (k, j, *x, *y)))
{
.map(move |(j, y)| (k, j, *x, *y))
}) {
let weight = *self.weights[i - 1].index((j, k));
let c = if weight < 0. { 0. } else { 1. };
draw_line(
p1.0,
p1.1,
p2.0,
p2.1,
1.,
Color::new(1., 1., 1., (self.weights[i - 1].index((j, k))).abs()),
Color::new(1., c, c, weight.abs()),
);
}
for p in &p1s {
@ -142,6 +146,10 @@ impl NN {
draw_circle(p.0, p.1, 10., WHITE);
draw_circle(p.0, p.1, 9., BLACK);
}
draw_rectangle(width * 0.45, height * 0.45, 10., 10., RED);
draw_text("-ve", width * 0.45 + 20., height * 0.45 + 10., 20., WHITE);
draw_rectangle(width * 0.45, height * 0.45 + 20., 10., 10., WHITE);
draw_text("+ve", width * 0.45 + 20., height * 0.45 + 30., 20., WHITE);
}
pub fn export(&self) -> String {

View File

@ -6,14 +6,16 @@ use crate::{asteroids::Asteroid, nn::NN, HEIGHT, WIDTH};
#[derive(Default)]
pub struct Player {
pub pos: Vec2,
pub vel: Vec2,
vel: Vec2,
acc: f32,
pub dir: Vec2,
rot: f32,
drag: f32,
bullets: Vec<Bullet>,
asteroid: Option<Asteroid>,
asteroid_data: Vec<(f32, f32, f32)>,
inputs: Vec<f32>,
outputs: Vec<bool>,
// asteroid_data: Vec<(f32, f32, f32)>,
last_shot: u8,
shot_interval: u8,
pub brain: Option<NN>,
@ -36,6 +38,7 @@ impl Player {
alive: true,
debug: false,
shots: 4,
outputs: vec![false; 4],
..Default::default()
}
@ -51,7 +54,7 @@ impl Player {
// );
p.brain = Some(brain);
} else {
p.brain = Some(NN::new(vec![5, 8, 8, 4]));
p.brain = Some(NN::new(vec![5, 6, 6, 4]));
}
p
}
@ -117,9 +120,9 @@ impl Player {
self.lifespan += 1;
self.last_shot += 1;
self.acc = 0.;
let mut keys = vec![false, false, false, false];
self.outputs = vec![false; 4];
if let Some(ast) = self.asteroid.as_ref() {
let inputs = vec![
self.inputs = vec![
(ast.pos - self.pos).length() / WIDTH,
self.dir.angle_between(ast.pos - self.pos),
(ast.vel - self.vel).x / 11.,
@ -140,21 +143,25 @@ impl Player {
// );
if let Some(brain) = &self.brain {
keys = brain.feed_forward(inputs).iter().map(|&x| x > 0.).collect();
self.outputs = brain
.feed_forward(&self.inputs)
.iter()
.map(|&x| x > 0.85)
.collect();
}
}
if is_key_down(KeyCode::Right) && self.debug || keys[0] {
if is_key_down(KeyCode::Right) && self.debug || self.outputs[0] {
self.rot = (self.rot + 0.1 + TAU as f32) % TAU as f32;
self.dir = vec2(self.rot.cos(), self.rot.sin());
}
if is_key_down(KeyCode::Left) && self.debug || keys[1] {
if is_key_down(KeyCode::Left) && self.debug || self.outputs[1] {
self.rot = (self.rot - 0.1 + TAU as f32) % TAU as f32;
self.dir = vec2(self.rot.cos(), self.rot.sin());
}
if is_key_down(KeyCode::Up) && self.debug || keys[2] {
if is_key_down(KeyCode::Up) && self.debug || self.outputs[2] {
self.acc = 0.14;
}
if is_key_down(KeyCode::Space) && self.debug || keys[3] {
if is_key_down(KeyCode::Space) && self.debug || self.outputs[3] {
if self.last_shot > self.shot_interval {
self.last_shot = 0;
self.shots += 1;
@ -243,6 +250,12 @@ impl Player {
bullet.draw();
}
}
pub fn draw_brain(&self, width: f32, height: f32) {
if let Some(brain) = &self.brain {
brain.draw(width, height, &self.inputs);
}
}
}
struct Bullet {

View File

@ -5,7 +5,7 @@ use crate::{nn::NN, world::World, HEIGHT, WIDTH};
#[derive(Default)]
pub struct Population {
size: usize,
gen: i32,
pub gen: i32,
best: bool,
pub worlds: Vec<World>,
}
@ -49,13 +49,13 @@ impl Population {
world.draw();
}
}
draw_text(
&format!("Gen: {}", self.gen),
-150. + WIDTH * 0.5,
30. - HEIGHT * 0.5,
32.,
WHITE,
);
// draw_text(
// &format!("Gen: {}", self.gen),
// -150. + WIDTH * 0.5,
// 30. - HEIGHT * 0.5,
// 32.,
// WHITE,
// );
// draw black background outside the screen
let th = (screen_height() - HEIGHT) * 0.5;