Skip to content

Commit

Permalink
feat: parallelize consuming plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
dsgallups committed Sep 4, 2024
1 parent 37c1deb commit b020a45
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 116 deletions.
9 changes: 3 additions & 6 deletions src/cell/mouth.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use bevy::prelude::*;

use crate::{neighbor::VecExt as _, organism::Organism, CellTree, GameState};
use crate::{neighbor::VecExt as _, organism::Belly, CellTree, GameState};

use super::Food;

Expand All @@ -19,7 +19,7 @@ fn consume_food(
mut commands: Commands,
locations: Res<CellTree>,
mouths: Query<(&GlobalTransform, &Parent), With<MouthCell>>,
mut organisms: Query<&mut Organism>,
mut organisms: Query<&mut Belly>,
food: Query<&Food>,
) {
for (mouth, mouth_parent) in &mouths {
Expand All @@ -40,10 +40,7 @@ fn consume_food(
}

if food_eaten > 0 {
organisms
.get_mut(mouth_parent.get())
.unwrap()
.ate_food(food_eaten);
organisms.get_mut(mouth_parent.get()).unwrap().0 += food_eaten;
}
}
}
4 changes: 2 additions & 2 deletions src/environment/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
pub use bevy::prelude::*;
use mouse_hover::hover_over_organism;

use crate::cell::CellType;
use crate::{cell::CellType, organism::Belly};

