use crate::racetrack::Racetrack;
use crate::rng::Rng;
use crate::vector::Vector;
use std::iter;
const N_MID_WEIGHTS: usize = 32;
#[derive(Clone, PartialEq, PartialOrd)]
pub struct Brain {
view_dist: i32,
mid_weights: [Vec<f32>; N_MID_WEIGHTS],
out_weights: [[f32; N_MID_WEIGHTS]; 5],
}
impl Brain {
pub fn random(view_dist: i32, rng: &mut Rng) -> Brain {
let view_dist = pos_round_up_2(i32::abs(view_dist));
let n_inputs = ((view_dist / 2) * (view_dist / 2) * 4) as usize - 2;
Brain {
view_dist,
mid_weights: repeat_array(|| {
iter::repeat_with(|| random_f32(rng))
.take(n_inputs)
.collect()
}),
out_weights: [
repeat_array(|| random_f32(rng)),
repeat_array(|| random_f32(rng)),
repeat_array(|| random_f32(rng)),
repeat_array(|| random_f32(rng)),
repeat_array(|| random_f32(rng)),
],
}
}
pub fn mutant(&self, rng: &mut Rng, amount: f64) -> Brain {
let amount = amount as f32;
let mut mutant = self.clone();
for neuron in mutant.mid_weights.iter_mut() {
for weight in neuron {
*weight += random_f32(rng) * amount;
}
}
for neuron in mutant.out_weights.iter_mut() {
for weight in neuron {
*weight += random_f32(rng) * amount;
}
}
mutant
}
pub fn compute_accel(&self, vel: Vector, track: &Racetrack) -> Vector {
let mut mid_iter = self.mid_weights.iter();
let mid_out = repeat_array(|| {
if let Some(neuron) = mid_iter.next() {
let quarter = (neuron.len() - 2) / 4;
let mut sum = 0.0;
let mut i = 0;
for x in (1..self.view_dist).step_by(2) {
for y in (1..self.view_dist).step_by(2) {
if let Some(true) = track.get(Vector::new(x, y)) {
sum += neuron[i];
}
if let Some(true) = track.get(Vector::new(-x, y)) {
sum += neuron[i + 1 * quarter];
}
if let Some(true) = track.get(Vector::new(-x, -y)) {
sum += neuron[i + 2 * quarter];
}
if let Some(true) = track.get(Vector::new(x, -y)) {
sum += neuron[i + 3 * quarter];
}
i += 1;
}
}
sum += vel.x as f32 * neuron[neuron.len() - 2];
sum += vel.y as f32 * neuron[neuron.len() - 1];
sum
} else {
unreachable!()
}
});
assert!(mid_iter.next().is_none());
let mut out = vec![
compute_out(&self.out_weights[0], &mid_out),
compute_out(&self.out_weights[1], &mid_out),
compute_out(&self.out_weights[2], &mid_out),
compute_out(&self.out_weights[3], &mid_out),
compute_out(&self.out_weights[4], &mid_out),
]
.into_iter();
let mut max = out.next().unwrap();
let mut max_i = 0;
for (i, choice) in out.enumerate() {
if choice > max {
max = choice;
max_i = i;
}
}
match max_i {
0 => Vector::new(1, 0),
1 => Vector::new(0, 1),
2 => Vector::new(-1, 0),
3 => Vector::new(0, -1),
4 => Vector::ORIGIN,
_ => unreachable!(),
}
}
}
fn pos_round_up_2(num: i32) -> i32 {
(num + 1) & !1
}
fn random_f32(rng: &mut Rng) -> f32 {
rng.forward() as f32 / Rng::RAND_MAX as f32 * 2.0 - 1.0
}
fn repeat_array<T, F: FnMut() -> T>(mut f: F) -> [T; N_MID_WEIGHTS] {
[
f(),
f(),
f(),
f(),
f(),
f(),
f(),
f(),
f(),
f(),
f(),
f(),
f(),
f(),
f(),
f(),
f(),
f(),
f(),
f(),
f(),
f(),
f(),
f(),
f(),
f(),
f(),
f(),
f(),
f(),
f(),
f(),
]
}
fn compute_out(neuron: &[f32; N_MID_WEIGHTS], inputs: &[f32; N_MID_WEIGHTS]) -> f32 {
neuron.into_iter().zip(inputs).map(|(&w, &i)| w * i).sum()
}