phago_core/primitives/sense.rs
1//! SENSE — Chemotaxis
2//!
3//! Cells navigate by sensing chemical gradients. A neutrophil detects
4//! increasing concentrations of inflammatory signals and moves toward
5//! the source. The cell doesn't know where the problem is — it follows
6//! the gradient.
7//!
8//! Agents also emit signals (autoinducers) for quorum sensing. When
9//! enough agents emit presence signals, the collective detects its own
10//! density and can trigger phase transitions.
11
12use crate::substrate::Substrate;
13use crate::types::*;
14
15/// Detect environmental signals, compute gradients, and emit signals.
16///
17/// Sensing is local, not global. An agent only perceives signals within
18/// its sensing radius — it has no access to the full substrate state.
19/// Navigation emerges from following local gradients, not from knowing
20/// the global map.
21pub trait Sense {
22 /// The radius within which this agent can sense signals.
23 fn sense_radius(&self) -> f64;
24
25 /// Read signals from the local environment.
26 ///
27 /// Returns only signals within `sense_radius` of the agent's position.
28 fn sense(&self, substrate: &dyn Substrate) -> Vec<Signal> {
29 let position = self.sense_position();
30 let radius = self.sense_radius();
31 substrate
32 .signals_near(&position, radius)
33 .into_iter()
34 .cloned()
35 .collect()
36 }
37
38 /// The position from which this agent senses.
39 fn sense_position(&self) -> Position;
40
41 /// Compute the gradient — direction of increasing signal strength.
42 ///
43 /// Like a cell comparing receptor binding rates across its surface.
44 fn gradient(&self, substrate: &dyn Substrate) -> Vec<Gradient>;
45
46 /// Emit a signal into the substrate.
47 ///
48 /// Used for quorum sensing (presence signals), alerting
49 /// (anomaly signals), and coordination (capability signals).
50 fn emit(&self, signal: Signal, substrate: &mut dyn Substrate) {
51 substrate.emit_signal(signal);
52 }
53
54 /// Determine next orientation based on sensed gradients.
55 ///
56 /// Returns where the agent should move or what it should prioritize.
57 fn orient(&self, gradients: &[Gradient]) -> Orientation;
58}