use super::{
organism::{Organism, OrganismPlugin},
Expand Down Expand Up @@ -121,5 +121,5 @@ fn clear_background(mut color: ResMut<ClearColor>) {
/// Creates the first organism. [`Organism::insert_at`] is used to unify spawning of the organism in the ECS
/// as well as placing itself in the [`OccupiedLocations`] hashmap.
fn spawn_first_organism(mut commands: Commands) {
Organism::first_organism().insert_at(&mut commands, Vec2::new(10., 10.));
Organism::first_organism().insert_at(&mut commands, Vec2::new(10., 10.), Belly(3));
}
62 changes: 19 additions & 43 deletions src/organism/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@ pub use plugin::*;
mod reproduction;
use reproduction::*;

#[derive(Component, Clone)]
pub struct Belly(pub u64);

#[derive(Component)]
pub struct Age(pub u64);

#[derive(Component)]
pub struct StarvedAt(pub u64);

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum BrainType {
Predator,
Expand All @@ -23,17 +32,12 @@ pub struct Organism {
genome: Genome,
brain: Option<BrainType>,
can_move: bool,
belly: u64,
/// in millis
time_born: u64,
offspring: u64,
/// in millis
last_starved: u64,
can_reproduce_at: u64,
}

impl Organism {
fn new(genome: Genome, belly: u64, time_born: u64) -> Self {
fn new(genome: Genome) -> Self {
let mut has_producer = false;
let mut has_eye = false;
let mut has_mover = false;
Expand Down Expand Up @@ -69,15 +73,11 @@ impl Organism {
genome,
brain: brain_type,
can_move: has_mover && !has_producer,
belly,
time_born,
offspring: 0,
last_starved: 0,
can_reproduce_at,
}
}
pub fn ready_to_reproduce(&self) -> bool {
self.belly >= self.can_reproduce_at
pub fn ready_to_reproduce(&self, belly: &Belly) -> bool {
belly.0 >= self.can_reproduce_at
}

/// returns the number of cells this organism takes up based on its genome
Expand All @@ -102,47 +102,20 @@ impl Organism {
self.can_move
}

pub fn reproduce(&mut self, current_tick: u64) -> Option<Organism> {
//always lose half of one's belly
self.belly /= 2;
pub fn reproduce(&self) -> Option<Organism> {
let child_genome = self.genome.reproduce();
if child_genome.num_cells() < 2 {
return None;
}
self.offspring += 1;
Some(Self::new(child_genome, self.belly, current_tick))
Some(Self::new(child_genome))
}

pub fn first_organism() -> Self {
Self::new(Genome::first_organism(), 3, 0)
}
pub fn ate_food(&mut self, amt: u64) {
if self.belly < self.can_reproduce_at {
self.belly += amt;
}
}

pub fn lost_food(&mut self, amt: u64, time: u64) {
self.belly = self.belly.saturating_sub(amt);
self.last_starved = time;
//info!("Organism lost food, belly is at {}", self.belly)
}

/// returns the millisecond last starved
pub fn time_last_starved(&self) -> u64 {
self.last_starved
}

pub fn time_born(&self) -> u64 {
self.time_born
}

pub fn belly(&self) -> u64 {
self.belly
Self::new(Genome::first_organism())
}

/// Uses both the ECS and the global positioning hashmap to insert itself.
pub fn insert_at(self, commands: &mut Commands, location: impl VecExt) {
pub fn insert_at(self, commands: &mut Commands, location: impl VecExt, belly: Belly) {
/*info!(
"\nInserting Organism into the world:\nLocation: {:?}\nto insert:{:#?}",
global_location, self
Expand All @@ -157,6 +130,9 @@ impl Organism {
..Default::default()
},
self,
belly,
Age(0),
StarvedAt(0),
))
.with_children(|child_builder| {
genome.spawn_cells(child_builder);
Expand Down
156 changes: 91 additions & 65 deletions src/organism/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::{
CellTree, GameState,
};

use super::Organism;
use super::{Age, Belly, Organism, StarvedAt};

/// Combines the systems of cells and organism actions
pub struct OrganismPlugin;
Expand All @@ -27,96 +27,122 @@ impl Plugin for OrganismPlugin {
.add_systems(
Update,
(
age_organism.run_if(in_state(GameState::Playing)),
organism_hunger.run_if(in_state(GameState::Playing)),
starve_organism.run_if(in_state(GameState::Playing)),
reproduce_organism.run_if(in_state(GameState::Playing)),
),
);
}
}

fn starve_organism(
fn age_organism(time: Res<Time>, mut ages: Query<&mut Age>) {
ages.par_iter_mut().for_each(|mut age| {
age.0 += time.delta().as_millis() as u64;
})
}

fn organism_hunger(
time: Res<Time>,
mut commands: Commands,
settings: Res<EnvironmentSettings>,
mut organisms: Query<(Entity, &Children, &mut Organism)>,
locations: Query<&GlobalTransform>,
mut organisms: Query<(&mut Belly, &Age, &mut StarvedAt), With<Organism>>,
) {
for (organism_entity, children, mut organism) in &mut organisms {
let current_time = time.elapsed().as_millis() as u64;
let time_alive = current_time - organism.time_born();

let d_time_last_starved = current_time - organism.time_last_starved();
if d_time_last_starved >= settings.hunger_tick {
// based on the age of the organism, we out how much food it should lose
let age_cost = (time_alive / settings.age_rate) + 1;
organism.lost_food(age_cost, current_time);
}

if organism.belly() == 0 {
//before the organism dies, we need to turn the children
//into food :D
// because it could be killed in the meantime or whatnot, we should just lay down the food here
for child in children.iter() {
let location = locations.get(*child).unwrap();
commands.spawn(FoodBundle::at(location.translation()));
organisms
.par_iter_mut()
.for_each(|(mut belly, age, mut organism_starved_at)| {
let current_time = time.elapsed().as_millis() as u64;

let d_time_last_starved = current_time - organism_starved_at.0;
if d_time_last_starved >= settings.hunger_tick {
// based on the age of the organism, we out how much food it should lose
let age_cost = (age.0 / settings.age_rate) + 1;
belly.0 = belly.0.saturating_sub(age_cost);
organism_starved_at.0 = current_time;
}
});
}

commands.entity(organism_entity).despawn_recursive();
}
}
fn starve_organism(
par_commands: ParallelCommands,
organisms: Query<(Entity, &Children, &Belly), With<Organism>>,
locations: Query<&GlobalTransform>,
) {
organisms
.par_iter()
.for_each(|(organism_entity, children, belly)| {
if belly.0 == 0 {
//before the organism dies, we need to turn the children
//into food :D
// because it could be killed in the meantime or whatnot, we should just lay down the food here

par_commands.command_scope(|mut commands| {
for child in children.iter() {
let location = locations.get(*child).unwrap();
commands.spawn(FoodBundle::at(location.translation()));
}

commands.entity(organism_entity).despawn_recursive();
});
}
})
}

fn reproduce_organism(
time: Res<Time>,
mut commands: Commands,
par_commands: ParallelCommands,
settings: Res<EnvironmentSettings>,
tree: Res<CellTree>,
mut organisms: Query<(&GlobalTransform, &mut Organism)>,
mut organisms: Query<(&GlobalTransform, &mut Belly, &Organism)>,
) {
if settings
.max_organisms
.is_some_and(|max| organisms.iter().count() >= max)
{
return;
}

for (organism_transform, mut organism) in &mut organisms {
if organism.ready_to_reproduce() {
let Some(new_organism) = organism.reproduce(time.elapsed().as_millis() as u64) else {
continue;
};

let organism_location = organism_transform.translation();

//info!("the organism has created a child!");
//the organism gets three chances to place within the radius of the parent.
//otherwise, it dies.
let mut chance = 0;
let new_organism_location = 'find: loop {
if chance > 3 {
break None;
}
let random_location = organism_location.rand_around(settings.spawn_radius);

// must spawn 3 blocks away from anything
// todo(dsgallups); hack
for (_, e) in tree.within_distance(random_location, organism.radius() as f32 + 1.) {
if e.is_some() {
chance += 1;
continue 'find;
organisms
.par_iter_mut()
.for_each(|(organism_transform, mut belly, organism)| {
if organism.ready_to_reproduce(&belly) {
belly.0 /= 2;
let Some(new_organism) = organism.reproduce() else {
return;
};

let organism_location = organism_transform.translation();

//info!("the organism has created a child!");
//the organism gets three chances to place within the radius of the parent.
//otherwise, it dies.
let mut chance = 0;
let new_organism_location = 'find: loop {
if chance > 3 {
break None;
}
let random_location = organism_location.rand_around(settings.spawn_radius);

// must spawn 3 blocks away from anything
// todo(dsgallups); hack
for (_, e) in
tree.within_distance(random_location, organism.radius() as f32 + 1.)
{
if e.is_some() {
chance += 1;
continue 'find;
}
}
}

break Some(random_location);
};
break Some(random_location);
};

let Some(final_child_location) = new_organism_location else {
//tough
continue;
};
let Some(final_child_location) = new_organism_location else {
//tough
return;
};

new_organism.insert_at(&mut commands, final_child_location);
//info!("Child spawned!");
}
}
par_commands.command_scope(|mut commands| {
new_organism.insert_at(&mut commands, final_child_location, belly.clone());
})
//info!("Child spawned!");
}
});
}

0 comments on commit b020a45

Please sign in to comment.