diff --git a/stackline-cli/src/main.rs b/stackline-cli/src/main.rs index f8544db..b21f063 100644 --- a/stackline-cli/src/main.rs +++ b/stackline-cli/src/main.rs @@ -273,6 +273,8 @@ fn main() { fn run(world: &mut World, steps: usize) -> std::io::Result<()> { let mut stdout = std::io::stdout(); let mut first = true; + + world.init(); for _ in 0..steps { if !first { world.step(); diff --git a/stackline/src/context.rs b/stackline/src/context.rs index 6db052c..6a9b134 100644 --- a/stackline/src/context.rs +++ b/stackline/src/context.rs @@ -479,3 +479,29 @@ impl UpdateCommit { } } } + +#[derive(Clone)] +#[non_exhaustive] +pub struct InitContext<'a> { + pub world: &'a World, + pub pane: &'a Pane, + pub world_coordinates: (i32, i32), + pub pane_coordinates: (String, usize, usize), +} + +impl<'a> InitContext<'a> { + pub fn new(world: &'a World, coordinates: (String, usize, usize)) -> Option { + let pane = world.get_pane(&coordinates.0)?; + let position = pane.position(); + + Some(InitContext { + world, + pane, + world_coordinates: ( + position.0 + coordinates.1 as i32, + position.1 + coordinates.2 as i32, + ), + pane_coordinates: coordinates, + }) + } +} diff --git a/stackline/src/pane.rs b/stackline/src/pane.rs index 651034d..b261c07 100644 --- a/stackline/src/pane.rs +++ b/stackline/src/pane.rs @@ -10,6 +10,7 @@ pub struct Pane { position: (i32, i32), + #[serde(skip)] pub(crate) signals: Vec<(usize, usize)>, } @@ -56,6 +57,34 @@ impl Pane { }) } + pub(crate) fn init(&self, name: &str, world: &World) { + for index in 0..self.tiles.len() { + let mut tile = self + .tiles + .borrow_mut(index) + .unwrap_or_else(|| unreachable!()); + let ctx = InitContext::new( + world, + (name.to_string(), index % self.width, index / self.width), + ) + .unwrap(); + + tile.init(ctx); + } + } + + pub(crate) fn init_signals(&mut self) { + let mut signals = Vec::new(); + + for (x, y, tile) in self.tiles_iter() { + if tile.signal().is_some() { + signals.push((x, y)); + } + } + + self.signals = signals; + } + /// Returns the width of the current pane /// /// ## Example @@ -480,9 +509,13 @@ impl Pane { commit.apply(self) } + pub fn tiles(&self) -> &VecCell { + &self.tiles + } + /// Returns an iterator over the tiles and their coordinates #[inline] - pub fn tiles(&self) -> impl Iterator)> + '_ { + pub fn tiles_iter(&self) -> impl Iterator)> + '_ { self.tiles .iter() .enumerate() @@ -492,7 +525,7 @@ impl Pane { /// Draws the Pane at `(dx + self.position.0, dy + self.position.1)` on a [`TextSurface`]. /// Empty tiles will leave the `TextSurface` untouched, but tiles are free to modify the characters around them. pub fn draw(&self, dx: i32, dy: i32, surface: &mut TextSurface) { - for (x, y, tile) in self.tiles() { + for (x, y, tile) in self.tiles_iter() { let x = x as i32 + dx + self.position.0 as i32; let y = y as i32 + dy + self.position.1 as i32; @@ -614,4 +647,41 @@ mod test { } } } + + #[test] + fn test_init() { + use crate::tile::Wire; + use std::collections::HashSet; + use Orientation::*; + + let mut pane = test_tile_setup!( + 2, + 2, + [ + Wire::new(Horizontal), + Wire::new(Vertical), + Wire::new(Any), + () + ] + ); + + assert_eq!(pane.signals, vec![]); + + pane.get_mut((0, 0)) + .unwrap() + .set_signal(Some(Signal::empty((0, 0), Direction::Right))); + pane.get_mut((1, 0)) + .unwrap() + .set_signal(Some(Signal::empty((1, 0), Direction::Right))); + + pane.init_signals(); + + assert_eq!( + pane.signals + .iter() + .copied() + .collect::>(), + HashSet::from_iter(vec![(0, 0), (1, 0)].into_iter()) + ); + } } diff --git a/stackline/src/tile/full.rs b/stackline/src/tile/full.rs index d5f8793..cb570a8 100644 --- a/stackline/src/tile/full.rs +++ b/stackline/src/tile/full.rs @@ -29,6 +29,12 @@ impl FullTile { } } + pub fn init<'b>(&'b mut self, context: InitContext<'b>) { + if let Some(ref mut tile) = self.cell { + tile.init(context) + } + } + pub fn accepts_signal(&self, direction: Direction) -> bool { match self.cell { Some(ref tile) => self.state.accepts_signal() && tile.accepts_signal(direction), diff --git a/stackline/src/tile/mod.rs b/stackline/src/tile/mod.rs index 4dc56cc..6f9916e 100644 --- a/stackline/src/tile/mod.rs +++ b/stackline/src/tile/mod.rs @@ -141,7 +141,15 @@ include!(concat!(env!("OUT_DIR"), "/anytile.rs")); /// ``` #[enum_dispatch(AnyTile)] pub trait Tile: std::clone::Clone + std::fmt::Debug + Serialize + for<'d> Deserialize<'d> { + #[inline] + #[allow(unused_variables)] + fn init<'b>(&'b mut self, context: InitContext<'b>) { + // noop + } + /// Function to be called when the tile needs to be updated. + /// + /// This function might be called every frame, so as much work should be done in [`Tile::init()`] instead. #[inline] fn update<'b>(&'b mut self, mut context: UpdateContext<'b>) { context.next_state(); diff --git a/stackline/src/world.rs b/stackline/src/world.rs index 7270e5d..52c9258 100644 --- a/stackline/src/world.rs +++ b/stackline/src/world.rs @@ -1,7 +1,7 @@ use super::*; use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use veccell::{VecRef, VecRefMut}; +use veccell::VecRef; #[derive(Debug, Serialize, Deserialize)] pub struct World { @@ -15,6 +15,19 @@ impl World { } } + /// Method to be called before running the simulation + // TODO: set a flag to true? + pub fn init(&mut self) { + for (name, pane) in self.panes.iter() { + pane.init(name, self); + } + + for pane in self.panes.values_mut() { + pane.init_signals(); + } + } + + /// Runs a single step of the simulation pub fn step(&mut self) { let mut outbound_signals = Vec::new(); diff --git a/stackline/tiles/wire.rs b/stackline/tiles/wire.rs index a70bcf6..b6eef8a 100644 --- a/stackline/tiles/wire.rs +++ b/stackline/tiles/wire.rs @@ -172,7 +172,7 @@ mod test { assert_no_signal!(pane, (1, 0)); pane.step(); - for (_, _, tile) in pane.tiles() { + for tile in pane.tiles() { assert!(tile.signal().is_none()); }