asteroids-genetic/src/main.rs

341 lines
12 KiB
Rust
Raw Normal View History

2022-10-08 13:15:07 +00:00
mod asteroids;
2022-10-09 18:14:22 +00:00
mod nn;
2022-10-08 13:15:07 +00:00
mod player;
2022-10-09 20:11:24 +00:00
mod population;
2022-10-08 15:27:05 +00:00
mod world;
2022-10-08 13:15:07 +00:00
2023-01-08 20:09:10 +00:00
use nn::NN;
use tinyfiledialogs::*;
2023-01-07 21:59:59 +00:00
use macroquad::{
prelude::*,
ui::{hash, root_ui, widgets, Skin},
};
2022-10-10 18:36:14 +00:00
use population::Population;
2023-01-08 20:09:10 +00:00
use world::World;
2022-10-08 13:15:07 +00:00
2023-01-06 22:47:57 +00:00
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)]
2022-10-08 13:15:07 +00:00
async fn main() {
rand::srand(macroquad::miniquad::date::now() as _);
2023-01-08 20:40:34 +00:00
let pause = load_texture("assets/pause.png").await.unwrap();
let play = load_texture("assets/play.png").await.unwrap();
let fast = load_texture("assets/fast.png").await.unwrap();
let slow = load_texture("assets/slow.png").await.unwrap();
let restart = load_texture("assets/restart.png").await.unwrap();
2023-01-06 22:47:57 +00:00
let th = (screen_height() - HEIGHT) * 0.5;
let gamecam = Camera2D {
2022-10-09 05:40:56 +00:00
zoom: vec2(2. / screen_width(), -2. / screen_height()),
2023-01-06 22:47:57 +00:00
offset: vec2((2. * th + WIDTH) / screen_width() - 1., 0.),
2022-10-08 13:15:07 +00:00
..Default::default()
};
2023-01-08 19:07:35 +00:00
let netcam = Camera2D {
2023-01-06 22:47:57 +00:00
zoom: vec2(2. / screen_width(), -2. / screen_height()),
offset: vec2(
(th + WIDTH) / screen_width(),
-((th + HEIGHT) * 0.5) / screen_height(),
),
..Default::default()
};
2023-01-08 19:07:35 +00:00
let statcam = Camera2D {
zoom: vec2(2. / screen_width(), -2. / screen_height()),
offset: vec2(
(th + WIDTH) / screen_width(),
((th + HEIGHT) * 0.5) / screen_height(),
),
..Default::default()
};
2023-01-08 20:40:34 +00:00
let mut speedup = 1;
2023-01-07 21:59:59 +00:00
let mut paused = false;
2023-01-08 19:07:35 +00:00
let mut bias = false;
let mut size = 100;
let mut pop = Population::new(size as usize);
2023-01-11 18:56:03 +00:00
let mut x = 0.;
let mut xy = "".to_string();
let mut l1: usize = 6;
let mut l2: usize = 6;
let mut l3: usize = 0;
2023-01-08 19:07:35 +00:00
let ui_thick = 34.;
2023-01-11 18:56:03 +00:00
let nums = &[
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16",
];
2023-01-07 21:59:59 +00:00
let skin = {
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.))
2023-01-08 19:07:35 +00:00
.color_inactive(WHITE)
2023-01-07 21:59:59 +00:00
.build();
2023-01-11 18:56:03 +00:00
let button_style = 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.))
2023-01-07 21:59:59 +00:00
.color_hovered(RED)
.color_clicked(BLUE)
.text_color(WHITE)
.text_color_hovered(WHITE)
.text_color_clicked(WHITE)
.margin(RectOffset::new(10., 10., 8., 8.))
2023-01-08 19:07:35 +00:00
.color_inactive(WHITE)
2023-01-07 21:59:59 +00:00
.build();
let label_style = root_ui()
.style_builder()
.text_color(WHITE)
.font_size(24)
.margin(RectOffset::new(5., 5., 4., 4.))
2023-01-11 18:56:03 +00:00
// .text_color_hovered(LIGHTGRAY)
// .text_color_clicked(WHITE)
2023-01-08 19:07:35 +00:00
.build();
let group_style = root_ui()
.style_builder()
2023-01-11 18:56:03 +00:00
.color(Color::new(1., 0., 0., 1.))
.build();
let editbox_style = 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.))
// .margin(RectOffset::new(10., 10., 8., 8.))
.text_color(WHITE)
// .color_hovered(WHITE)
// .color_inactive(WHITE)
// .color(WHITE)
.build();
let combobox_style = 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.))
// .margin(RectOffset::new(1., 1., 1., 1.))
// .text_color_hovered(WHITE)
// .text_color(WHITE)
.color_hovered(WHITE)
.color_selected_hovered(WHITE)
.color_inactive(WHITE)
.color_clicked(WHITE)
.color(WHITE)
2023-01-07 21:59:59 +00:00
.build();
Skin {
window_style,
button_style,
label_style,
2023-01-08 19:07:35 +00:00
group_style,
2023-01-11 18:56:03 +00:00
editbox_style,
combobox_style,
2023-01-07 21:59:59 +00:00
margin: 0.,
..root_ui().default_skin()
}
};
2023-01-11 18:56:03 +00:00
let mut skin2 = skin.clone();
skin2.label_style = root_ui()
.style_builder()
.text_color(WHITE)
.font_size(16)
// .margin(RectOffset::new(5., 5., 4., 0.))
.text_color_hovered(LIGHTGRAY)
.text_color_clicked(WHITE)
.build();
2022-10-08 13:15:07 +00:00
loop {
2022-10-10 10:13:06 +00:00
clear_background(BLACK);
2023-01-08 19:07:35 +00:00
set_camera(&gamecam);
2022-10-10 19:43:05 +00:00
if is_key_pressed(KeyCode::S) {
2023-01-08 20:40:34 +00:00
speedup = (speedup * 10) % 9999;
2023-01-09 19:25:18 +00:00
}
if is_key_pressed(KeyCode::P) {
paused = !paused;
2022-10-10 19:43:05 +00:00
}
2023-01-08 20:40:34 +00:00
if !paused {
for _ in 0..speedup {
2023-01-07 21:59:59 +00:00
pop.update();
}
2022-10-10 19:43:05 +00:00
}
2023-01-08 20:40:34 +00:00
pop.draw();
2023-01-06 22:47:57 +00:00
draw_rectangle_lines(-WIDTH * 0.5, -HEIGHT * 0.5, WIDTH, HEIGHT, 2., WHITE);
2023-01-07 21:59:59 +00:00
draw_rectangle_lines(
WIDTH * 0.5 + th,
-HEIGHT * 0.5,
screen_width() - WIDTH - 3. * th,
2023-01-08 19:07:35 +00:00
ui_thick,
2023-01-07 21:59:59 +00:00
2.,
WHITE,
);
draw_rectangle_lines(
WIDTH * 0.5 + th,
2023-01-08 19:07:35 +00:00
-HEIGHT * 0.5 + (screen_height() - 3. * th) * 0.5 - ui_thick,
2023-01-07 21:59:59 +00:00
screen_width() - WIDTH - 3. * th,
2023-01-08 19:07:35 +00:00
ui_thick,
2023-01-07 21:59:59 +00:00
2.,
WHITE,
);
2023-01-06 22:47:57 +00:00
2023-01-08 19:07:35 +00:00
set_camera(&netcam);
2023-01-11 18:56:03 +00:00
pop.worlds[pop.track].player.draw_brain(
2023-01-06 22:47:57 +00:00
screen_width() - WIDTH - 3. * th,
(screen_height() - 3. * th) * 0.5,
2023-01-08 19:07:35 +00:00
bias,
);
set_camera(&statcam);
2023-01-11 18:56:03 +00:00
pop.worlds[pop.track].draw_stats(
2023-01-08 19:07:35 +00:00
screen_width() - WIDTH - 3. * th,
(screen_height() - 7. * th) * 0.5 - 2. * ui_thick,
2023-01-11 18:56:03 +00:00
pop.worlds.iter().fold(1, |acc, w| {
acc + if w.fitness > pop.worlds[pop.track].fitness {
1
} else {
0
}
}),
2023-01-06 22:47:57 +00:00
);
2023-01-11 18:56:03 +00:00
if !pop.focus
&& is_mouse_button_pressed(MouseButton::Left)
&& mouse_position().0 < WIDTH + th
{
2023-01-09 19:25:18 +00:00
let (x, y) = mouse_position();
2023-01-11 18:56:03 +00:00
pop.change_track(vec2(x - th - WIDTH * 0.5, y - th - HEIGHT * 0.5));
2023-01-09 19:25:18 +00:00
}
2023-01-07 21:59:59 +00:00
2023-01-08 19:07:35 +00:00
let ui_width = screen_width() - WIDTH - 3. * th + 1.;
let ui_height = (screen_height() - 3. * th) * 0.5;
2023-01-07 21:59:59 +00:00
root_ui().window(
hash!(),
vec2(WIDTH + 2. * th, th),
2023-01-08 19:07:35 +00:00
vec2(ui_width, ui_height),
2023-01-07 21:59:59 +00:00
|ui| {
2023-01-11 18:56:03 +00:00
ui.push_skin(&skin);
2023-01-08 19:07:35 +00:00
widgets::Group::new(hash!(), Vec2::new(ui_width, ui_thick))
.position(Vec2::new(0., 0.))
.ui(ui, |ui| {
ui.label(None, &format!("Generation: {}", pop.gen));
2023-01-08 20:40:34 +00:00
ui.same_line(242.);
2023-01-08 20:09:10 +00:00
if widgets::Button::new("Load Model").ui(ui) {
2023-01-08 20:40:34 +00:00
if let Some(path) = open_file_dialog("Load Model", "model.json", None) {
2023-01-08 20:09:10 +00:00
let brain = NN::import(&path);
size = 1;
pop = Population::new(1);
pop.worlds[0] = World::simulate(Some(brain));
}
}
2023-01-08 19:07:35 +00:00
ui.same_line(0.);
2023-01-08 20:09:10 +00:00
if widgets::Button::new("Save Model").ui(ui) {
2023-01-08 20:40:34 +00:00
if let Some(path) = save_file_dialog("Save Model", "model.json") {
2023-01-11 18:56:03 +00:00
pop.worlds[pop.track].export_brain(&path);
2023-01-08 20:09:10 +00:00
}
}
2023-01-08 19:07:35 +00:00
ui.same_line(0.);
2023-01-08 20:40:34 +00:00
if widgets::Button::new(slow).ui(ui) {
speedup = std::cmp::max(speedup / 10, 1);
};
ui.same_line(0.);
if widgets::Button::new("1x").ui(ui) {
speedup = 1;
};
ui.same_line(0.);
2023-01-08 19:07:35 +00:00
if widgets::Button::new(fast).ui(ui) {
2023-01-08 20:40:34 +00:00
speedup = std::cmp::min(speedup * 10, 1000);
2023-01-08 19:07:35 +00:00
};
ui.same_line(0.);
if widgets::Button::new(if paused { play } else { pause }).ui(ui) {
paused = !paused;
};
});
widgets::Group::new(hash!(), Vec2::new(ui_width, ui_thick))
.position(Vec2::new(0., ui_height - ui_thick))
.ui(ui, |ui| {
ui.label(Some(vec2(0., 2.)), "«Population»");
widgets::Group::new(hash!(), Vec2::new(100., ui_thick))
.position(Vec2::new(140., 0.))
.ui(ui, |ui| {
2023-01-09 19:25:18 +00:00
ui.drag(hash!(), "", Some((1, 300)), &mut size);
2023-01-08 19:07:35 +00:00
});
2023-01-11 18:56:03 +00:00
ui.same_line(279.);
if widgets::Button::new(if pop.debug { "Debug:ON " } else { "Debug:OFF" })
.ui(ui)
{
2023-01-09 19:25:18 +00:00
pop.debug = !pop.debug;
};
2023-01-08 19:26:00 +00:00
ui.same_line(0.);
2023-01-08 19:07:35 +00:00
if widgets::Button::new(if bias { "Hide Bias" } else { "Show Bias" }).ui(ui)
{
bias = !bias;
};
ui.same_line(0.);
2023-01-11 18:56:03 +00:00
if widgets::Button::new(if !pop.focus { "Focus:OFF" } else { "Focus:ON " })
2023-01-08 19:07:35 +00:00
.ui(ui)
{
2023-01-09 19:25:18 +00:00
pop.focus = !pop.focus;
2023-01-08 19:07:35 +00:00
};
ui.same_line(0.);
if widgets::Button::new(restart).ui(ui) {
pop = Population::new(size as usize);
};
});
2023-01-11 18:56:03 +00:00
ui.pop_skin();
ui.push_skin(&skin2);
widgets::Group::new(
hash!(),
Vec2::new(ui_width * 0.2, ui_height * 0.8 - 2. * th - 2. * ui_thick),
)
.position(Vec2::new(ui_width * 0.8, ui_height * 0.2 + ui_thick + th))
.ui(ui, |ui| {
// ui.input_text(hash!(), "vec2(100., 100.)", &mut xy);
ui.label(None, "Hidden Layers Neuron");
ui.label(None, "Config");
ui.label(None, " ");
ui.combo_box(hash!(), "Layer 1", nums, &mut l1);
ui.combo_box(hash!(), "Layer 2", nums, &mut l2);
ui.combo_box(hash!(), "Layer 3", nums, &mut l3);
ui.label(None, " ");
ui.label(None, "Mutation Rate");
// ui.(hash!(), "", 0.0..0.2, &mut x);
});
2023-01-07 21:59:59 +00:00
},
);
2023-01-06 22:47:57 +00:00
next_frame().await;
2022-10-08 13:15:07 +00:00
}
}