use crate::observation::Observation;
use crate::state::State;
use crate::substrates::ca::state::CAState;
#[derive(Debug, Clone, Default)]
pub struct FullStateObserver;
impl<const N: usize, const R: usize> Observation<CAState<N, R>> for FullStateObserver {
type Output = Vec<u8>;
fn observe(&self, state: &CAState<N, R>) -> Self::Output {
state.canonical_encoding()
}
}
#[derive(Debug, Clone, Default)]
pub struct DensityObserver;
impl<const N: usize, const R: usize> Observation<CAState<N, R>> for DensityObserver {
type Output = Vec<u8>;
fn observe(&self, state: &CAState<N, R>) -> Self::Output {
let count: u8 = state.cells().iter().copied().sum();
vec![count]
}
}
#[derive(Debug, Clone, Default)]
pub struct ParityObserver;
impl<const N: usize, const R: usize> Observation<CAState<N, R>> for ParityObserver {
type Output = Vec<u8>;
fn observe(&self, state: &CAState<N, R>) -> Self::Output {
let parity: u8 = state.cells().iter().copied().sum::<u8>() % 2;
vec![parity]
}
}
#[derive(Debug, Clone)]
pub enum CAObserver {
FullState,
Density,
Parity,
}
impl CAObserver {
pub fn from_name(name: &str) -> Self {
match name {
"full_state" => Self::FullState,
"density" => Self::Density,
"parity" => Self::Parity,
_ => Self::FullState,
}
}
pub fn name(&self) -> &str {
match self {
Self::FullState => "full_state",
Self::Density => "density",
Self::Parity => "parity",
}
}
}
impl<const N: usize, const R: usize> Observation<CAState<N, R>> for CAObserver {
type Output = Vec<u8>;
fn observe(&self, state: &CAState<N, R>) -> Self::Output {
match self {
Self::FullState => FullStateObserver.observe(state),
Self::Density => DensityObserver.observe(state),
Self::Parity => ParityObserver.observe(state),
}
}
}
pub fn get_observer<const N: usize, const R: usize>(
name: &str,
) -> Option<Box<dyn Observation<CAState<N, R>, Output = Vec<u8>> + Send + Sync>> {
match name {
"full_state" => Some(Box::new(FullStateObserver)),
"density" => Some(Box::new(DensityObserver)),
"parity" => Some(Box::new(ParityObserver)),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_full_state_observer() {
let state = CAState::<8, 1>::new([1, 0, 1, 0, 0, 0, 0, 0]);
let obs = FullStateObserver;
assert_eq!(obs.observe(&state), vec![1, 0, 1, 0, 0, 0, 0, 0]);
}
#[test]
fn test_density_observer() {
let state = CAState::<8, 1>::new([1, 0, 1, 0, 0, 0, 0, 0]);
let obs = DensityObserver;
assert_eq!(obs.observe(&state), vec![2]); }
#[test]
fn test_parity_observer() {
let state = CAState::<8, 1>::new([1, 0, 1, 0, 0, 0, 0, 0]);
let obs = ParityObserver;
assert_eq!(obs.observe(&state), vec![0]); }
#[test]
fn test_full_state_distinguishes() {
let s1 = CAState::<8, 1>::new([0; 8]);
let s2 = CAState::<8, 1>::new([1, 0, 0, 0, 0, 0, 0, 0]);
let obs = FullStateObserver;
assert_ne!(obs.observe(&s1), obs.observe(&s2));
}
#[test]
fn test_get_observer() {
let obs = get_observer::<8, 1>("density");
assert!(obs.is_some());
let obs = get_observer::<8, 1>("nonexistent");
assert!(obs.is_none());
}
}