asteroids-genetic/src/world.rs

186 lines
6.3 KiB
Rust
Raw Normal View History

2022-10-08 19:47:47 +00:00
use crate::{
asteroids::{Asteroid, AsteroidSize},
2022-10-09 20:11:24 +00:00
nn::NN,
2022-10-08 19:47:47 +00:00
player::Player,
};
2022-10-08 15:27:05 +00:00
use macroquad::{prelude::*, rand::gen_range};
#[derive(Default)]
pub struct World {
2023-01-04 19:57:59 +00:00
pub player: Player,
2022-10-08 15:27:05 +00:00
asteroids: Vec<Asteroid>,
2023-01-04 19:57:59 +00:00
pub score: f32,
2022-10-09 05:40:56 +00:00
pub over: bool,
2022-10-22 19:40:19 +00:00
pub fitness: f32,
2022-10-08 15:27:05 +00:00
}
impl World {
2022-10-22 19:40:19 +00:00
pub fn simulate(brain: Option<NN>) -> Self {
2022-10-09 20:11:24 +00:00
Self {
player: Player::simulate(brain),
2023-01-05 20:10:35 +00:00
score: 1.,
asteroids: vec![
Asteroid::new_to(vec2(0., 0.), 1.5, AsteroidSize::Large),
Asteroid::new(AsteroidSize::Large),
Asteroid::new(AsteroidSize::Large),
Asteroid::new(AsteroidSize::Large),
Asteroid::new(AsteroidSize::Large),
],
2022-10-09 20:11:24 +00:00
..Default::default()
}
}
2022-10-22 19:40:19 +00:00
pub fn set_best(&mut self) {
self.player.color = Some(RED);
}
2022-10-10 18:36:14 +00:00
pub fn see_brain(&self) -> &NN {
self.player.brain.as_ref().unwrap()
}
2023-01-06 22:47:57 +00:00
pub fn export_brain(&self) {
2023-01-06 09:10:29 +00:00
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");
}
2022-10-08 15:27:05 +00:00
pub fn update(&mut self) {
2022-10-08 20:17:06 +00:00
let mut to_add: Vec<Asteroid> = Vec::new();
2022-10-08 15:27:05 +00:00
for asteroid in &mut self.asteroids {
asteroid.update();
2022-10-08 20:17:06 +00:00
if self.player.check_bullet_collisions(asteroid) {
2023-01-04 19:57:59 +00:00
self.score += 1.;
2022-10-08 20:17:06 +00:00
match asteroid.size {
AsteroidSize::Large => {
2022-10-10 10:13:06 +00:00
let rand = vec2(gen_range(-0.8, 0.8), gen_range(-0.8, 0.8));
2022-10-08 20:17:06 +00:00
to_add.push(Asteroid::new_from(
asteroid.pos,
asteroid.vel + rand,
AsteroidSize::Medium,
));
to_add.push(Asteroid::new_from(
asteroid.pos,
asteroid.vel - rand,
AsteroidSize::Medium,
));
}
AsteroidSize::Medium => {
2022-10-10 10:13:06 +00:00
let rand = vec2(gen_range(-0.6, 0.6), gen_range(-0.6, 0.6));
2022-10-08 20:17:06 +00:00
to_add.push(Asteroid::new_from(
asteroid.pos,
asteroid.vel + rand,
AsteroidSize::Small,
));
to_add.push(Asteroid::new_from(
asteroid.pos,
asteroid.vel - rand,
AsteroidSize::Small,
));
}
_ => {}
}
}
2023-01-05 20:33:25 +00:00
if self.player.check_player_collision(asteroid) {
2023-01-05 16:46:08 +00:00
self.over = true;
}
}
2023-01-08 19:07:35 +00:00
self.fitness =
(self.score / self.player.shots as f32).powi(2) * self.player.lifespan as f32;
2023-01-05 20:33:25 +00:00
self.player.update();
2022-10-08 20:17:06 +00:00
self.asteroids.append(&mut to_add);
2022-10-08 19:47:47 +00:00
self.asteroids.retain(|asteroid| asteroid.alive);
// if self.asteroids.iter().fold(0, |acc, x| {
// acc + match x.size {
// AsteroidSize::Large => 4,
// AsteroidSize::Medium => 2,
// AsteroidSize::Small => 1,
// }
// }) < self.max_asteroids
2023-01-05 20:10:35 +00:00
// || self.player.lifespan % 200 == 0
2023-01-04 19:57:59 +00:00
// {
if self.player.lifespan % 200 == 0 {
self.asteroids
.push(Asteroid::new_to(self.player.pos, 1.5, AsteroidSize::Large));
2022-10-08 15:27:05 +00:00
}
}
pub fn draw(&self) {
self.player.draw();
for asteroid in &self.asteroids {
asteroid.draw();
}
2023-01-04 19:57:59 +00:00
draw_text(
2023-01-05 16:46:08 +00:00
&format!(
"{}",
(self.score / self.player.shots as f32).powi(2) * self.player.lifespan as f32
),
2023-01-04 19:57:59 +00:00
self.player.pos.x - 20.,
self.player.pos.y - 20.,
12.,
WHITE,
);
2022-10-08 15:27:05 +00:00
}
2023-01-08 19:07:35 +00:00
pub fn draw_stats(&self, width: f32, height: f32) {
draw_rectangle_lines(-width * 0.5, -height * 0.5, width, height, 2., WHITE);
let scale = 2.5;
let offset = vec2(-width * 0.3, -height * 0.1);
let p1 = scale * vec2(0., -20.) + offset;
let p2 = scale * vec2(-12.667, 18.) + offset;
let p3 = scale * vec2(12.667, 18.) + offset;
let p4 = scale * vec2(-10., 10.) + offset;
let p5 = scale * vec2(10., 10.) + offset;
let p6 = scale * vec2(0., 25.) + offset;
let p7 = scale * vec2(-6., 10.) + offset;
let p8 = scale * vec2(6., 10.) + offset;
draw_line(p1.x, p1.y, p2.x, p2.y, 2., WHITE);
draw_line(p1.x, p1.y, p3.x, p3.y, 2., WHITE);
draw_line(p4.x, p4.y, p5.x, p5.y, 2., WHITE);
if self.player.outputs[2] > 0. && (gen_range(0., 1.) < 0.4 || self.over) {
draw_triangle_lines(p6, p7, p8, 2., WHITE);
}
let l1 = scale * vec2(30., 0.) + offset;
let l2 = scale * vec2(25., -5.) + offset;
let l3 = scale * vec2(25., 5.) + offset;
if self.player.outputs[0] > 0. {
draw_line(l1.x, l1.y, l2.x, l2.y, 2., WHITE);
draw_line(l1.x, l1.y, l3.x, l3.y, 2., WHITE);
}
let l1 = -scale * vec2(30., 0.) + offset;
let l2 = -scale * vec2(25., -5.) + offset;
let l3 = -scale * vec2(25., 5.) + offset;
if self.player.outputs[1] > 0. {
draw_line(l1.x, l1.y, l2.x, l2.y, 2., WHITE);
draw_line(l1.x, l1.y, l3.x, l3.y, 2., WHITE);
}
let l1 = -scale * vec2(0., 35.) + offset;
if self.player.outputs[3] > 0. {
draw_circle(l1.x, l1.y, 5., WHITE);
draw_circle(l1.x, l1.y, 3.5, BLACK);
}
draw_text(
if self.over { "DEAD" } else { "ALIVE" },
-width * 0.5 + 20.,
75.,
24.,
if self.over { RED } else { GREEN },
);
draw_text(
&format!("Score: {}", self.score),
-width * 0.5 + 20.,
100.,
24.,
WHITE,
);
draw_text(
&format!("Fitness: {:.2}", self.fitness),
-width * 0.5 + 20.,
125.,
24.,
WHITE,
);
}
2022-10-08 15:27:05 +00:00
}