diff --git a/stackline/Cargo.toml b/stackline/Cargo.toml old mode 100644 new mode 100755 diff --git a/stackline/src/context.rs b/stackline/src/context.rs index ecc796f..43cb646 100644 --- a/stackline/src/context.rs +++ b/stackline/src/context.rs @@ -24,7 +24,7 @@ This type is most commonly found when implementing [`Tile::update`]: struct MyTile(u8); impl Tile for MyTile { - fn update<'b>(&'b mut self, ctx: UpdateContext<'b>) { + fn update<'b>(&'b mut self, mut ctx: UpdateContext<'b>) { // Counts the number of active neighbors let mut active_neighbors = 0; @@ -45,7 +45,7 @@ impl Tile for MyTile { } self.0 = active_neighbors; - ctx.next_state(); // Go dormant + ctx.next_state(); // Become dormant } # # fn transmit<'b>(&'b self, signal: std::rc::Rc, ctx: TransmitContext<'b>) {} diff --git a/stackline/src/pane.rs b/stackline/src/pane.rs index 5b5ff07..c45f846 100644 --- a/stackline/src/pane.rs +++ b/stackline/src/pane.rs @@ -91,7 +91,8 @@ impl Pane { Some(()) } - pub fn update_all(&mut self) { + /// Calls [`Pane::update`] on all non-empty, non-idle tiles + fn update_all(&mut self) { for y in 0..self.height.get() { for x in 0..self.width.get() { if let Some((ctx, tile)) = UpdateContext::new(self, (x, y)) { @@ -112,7 +113,8 @@ impl Pane { Some(()) } - pub fn transmit_all(&mut self) { + /// Calls [`Pane::transmit`] on all tiles with a signal + fn transmit_all(&mut self) { // TODO: store a second buffer and perform swap reads for signal in std::mem::replace(&mut self.signals, vec![]) { if let Some(upgraded) = signal.upgrade() { @@ -122,9 +124,24 @@ impl Pane { } } + /// Runs a single simulation step, which consists of: + /// - an update phase, which mutates the inner state of [active](State::Active)] cells by calling [`Tile::update`] + /// - a transmit phase, which mutates and moves signals between cells by calling [`Tile::transmit`] + pub fn step(&mut self) { + self.update_all(); + self.transmit_all(); + } + /// Returns an iterator over the tiles and their coordinates #[inline] pub fn tiles<'b>(&'b self) -> impl Iterator + 'b { self.tiles.iter().enumerate().map(move |(i, v)| (i % self.width, i / self.width, v)) } + + /// Returns a mutable iterator over the tiles and their coordinates + #[inline] + pub fn tiles_mut<'b>(&'b mut self) -> impl Iterator + 'b { + let width = self.width; + self.tiles.iter_mut().enumerate().map(move |(i, v)| (i % width, i / width, v)) + } } diff --git a/stackline/src/tile/mod.rs b/stackline/src/tile/mod.rs index 17ac71b..ecdc80e 100644 --- a/stackline/src/tile/mod.rs +++ b/stackline/src/tile/mod.rs @@ -130,6 +130,7 @@ pub trait Tile: DynClone + std::fmt::Debug { /// Should return true iff the tile accepts a signal travelling in `Direction` #[inline] + #[allow(unused_variables)] fn accepts_signal(&self, direction: Direction) -> bool { true } diff --git a/stackline/src/tile/wire.rs b/stackline/src/tile/wire.rs index 7e36a5d..6823f7b 100644 --- a/stackline/src/tile/wire.rs +++ b/stackline/src/tile/wire.rs @@ -42,6 +42,11 @@ impl Diode { impl Tile for Diode { fn transmit<'b>(&'b self, signal: Rc, mut context: TransmitContext<'b>) { + // Block signals coming from where the diode is looking + if signal.direction().opposite() == self.0 { + return; + } + if let Some(pos) = context.offset(self.0.into_offset()) { if context.accepts_signal(pos, self.0) { context.send(pos, signal.clone_move(self.0).unwrap_or_else(|| unreachable!())); @@ -66,49 +71,79 @@ mod test { // Test the signal going from left to right test_set_signal!(pane, (0, 0), Direction::Right); - pane.update_all(); - pane.transmit_all(); + pane.step(); assert_signal!(pane, (1, 0)); assert_no_signal!(pane, (0, 0)); assert_no_signal!(pane, (2, 0)); assert_no_signal!(pane, (1, 1)); - pane.update_all(); - pane.transmit_all(); + pane.step(); assert_signal!(pane, (2, 0)); assert_signal!(pane, (1, 1)); assert_no_signal!(pane, (0, 0)); assert_no_signal!(pane, (1, 0)); - pane.update_all(); - pane.transmit_all(); + pane.step(); for (_, _, tile) in pane.tiles() { assert!(tile.signal().is_none()); } // Let the simulation cool down - pane.update_all(); - pane.update_all(); + pane.step(); + pane.step(); // Test the signal going from right to left test_set_signal!(pane, (2, 0), Direction::Left); - pane.update_all(); - pane.transmit_all(); + pane.step(); assert_signal!(pane, (1, 0)); assert_no_signal!(pane, (0, 0)); assert_no_signal!(pane, (2, 0)); assert_no_signal!(pane, (1, 1)); - pane.update_all(); - pane.transmit_all(); + pane.step(); assert_signal!(pane, (0, 0)); assert_signal!(pane, (1, 1)); assert_no_signal!(pane, (2, 0)); assert_no_signal!(pane, (1, 0)); } + + #[test] + fn test_diode_transmit() { + use crate::Direction::*; + + let mut pane = test_tile_setup!(3, 2, [ + Diode::new(Right), Diode::new(Right), Diode::new(Down), + (), Diode::new(Up), Diode::new(Left) + ]); + + // Test the signal going from left to right + test_set_signal!(pane, (0, 0), Direction::Right); + + pane.step(); + assert_signal!(pane, (1, 0)); + assert_no_signal!(pane, (0, 0)); + + let positions = [ + (2, 0), + (2, 1), + (1, 1), + (1, 0) + ]; + + for &pos in positions.iter().cycle().take(16) { + pane.step(); + assert_signal!(pane, pos); + for &pos2 in positions.iter() { + if pos == pos2 { + continue + } + assert_no_signal!(pane, pos2); + } + } + } }