use crate::constants::simulation::{CELL_SUSTAIN_ENERGY_COST, DEAD_CELL_REMOVE_RATE, LEAF_ABSORB_RATE, MAX_GROWTHS_PER_ITERATION, SEED_ENERGY_DRAIN, SEED_SPAWN_RATE, SUN_POWER, TRUNK_ABSORB_RATE};
use crate::sim_mod::cell_types::CellType;
use crate::sim_mod::cell_types::CellType::{Empty, Leaf, Trunk, Dead};
use crate::sim_mod::plant::Plant;
use crate::traits::color_convert::ColorConvert;
use macroquad::math::IVec2;
use rand::{random_bool, Rng};
use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator};
use macroquad::rand::{gen_range};
pub struct Habitat {
grid_size: IVec2,
cell_map: Vec<Vec<CellType>>,
plants: Vec<Plant>,
seeds: Vec<Plant>,
dead_cells: Vec<IVec2>,
ground_buffer: Vec<Vec<Plant>>,
minimum_plants: usize,
}
impl Habitat {
pub fn new(grid_size: IVec2) -> Self {
Self {
grid_size,
cell_map: vec![vec![Empty; grid_size.y as usize]; grid_size.x as usize],
plants: Vec::new(),
seeds: Vec::new(),
dead_cells: Vec::new(),
ground_buffer: {
let mut out = vec![];
out.resize_with(grid_size.x as usize, || Vec::new());
out
},
minimum_plants: 0,
}
}
pub fn get_rgb_data(&self) -> Vec<u8> {
self.cell_map
.iter()
.flatten()
.map(|x| x.get_color())
.flat_map(|x| [x.r, x.g, x.b])
.map(|x| (x * 255.0) as u8)
.collect::<Vec<u8>>()
}
pub fn update(&mut self) {
if self.get_total_plant_count() < self.minimum_plants {
self.spawn_plant()
}
self.plants.par_iter_mut().for_each(|plant| plant.increase_age());
let plant_growth = self.create_growths();
for (idx, growths) in plant_growth.into_iter().enumerate() {
let mut plant = &mut self.plants[idx];
for (pos,response_ix, cell_type, energy_cost) in growths {
plant.add_cell(pos, cell_type, response_ix);
plant.give_energy(-energy_cost);
}
}
let mut dead_plant_ix = Vec::<usize>::new();
for (ix, plant) in self.plants.iter().enumerate() {
if plant.is_dead() {
dead_plant_ix.push(ix);
for cell_ix in 0..plant.get_cells().len() {
if random_bool(SEED_SPAWN_RATE as f64) {
let (pos, _) = plant.get_cells()[cell_ix];
self.seeds.push(plant.new_offspring(pos));
}
}
}
}
for ix in dead_plant_ix {
for (pos, _) in self.plants[ix].get_cells().iter() {
self.dead_cells.push(*pos);
}
}
self.plants.retain(|plant| !plant.is_dead());
self.apply_plants();
self.update_all_columns();
self.supply_plant_energy();
self.dead_cells.retain(|_| !random_bool(DEAD_CELL_REMOVE_RATE as f64));
self.update_seeds();
self.update_grounded_buffer();
self.show_seeds();
}
pub fn spawn_plant(&mut self) {
let mut rng = rand::rng();
let pos = (rng.random_range(0..self.grid_size.x), self.grid_size.y-1);
if let Empty = self.get_cell_at(pos.into()) {
self.seeds.push(Plant::new(pos.into()));
}
}
pub fn set_minimum_plants(&mut self, minimum_plants: usize) {
self.minimum_plants = minimum_plants;
}
pub fn get_total_plant_count(&self) -> usize {
self.plants.len() + self.seeds.len()
+ self.ground_buffer.iter().map(|x| x.len()).sum::<usize>()
}
fn apply_plants(&mut self) {
self.cell_map = vec![vec![Empty; self.grid_size.y as usize]; self.grid_size.x as usize];
let mut leaves = Vec::<(IVec2, CellType)>::new();
let mut trunks = Vec::<(IVec2, CellType)>::new();
for plant in &self.plants {
for (pos, cell) in plant.get_cells().iter() {
match cell {
Leaf { .. } => {leaves.push((*pos, *cell));},
Trunk { .. } => {trunks.push((*pos, *cell));},
_ => {}
}
}
}
for (pos, cell) in trunks.iter() {
self.set_cell(*pos, *cell)
}
for (pos, cell) in leaves.iter() {
self.set_cell(*pos, *cell)
}
for ix in 0..self.dead_cells.len() {
self.set_cell(self.dead_cells[ix], Dead);
}
}
fn set_cell(&mut self, pos: IVec2, cell_type: CellType) {
self.cell_map[((pos.x + self.grid_size.x) % self.grid_size.x) as usize]
[(self.grid_size.y - 1 - pos.y) as usize] = cell_type;
}
fn is_in_grid(&self, pos: IVec2) -> bool {
!(pos.x < 0 || pos.x >= self.grid_size.x || pos.y < 0 || pos.y >= self.grid_size.y)
}
fn is_in_y_bounds(&self, pos: IVec2) -> bool {
!(pos.y < 0 || pos.y >= self.grid_size.y)
}
fn get_cell_at(&self, pos: IVec2) -> &CellType {
&self.cell_map
[((pos.x + self.grid_size.x) % self.grid_size.x) as usize]
[(self.grid_size.y - 1 - pos.y) as usize]
}
fn spawn_random_cell(&mut self) {
let mut rng = rand::rng();
for _ in 0..100 {
let pos = IVec2::new(
rng.random_range(0..self.grid_size.x),
rng.random_range(0..self.grid_size.y),
);
if let Empty = self.get_cell_at(pos) {
self.set_cell(pos, CellType::new_leaf());
break;
}
}
}
fn column_update(column: &mut Vec<CellType>) {
let mut current_energy = 1.;
for cell in column {
match cell {
Empty | CellType::Seed => {}
Leaf { sun_absorbed } => {
*sun_absorbed = current_energy * LEAF_ABSORB_RATE;
current_energy -= *sun_absorbed;
}
Trunk { .. } | CellType::Dead => {
current_energy -= TRUNK_ABSORB_RATE * current_energy;
}
}
}
}
fn update_all_columns(&mut self) {
self.cell_map
.par_iter_mut()
.for_each(|column| Habitat::column_update(column))
}
fn create_growths(&self) -> Vec<Vec<(IVec2, usize, CellType, f32)>> {
let mut plant_growth: Vec<Vec<(IVec2, usize, CellType, f32)>> = Vec::with_capacity(self.plants.len());
for plant in &self.plants {
let mut growth_proposals = plant.create_growth_proposals();
growth_proposals.retain(|(pos, _, _, _)|
self.is_in_y_bounds(*pos)
&& match self.get_cell_at(*pos) {
Empty => true,
_ => false
}
);
let mut growths = Vec::with_capacity(MAX_GROWTHS_PER_ITERATION);
for _ in 0..MAX_GROWTHS_PER_ITERATION {
if growth_proposals.len() > 0 {
growths.push(growth_proposals.remove(gen_range(0, growth_proposals.len())));
}
}
plant_growth.push(growths);
}
plant_growth
}
fn supply_plant_energy(&mut self) {
let mut plant_leaves = Vec::<Vec<IVec2>>::new();
for mut plant in &mut self.plants {
let mut leaves = Vec::<IVec2>::new();
for (pos, cell) in plant.get_cells().iter() {
match cell {
Leaf { .. } => {
leaves.push(IVec2::from(*pos));
}
_ => {}
}
}
plant_leaves.push(leaves);
}
for (idx, leaves) in plant_leaves.iter().enumerate() {
let mut collected_energy = 0f32;
for leaf_pos in leaves {
match self.get_cell_at(*leaf_pos) {
Leaf { sun_absorbed } => {
collected_energy += sun_absorbed * SUN_POWER;
}
_ => {}
}
}
let energy_cost = self.plants[idx].get_cells().len() as f32 * CELL_SUSTAIN_ENERGY_COST;
self.plants[idx].give_energy(
collected_energy - energy_cost);
}
}
fn update_seeds(&mut self) {
let mut indexes = Vec::<(usize, i32)>::with_capacity(self.plants.len());
for ix in 0..self.seeds.len() {
let (x, y) = self.seeds[ix].get_pos().into();
let new_pos = IVec2::new(x + gen_range(-1, 2), y-1);
self.seeds[ix].set_pos(new_pos);
if let IVec2 { x, y: -1 } = self.seeds[ix].get_pos() {
self.seeds[ix].set_pos(IVec2::new(x + 1, 0));
indexes.push((ix, x));
}
}
indexes.reverse();
for (ix, x) in indexes {
self.ground_buffer[((self.grid_size.x + x) % self.grid_size.x) as usize]
.push(self.seeds.remove(ix));
}
}
fn show_seeds(&mut self) {
for ix in 0..self.seeds.len() {
let pos = self.seeds[ix].get_pos();
self.set_cell(pos, CellType::Seed);
}
}
fn update_grounded_buffer(&mut self) {
let mut growing_seeds = Vec::<(usize, usize)>::with_capacity(self.grid_size.x as usize);
for (ix, container ) in self.ground_buffer.iter().enumerate() {
if let Empty = self.get_cell_at((ix as i32, 0i32).into()) {
match container.len() {
0 => {}
1 => {
growing_seeds.push((ix, 0));
}
_ => {
let container_ix = container.into_iter().enumerate()
.max_by(|(_, plant1), (_, plant2) |
plant1.get_energy().total_cmp(&plant2.get_energy())).unwrap().0;
growing_seeds.push((ix, container_ix));
}
}
}
}
for (column, ix) in growing_seeds {
let mut plant = self.ground_buffer[column].remove(ix);
plant.create_root();
self.plants.push(plant);
}
for ix in 0..self.grid_size.x as usize {
for plant in &mut self.ground_buffer[ix] {
plant.give_energy(-SEED_ENERGY_DRAIN)
}
self.ground_buffer[ix].retain(|seed| !seed.is_dead())
}
}
}