use crate::action::ActionDescriptor;
use crate::authoring::model::field::{Extent, FieldLayer};
use crate::metadata::LaneSpec;
use crate::params::{ParamDescriptor, ParamValue};
use crate::spatial_hash::SpatialHash;
use crate::view::{StatDescriptor, StatValue};
pub trait AgentLanes: Send + Sync + 'static {
const LANES: &'static [LaneSpec];
fn alloc(n: usize) -> Self;
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
fn swap(&mut self);
fn heap_bytes(&self) -> usize;
fn positions(&self) -> (&[f32], &[f32]);
fn positions_mut(&mut self) -> (&mut [f32], &mut [f32]);
fn grow(&mut self, n: usize);
fn colors(&self) -> Option<&[u8]> {
None
}
}
pub trait NeighborIndex: Send + Sync + 'static {
const KIND: &'static str;
fn new(extent: Extent, cell_size: f32) -> Self;
fn rebuild(&mut self, pos_x: &[f32], pos_y: &[f32], cell_size: f32);
fn heap_bytes(&self) -> usize;
}
#[derive(Debug)]
pub struct NoIndex;
impl NeighborIndex for NoIndex {
const KIND: &'static str = "None";
fn new(_extent: Extent, _cell_size: f32) -> Self {
Self
}
fn rebuild(&mut self, _pos_x: &[f32], _pos_y: &[f32], _cell_size: f32) {}
fn heap_bytes(&self) -> usize {
0
}
}
impl NeighborIndex for SpatialHash {
const KIND: &'static str = "Spatial hash";
fn new(extent: Extent, cell_size: f32) -> Self {
Self::new(cell_size, extent.w, extent.h)
}
fn rebuild(&mut self, pos_x: &[f32], pos_y: &[f32], cell_size: f32) {
self.rebuild_with_cell_size(cell_size, pos_x, pos_y);
self.build(pos_x, pos_y);
}
fn heap_bytes(&self) -> usize {
Self::heap_bytes(self)
}
}
pub trait ChunkTally: Default + Send + Sized + 'static {
fn merge(self, other: Self) -> Self;
}
impl ChunkTally for () {
fn merge(self, (): Self) {}
}
impl ChunkTally for u32 {
fn merge(self, other: Self) -> Self {
self.saturating_add(other)
}
}
impl ChunkTally for u64 {
fn merge(self, other: Self) -> Self {
self + other
}
}
pub struct StepCtx<'a, A: AgentModel + ?Sized> {
pub field: <A::Field as FieldLayer>::Read<'a>,
pub index: &'a A::Index,
pub params: &'a A::Params,
pub extent: Extent,
}
impl<A: AgentModel + ?Sized> std::fmt::Debug for StepCtx<'_, A> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("StepCtx")
.field("extent", &self.extent)
.finish_non_exhaustive()
}
}
pub trait AgentModel: Send + Sync + 'static {
const NAME: &'static str;
const ID: &'static str;
const DESCRIPTION: &'static str;
const PALETTE: &'static [[u8; 4]];
const STATS: &'static [StatDescriptor];
const ACTIONS: &'static [ActionDescriptor] = &[];
const CHUNK: usize = 512;
const DEFAULT_AGENTS: u32;
const MAX_AGENTS: u32 = 10_000_000;
const DEFAULT_EXTENT: Extent;
type Lanes: AgentLanes;
type Field: FieldLayer;
type Index: NeighborIndex;
type Params: Send + Sync;
type Tally: ChunkTally;
fn param_descriptors() -> Vec<ParamDescriptor>;
fn from_params(params: &[ParamValue], extent: Extent) -> Self::Params;
fn index_cell_size(_params: &Self::Params) -> f32 {
1.0
}
fn init(lanes: &mut Self::Lanes, extent: Extent, params: &[ParamValue], rng: &mut u64);
fn run_deposit_pass(
_lanes: &Self::Lanes,
_deposits: &mut <Self::Field as FieldLayer>::DepositLanes,
_ctx: &StepCtx<'_, Self>,
) {
}
fn run_step_pass(lanes: &mut Self::Lanes, ctx: &StepCtx<'_, Self>, seed: u64, tick: u64) -> Self::Tally;
fn act(
_action: usize,
_lanes: &mut Self::Lanes,
_field: &mut Self::Field,
_extent: Extent,
_params: &[ParamValue],
_rng: &mut u64,
) {
}
fn stats(lanes: &Self::Lanes, field: &Self::Field, tally: &Self::Tally) -> Vec<StatValue>;
}
#[cfg(test)]
mod tests {
use super::ChunkTally;
#[test]
fn a_u32_tally_saturates() {
assert_eq!(ChunkTally::merge(u32::MAX - 1, 5), u32::MAX);
assert_eq!(ChunkTally::merge(2_u32, 3), 5);
}
}