ui bars
This commit is contained in:
parent
3ce1fdb957
commit
7dea9e0406
Binary file not shown.
After Width: | Height: | Size: 398 B |
143
src/main.rs
143
src/main.rs
|
@ -4,7 +4,12 @@ mod player;
|
||||||
mod population;
|
mod population;
|
||||||
mod world;
|
mod world;
|
||||||
|
|
||||||
use macroquad::prelude::*;
|
use std::borrow::BorrowMut;
|
||||||
|
|
||||||
|
use macroquad::{
|
||||||
|
prelude::*,
|
||||||
|
ui::{hash, root_ui, widgets, Skin},
|
||||||
|
};
|
||||||
use population::Population;
|
use population::Population;
|
||||||
|
|
||||||
pub const WIDTH: f32 = 800.;
|
pub const WIDTH: f32 = 800.;
|
||||||
|
@ -22,6 +27,10 @@ fn window_conf() -> Conf {
|
||||||
#[macroquad::main(window_conf)]
|
#[macroquad::main(window_conf)]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
rand::srand(macroquad::miniquad::date::now() as _);
|
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 th = (screen_height() - HEIGHT) * 0.5;
|
||||||
|
|
||||||
let gamecam = Camera2D {
|
let gamecam = Camera2D {
|
||||||
|
@ -51,29 +60,151 @@ async fn main() {
|
||||||
// };
|
// };
|
||||||
let mut pop = Population::new(100);
|
let mut pop = Population::new(100);
|
||||||
let mut speedup = false;
|
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 {
|
loop {
|
||||||
// set_camera(&cam);
|
|
||||||
clear_background(BLACK);
|
clear_background(BLACK);
|
||||||
if is_key_pressed(KeyCode::S) {
|
if is_key_pressed(KeyCode::S) {
|
||||||
speedup = !speedup;
|
speedup = !speedup;
|
||||||
}
|
}
|
||||||
if speedup {
|
if speedup {
|
||||||
for _ in 0..1000 {
|
if !paused {
|
||||||
pop.update();
|
for _ in 0..1000 {
|
||||||
|
pop.update();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
pop.update();
|
if !paused {
|
||||||
|
pop.update();
|
||||||
|
}
|
||||||
pop.draw();
|
pop.draw();
|
||||||
}
|
}
|
||||||
draw_rectangle_lines(-WIDTH * 0.5, -HEIGHT * 0.5, WIDTH, HEIGHT, 2., WHITE);
|
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);
|
set_camera(&maincam);
|
||||||
// draw_circle(0., 0., 20., RED);
|
// draw_circle(0., 0., 20., RED);
|
||||||
pop.worlds[0].see_brain().draw(
|
pop.worlds[0].player.draw_brain(
|
||||||
screen_width() - WIDTH - 3. * th,
|
screen_width() - WIDTH - 3. * th,
|
||||||
(screen_height() - 3. * th) * 0.5,
|
(screen_height() - 3. * th) * 0.5,
|
||||||
);
|
);
|
||||||
|
|
||||||
set_camera(&gamecam);
|
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);
|
// set_camera(&maincam);
|
||||||
// draw_texture_ex(
|
// draw_texture_ex(
|
||||||
|
|
40
src/nn.rs
40
src/nn.rs
|
@ -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);
|
// println!("inputs: {:?}", inputs);
|
||||||
let mut y = DMatrix::from_vec(inputs.len(), 1, inputs);
|
let mut y = DMatrix::from_vec(inputs.len(), 1, inputs.to_vec());
|
||||||
for i in 0..self.config.len() - 1 {
|
for i in 0..self.config.len() - 2 {
|
||||||
y = (&self.weights[i] * y.insert_row(self.config[i] - 1, 1.)).map(|x| {
|
y = (&self.weights[i] * y.insert_row(self.config[i] - 1, 1.)).map(|x| {
|
||||||
match self.activ_func {
|
match self.activ_func {
|
||||||
ActivationFunc::ReLU => x.max(0.),
|
ActivationFunc::ReLU => x.max(0.),
|
||||||
|
@ -89,13 +89,14 @@ impl NN {
|
||||||
ActivationFunc::Tanh => x.tanh(),
|
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()
|
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);
|
draw_rectangle_lines(-width * 0.5, -height * 0.5, width, height, 2., WHITE);
|
||||||
|
|
||||||
let width = width * 0.8;
|
let width = width * 0.8;
|
||||||
|
@ -106,31 +107,34 @@ impl NN {
|
||||||
for (i, layer) in self
|
for (i, layer) in self
|
||||||
.config
|
.config
|
||||||
.iter()
|
.iter()
|
||||||
.take(self.config.len() - 1)
|
// .take(self.config.len() - 1)
|
||||||
.map(|x| x - 1)
|
// .map(|x| x - 1)
|
||||||
.chain(self.config.last().map(|&x| x))
|
// .chain(self.config.last().map(|&x| x))
|
||||||
.enumerate()
|
.enumerate()
|
||||||
{
|
{
|
||||||
p1s = p2s;
|
p1s = p2s;
|
||||||
p2s = Vec::new();
|
p2s = Vec::new();
|
||||||
for neuron in 0..layer {
|
for neuron in 0..*layer {
|
||||||
p2s.push((
|
p2s.push((
|
||||||
i as f32 * width / (self.config.len() - 1) as f32 - width * 0.5,
|
i as f32 * width / (self.config.len() - 1) as f32 - width * 0.5,
|
||||||
neuron as f32 * vspace - (vspace * (layer - 1) as f32) * 0.5,
|
neuron as f32 * vspace - (vspace * (layer - 1) as f32) * 0.5,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
for (k, j, p1, p2) in p1s
|
for (k, j, p1, p2) in p1s.iter().enumerate().flat_map(|(k, x)| {
|
||||||
.iter()
|
p2s.iter()
|
||||||
.enumerate()
|
.take(p2s.len() - if i == self.config.len() - 1 { 0 } else { 1 })
|
||||||
.flat_map(|(k, x)| p2s.iter().enumerate().map(move |(j, y)| (k, j, *x, *y)))
|
.enumerate()
|
||||||
{
|
.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(
|
draw_line(
|
||||||
p1.0,
|
p1.0,
|
||||||
p1.1,
|
p1.1,
|
||||||
p2.0,
|
p2.0,
|
||||||
p2.1,
|
p2.1,
|
||||||
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 {
|
for p in &p1s {
|
||||||
|
@ -142,6 +146,10 @@ impl NN {
|
||||||
draw_circle(p.0, p.1, 10., WHITE);
|
draw_circle(p.0, p.1, 10., WHITE);
|
||||||
draw_circle(p.0, p.1, 9., BLACK);
|
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 {
|
pub fn export(&self) -> String {
|
||||||
|
|
|
@ -6,14 +6,16 @@ use crate::{asteroids::Asteroid, nn::NN, HEIGHT, WIDTH};
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Player {
|
pub struct Player {
|
||||||
pub pos: Vec2,
|
pub pos: Vec2,
|
||||||
pub vel: Vec2,
|
vel: Vec2,
|
||||||
acc: f32,
|
acc: f32,
|
||||||
pub dir: Vec2,
|
pub dir: Vec2,
|
||||||
rot: f32,
|
rot: f32,
|
||||||
drag: f32,
|
drag: f32,
|
||||||
bullets: Vec<Bullet>,
|
bullets: Vec<Bullet>,
|
||||||
asteroid: Option<Asteroid>,
|
asteroid: Option<Asteroid>,
|
||||||
asteroid_data: Vec<(f32, f32, f32)>,
|
inputs: Vec<f32>,
|
||||||
|
outputs: Vec<bool>,
|
||||||
|
// asteroid_data: Vec<(f32, f32, f32)>,
|
||||||
last_shot: u8,
|
last_shot: u8,
|
||||||
shot_interval: u8,
|
shot_interval: u8,
|
||||||
pub brain: Option<NN>,
|
pub brain: Option<NN>,
|
||||||
|
@ -36,6 +38,7 @@ impl Player {
|
||||||
alive: true,
|
alive: true,
|
||||||
debug: false,
|
debug: false,
|
||||||
shots: 4,
|
shots: 4,
|
||||||
|
outputs: vec![false; 4],
|
||||||
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
|
@ -51,7 +54,7 @@ impl Player {
|
||||||
// );
|
// );
|
||||||
p.brain = Some(brain);
|
p.brain = Some(brain);
|
||||||
} else {
|
} else {
|
||||||
p.brain = Some(NN::new(vec![5, 8, 8, 4]));
|
p.brain = Some(NN::new(vec![5, 6, 6, 4]));
|
||||||
}
|
}
|
||||||
p
|
p
|
||||||
}
|
}
|
||||||
|
@ -117,9 +120,9 @@ impl Player {
|
||||||
self.lifespan += 1;
|
self.lifespan += 1;
|
||||||
self.last_shot += 1;
|
self.last_shot += 1;
|
||||||
self.acc = 0.;
|
self.acc = 0.;
|
||||||
let mut keys = vec![false, false, false, false];
|
self.outputs = vec![false; 4];
|
||||||
if let Some(ast) = self.asteroid.as_ref() {
|
if let Some(ast) = self.asteroid.as_ref() {
|
||||||
let inputs = vec![
|
self.inputs = vec![
|
||||||
(ast.pos - self.pos).length() / WIDTH,
|
(ast.pos - self.pos).length() / WIDTH,
|
||||||
self.dir.angle_between(ast.pos - self.pos),
|
self.dir.angle_between(ast.pos - self.pos),
|
||||||
(ast.vel - self.vel).x / 11.,
|
(ast.vel - self.vel).x / 11.,
|
||||||
|
@ -140,21 +143,25 @@ impl Player {
|
||||||
// );
|
// );
|
||||||
|
|
||||||
if let Some(brain) = &self.brain {
|
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.rot = (self.rot + 0.1 + TAU as f32) % TAU as f32;
|
||||||
self.dir = vec2(self.rot.cos(), self.rot.sin());
|
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.rot = (self.rot - 0.1 + TAU as f32) % TAU as f32;
|
||||||
self.dir = vec2(self.rot.cos(), self.rot.sin());
|
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;
|
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 {
|
if self.last_shot > self.shot_interval {
|
||||||
self.last_shot = 0;
|
self.last_shot = 0;
|
||||||
self.shots += 1;
|
self.shots += 1;
|
||||||
|
@ -243,6 +250,12 @@ impl Player {
|
||||||
bullet.draw();
|
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 {
|
struct Bullet {
|
||||||
|
|
|
@ -5,7 +5,7 @@ use crate::{nn::NN, world::World, HEIGHT, WIDTH};
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Population {
|
pub struct Population {
|
||||||
size: usize,
|
size: usize,
|
||||||
gen: i32,
|
pub gen: i32,
|
||||||
best: bool,
|
best: bool,
|
||||||
pub worlds: Vec<World>,
|
pub worlds: Vec<World>,
|
||||||
}
|
}
|
||||||
|
@ -49,13 +49,13 @@ impl Population {
|
||||||
world.draw();
|
world.draw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
draw_text(
|
// draw_text(
|
||||||
&format!("Gen: {}", self.gen),
|
// &format!("Gen: {}", self.gen),
|
||||||
-150. + WIDTH * 0.5,
|
// -150. + WIDTH * 0.5,
|
||||||
30. - HEIGHT * 0.5,
|
// 30. - HEIGHT * 0.5,
|
||||||
32.,
|
// 32.,
|
||||||
WHITE,
|
// WHITE,
|
||||||
);
|
// );
|
||||||
|
|
||||||
// draw black background outside the screen
|
// draw black background outside the screen
|
||||||
let th = (screen_height() - HEIGHT) * 0.5;
|
let th = (screen_height() - HEIGHT) * 0.5;
|
||||||
|
|
Loading…
Reference in New Issue