use ggez::{graphics, mint::Point2, Context, GameError};
use semeion::*;
use std::{any::Any, rc::Rc};
use super::Kind;
use crate::{env, Meshes};
#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)]
pub enum State {
ElectronHead,
ElectronTail,
Conductor,
}
impl entity::State for State {
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl State {
pub fn mesh(self, ctx: &mut Context) -> Result<graphics::Mesh, GameError> {
match self {
Self::ElectronHead => {
let blue = graphics::Color::new(0.0, 0.0, 255.0, 1.0);
Self::build_mesh(ctx, blue)
}
Self::ElectronTail => {
let red = graphics::Color::new(255.0, 0.0, 0.0, 1.0);
Self::build_mesh(ctx, red)
}
Self::Conductor => {
let yellow = graphics::Color::new(255.0, 255.0, 0.0, 1.0);
Self::build_mesh(ctx, yellow)
}
}
}
fn build_mesh(
ctx: &mut Context,
color: graphics::Color,
) -> Result<graphics::Mesh, GameError> {
let mut mesh = graphics::MeshBuilder::new();
let bounds = graphics::Rect::new(0.0, 0.0, env::SIDE, env::SIDE);
mesh.rectangle(graphics::DrawMode::fill(), bounds, color)?;
mesh.build(ctx)
}
}
#[derive(Debug)]
struct StateSnapshot {
current: State,
next: State,
}
impl StateSnapshot {
fn new(state: State) -> Self {
Self {
current: state,
next: state,
}
}
}
#[derive(Debug)]
pub struct Cell {
id: Id,
location: Location,
meshes: Rc<Meshes>,
state: StateSnapshot,
}
impl Cell {
pub fn new(location: Location, state: State, meshes: Rc<Meshes>) -> Self {
Self {
id: rand::random(),
location,
meshes,
state: StateSnapshot::new(state),
}
}
}
impl<'a> Entity<'a> for Cell {
type Kind = Kind;
type Context = Context;
fn id(&self) -> Id {
self.id
}
fn kind(&self) -> Self::Kind {
Kind::Cell
}
fn location(&self) -> Option<Location> {
Some(self.location)
}
fn scope(&self) -> Option<Scope> {
Some(Scope::with_magnitude(1))
}
fn state(&self) -> Option<&dyn entity::State> {
Some(&self.state.current)
}
fn observe(
&mut self,
neighborhood: Option<Neighborhood<Self::Kind, Self::Context>>,
) -> Result<(), Error> {
self.state.next = match self.state.current {
State::ElectronHead => State::ElectronTail,
State::ElectronTail => State::Conductor,
State::Conductor => {
let hood = neighborhood.expect("Invalid neighborhood");
let border = hood
.immediate_border(Scope::with_magnitude(1))
.expect("Invalid border");
let neighbors = border.iter().map(|t| t.entities()).flatten();
let count = neighbors
.filter(|e| {
let state = e
.state()
.and_then(|s| s.as_any().downcast_ref::<State>())
.expect("Invalid state");
state == &State::ElectronHead
})
.count();
if count == 1 || count == 2 {
State::ElectronHead
} else {
State::Conductor
}
}
};
Ok(())
}
fn react(
&mut self,
_: Option<Neighborhood<Self::Kind, Self::Context>>,
) -> Result<(), Error> {
self.state.current = self.state.next;
Ok(())
}
fn draw(
&self,
ctx: &mut Self::Context,
transform: Transform,
) -> Result<(), Error> {
debug_assert_eq!(transform, Transform::identity());
let offset = self.location.to_pixel_coords(env::SIDE);
let offset = Point2 {
x: offset.x,
y: offset.y,
};
let mesh = self
.meshes
.get(self.state.current)
.unwrap_or_else(|| panic!("No mesh for state {:?}", self.state));
let param = graphics::DrawParam::default();
graphics::draw(ctx, mesh, param.dest(offset))
.map_err(Error::with_message)
}
}