bevy_life 0.13.0

Generic and dynamic cellular automaton lib for bevy
Documentation
use crate::{
    components::{Cell, CellState},
    resources::CellMap,
    SimulationBatch, SimulationPause,
};
use bevy::{log, platform::collections::HashMap, prelude::*};

fn handle_cell<C, S>((cell, state): (&C, &S), map: &HashMap<C::Coordinates, S>) -> Option<S>
where
    C: Cell,
    S: CellState,
{
    let neighbor_states = cell.neighbor_coordinates().filter_map(|c| map.get(&c));
    let new_state = state.new_cell_state(neighbor_states);
    if &new_state == state {
        None
    } else {
        Some(new_state)
    }
}

#[allow(clippy::needless_pass_by_value)]
pub fn handle_cells<C, S>(
    mut commands: Commands,
    par_commands: ParallelCommands,
    query: Query<(Entity, &C, &S)>,
    pause: Option<Res<SimulationPause>>,
    batch: Option<Res<SimulationBatch>>,
) where
    C: Cell,
    S: CellState,
{
    if pause.is_some() {
        return;
    }
    let map: HashMap<_, _> = query
        .iter()
        .map(|(_entity, cell, state)| (cell.coords().clone(), state.clone()))
        .collect();
    if batch.is_some() {
        query.par_iter().for_each(|(entity, cell, state)| {
            if let Some(new_state) = handle_cell((cell, state), &map) {
                par_commands.command_scope(|mut cmd| {
                    cmd.entity(entity).try_insert(new_state);
                });
            }
        });
    } else {
        for (entity, cell, state) in query.iter() {
            if let Some(new_state) = handle_cell((cell, state), &map) {
                commands.entity(entity).try_insert(new_state);
            }
        }
    }
}

#[allow(clippy::needless_pass_by_value)]
pub fn handle_new_cells<C>(query: Query<(Entity, &C), Added<C>>, mut map: ResMut<CellMap<C>>)
where
    C: Cell,
{
    for (entity, new_cell) in query.iter() {
        let old_entity = map.insert_cell(new_cell.coords().clone(), entity);
        if let Some(e) = old_entity {
            if e != entity {
                log::warn!(
                    "{:?} replaced {:?} at {:?} coordinates",
                    entity,
                    e,
                    new_cell.coords()
                );
            }
        }
    }
}

pub fn handle_removed_cells<C>(mut removed_cells: RemovedComponents<C>, mut map: ResMut<CellMap<C>>)
where
    C: Cell,
{
    if removed_cells.is_empty() {
        return;
    }
    log::trace!("Removing {} cells from cell map", removed_cells.len());
    map.remove_entities(removed_cells.read());
}