Pane::step, test diodes

main
Shad Amethyst 2 years ago
parent d61650316a
commit 66ccc1dd30
Signed by: amethyst
GPG Key ID: D970C8DD1D6DEE36

@ -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<Signal>, ctx: TransmitContext<'b>) {}

@ -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<Item=(usize, usize, &'b FullTile)> + '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<Item=(usize, usize, &'b mut FullTile)> + 'b {
let width = self.width;
self.tiles.iter_mut().enumerate().map(move |(i, v)| (i % width, i / width, v))
}
}

@ -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
}

@ -42,6 +42,11 @@ impl Diode {
impl Tile for Diode {
fn transmit<'b>(&'b self, signal: Rc<Signal>, 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);
}
}
}
}

Loading…
Cancel
Save