92 lines
2.7 KiB
Rust
92 lines
2.7 KiB
Rust
use crate::{buffer::Buf, util::wrap};
|
|
use fastapprox::faster::{cos, sin};
|
|
use rand::{seq::SliceRandom, Rng};
|
|
use std::f32::consts::TAU;
|
|
use std::fmt::{Display, Formatter};
|
|
|
|
/// A single Physarum agent. The x and y positions are continuous, hence we use floating point numbers instead of integers.
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
pub struct Agent {
|
|
pub x: f32,
|
|
pub y: f32,
|
|
pub angle: f32,
|
|
pub population_id: usize,
|
|
pub i: usize,
|
|
}
|
|
|
|
impl Display for Agent {
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "{:?}", self)
|
|
}
|
|
}
|
|
|
|
impl Agent {
|
|
/// Construct a new agent with random parameters.
|
|
pub fn new<R: Rng + ?Sized>(
|
|
width: usize,
|
|
height: usize,
|
|
id: usize,
|
|
rng: &mut R,
|
|
i: usize,
|
|
) -> Self {
|
|
let (x, y, angle) = rng.gen::<(f32, f32, f32)>();
|
|
Agent {
|
|
x: x * width as f32,
|
|
y: y * height as f32,
|
|
angle: angle * TAU,
|
|
population_id: id,
|
|
i,
|
|
}
|
|
}
|
|
|
|
/// Tick an agent
|
|
pub fn tick(
|
|
&mut self,
|
|
buf: &Buf,
|
|
sensor_distance: f32,
|
|
sensor_angle: f32,
|
|
rotation_angle: f32,
|
|
step_distance: f32,
|
|
width: usize,
|
|
height: usize,
|
|
) {
|
|
let xc = self.x + cos(self.angle) * sensor_distance;
|
|
let yc = self.y + sin(self.angle) * sensor_distance;
|
|
|
|
let agent_add_sens = self.angle + sensor_angle;
|
|
let agent_sub_sens = self.angle - sensor_angle;
|
|
|
|
let xl = self.x + cos(agent_sub_sens) * sensor_distance;
|
|
let yl = self.y + sin(agent_sub_sens) * sensor_distance;
|
|
let xr = self.x + cos(agent_add_sens) * sensor_distance;
|
|
let yr = self.y + sin(agent_add_sens) * sensor_distance;
|
|
|
|
// We sense from the buffer because this is where we previously combined data from all the grid.
|
|
let center = buf.get_buf(xc, yc);
|
|
let left = buf.get_buf(xl, yl);
|
|
let right = buf.get_buf(xr, yr);
|
|
|
|
// Rotate and move logic
|
|
let mut rng = rand::thread_rng();
|
|
let mut direction: f32 = 0.0;
|
|
|
|
if (center > left) && (center > right) {
|
|
direction = 0.0;
|
|
} else if (center < left) && (center < right) {
|
|
direction = *[-1.0, 1.0]
|
|
.choose(&mut rng)
|
|
.expect("unable to choose random direction");
|
|
} else if left < right {
|
|
direction = 1.0;
|
|
} else if right < left {
|
|
direction = -1.0;
|
|
}
|
|
|
|
let delta_angle = rotation_angle * direction;
|
|
|
|
self.angle = wrap(self.angle + delta_angle, TAU);
|
|
self.x = wrap(self.x + step_distance * cos(self.angle), width as f32);
|
|
self.y = wrap(self.y + step_distance * sin(self.angle), height as f32);
|
|
}
|
|
}
|