From 21ae3bee01a8c002258cbbe1608cf58d80772253 Mon Sep 17 00:00:00 2001 From: Adrien Burgun Date: Mon, 15 Aug 2022 13:06:12 +0200 Subject: [PATCH] :sparkles: If, Cmp, Stacker --- stackline/src/signal.rs | 5 ++ stackline/tests/logic.rs | 27 ++++++++ stackline/tests/logic/if.json | 1 + stackline/tests/other.rs | 34 ++++++++++ stackline/tests/other/prime.json | 1 + stackline/tests/storage.rs | 18 +++++ stackline/tests/storage/natural.json | 1 + stackline/tiles/arithmetic.rs | 68 +++++++++++++++++++ stackline/tiles/logic.rs | 69 ++++++++++++++++++++ stackline/tiles/stack.rs | 47 ++++++++++++- stackline/tiles/storage.rs | 98 +++++++++++++++++++++++++++- stackline/tiles/transmit.rs | 2 +- stackline/tiles/wire.rs | 6 ++ 13 files changed, 372 insertions(+), 5 deletions(-) create mode 100644 stackline/tests/logic.rs create mode 100644 stackline/tests/logic/if.json create mode 100644 stackline/tests/other.rs create mode 100644 stackline/tests/other/prime.json create mode 100644 stackline/tests/storage/natural.json create mode 100644 stackline/tiles/logic.rs diff --git a/stackline/src/signal.rs b/stackline/src/signal.rs index 1325eb4..345b1c2 100644 --- a/stackline/src/signal.rs +++ b/stackline/src/signal.rs @@ -196,6 +196,11 @@ impl Signal { self.stack.len() } + /// Puts `other` on top of `self` + pub fn append(&mut self, other: &Signal) { + self.stack.extend(other.stack.iter().cloned()); + } + pub fn is_stack_empty(&self) -> bool { self.stack.is_empty() } diff --git a/stackline/tests/logic.rs b/stackline/tests/logic.rs new file mode 100644 index 0000000..8aa065c --- /dev/null +++ b/stackline/tests/logic.rs @@ -0,0 +1,27 @@ +#[allow(unused_imports)] +use stackline::prelude::*; +use stackline::signal; +mod common; + +#[test] +fn test_if() { + let mut world = load_test!("tests/logic/if.json"); + world.init(); + + world.set_signal((0, 0), signal!([0])); + + run!(world, 8); + + assert_stored!(world, 1, 2); + assert_no_stored!(world, 2, 2); + + let mut world = load_test!("tests/logic/if.json"); + world.init(); + + world.set_signal((0, 0), signal!([1])); + + run!(world, 8); + + assert_no_stored!(world, 1, 2); + assert_stored!(world, 2, 2); +} diff --git a/stackline/tests/logic/if.json b/stackline/tests/logic/if.json new file mode 100644 index 0000000..6e61279 --- /dev/null +++ b/stackline/tests/logic/if.json @@ -0,0 +1 @@ +{"panes":{"main":{"tiles":[{"cell":{"Diode":"Right"},"signal":null,"state":"Idle","updated":false},{"cell":{"If":{"invert":false}},"signal":null,"state":"Idle","updated":false},{"cell":{"Diode":"Down"},"signal":null,"state":"Idle","updated":false},{"cell":null,"signal":null,"state":"Idle","updated":false},{"cell":{"Wire":"Vertical"},"signal":null,"state":"Idle","updated":false},{"cell":{"Wire":"Vertical"},"signal":null,"state":"Idle","updated":false},{"cell":null,"signal":null,"state":"Idle","updated":false},{"cell":{"Store":{"signal":null}},"signal":null,"state":"Idle","updated":false},{"cell":{"Store":{"signal":null}},"signal":null,"state":"Idle","updated":false}],"width":3,"height":3,"position":[0,0]}},"blink_speed":{"secs":0,"nanos":250000000}} \ No newline at end of file diff --git a/stackline/tests/other.rs b/stackline/tests/other.rs new file mode 100644 index 0000000..8ac68d5 --- /dev/null +++ b/stackline/tests/other.rs @@ -0,0 +1,34 @@ +#[allow(unused_imports)] +use stackline::prelude::*; +use stackline::signal; +mod common; + +#[test] +fn test_prime() { + let mut world = load_test!("tests/other/prime.json"); + world.set_signal((4, 0), signal!()); + world.init(); + + run!(world, 500); + + assert_stored!(world, 1, 3, signal!([2, 3, 5, 7, 11, 13, 17, 19])); + + run!(world, 1500); + + let mut signal = signal!([2]); + + for n in 3..=53 { + let mut prime = true; + for f in 2..n { + if n % f == 0 { + prime = false; + break + } + } + if prime { + signal.push(Value::Number(n.into())); + } + } + + assert_stored!(world, 1, 3, signal); +} diff --git a/stackline/tests/other/prime.json b/stackline/tests/other/prime.json new file mode 100644 index 0000000..685c55e --- /dev/null +++ b/stackline/tests/other/prime.json @@ -0,0 +1 @@ +{"panes":{"main":{"tiles":[{"cell":{"Diode":"Down"},"signal":null,"state":"Idle","updated":false},{"cell":{"Diode":"Left"},"signal":null,"state":"Idle","updated":false},{"cell":null,"signal":null,"state":"Idle","updated":false},{"cell":null,"signal":null,"state":"Idle","updated":false},{"cell":{"Diode":"Right"},"signal":null,"state":"Idle","updated":false},{"cell":{"Wire":"Any"},"signal":null,"state":"Idle","updated":false},{"cell":{"Push":{"value":{"Number":1.0}}},"signal":null,"state":"Idle","updated":false},{"cell":{"Diode":"Right"},"signal":null,"state":"Idle","updated":false},{"cell":{"Push":{"value":{"Number":2.0}}},"signal":null,"state":"Idle","updated":false},{"cell":{"Add":null},"signal":null,"state":"Idle","updated":false},{"cell":{"Diode":"Down"},"signal":null,"state":"Idle","updated":false},{"cell":{"Wire":"Vertical"},"signal":null,"state":"Idle","updated":false},{"cell":{"Stacker":{"signal":null,"orientation":"Vertical"}},"signal":null,"state":"Idle","updated":false},{"cell":{"Wire":"Any"},"signal":null,"state":"Idle","updated":false},{"cell":{"Diode":"Left"},"signal":null,"state":"Idle","updated":false},{"cell":{"Push":{"value":{"Number":2.0}}},"signal":null,"state":"Idle","updated":false},{"cell":{"Diode":"Left"},"signal":null,"state":"Idle","updated":false},{"cell":null,"signal":null,"state":"Idle","updated":false},{"cell":{"Diode":"Up"},"signal":null,"state":"Idle","updated":false},{"cell":{"Reader":"Left"},"signal":null,"state":"Idle","updated":false},{"cell":{"Store":{"signal":null}},"signal":null,"state":"Idle","updated":false},{"cell":{"Wire":"Any"},"signal":null,"state":"Idle","updated":false},{"cell":{"Wire":"Vertical"},"signal":null,"state":"Idle","updated":false},{"cell":{"Reader":"Up"},"signal":null,"state":"Idle","updated":false},{"cell":{"Resistor":{"direction":"Left","signal":null}},"signal":null,"state":"Idle","updated":false},{"cell":null,"signal":null,"state":"Idle","updated":false},{"cell":null,"signal":null,"state":"Idle","updated":false},{"cell":null,"signal":null,"state":"Idle","updated":false},{"cell":null,"signal":null,"state":"Idle","updated":false},{"cell":null,"signal":null,"state":"Idle","updated":false},{"cell":{"Wire":"Vertical"},"signal":null,"state":"Idle","updated":false},{"cell":null,"signal":null,"state":"Idle","updated":false},{"cell":{"Wire":"Vertical"},"signal":null,"state":"Idle","updated":false},{"cell":{"Diode":"Right"},"signal":null,"state":"Idle","updated":false},{"cell":{"Store":{"signal":{"direction":"Right","stack":[]}}},"signal":null,"state":"Idle","updated":false},{"cell":{"Diode":"Right"},"signal":null,"state":"Idle","updated":false},{"cell":{"Wire":"Horizontal"},"signal":null,"state":"Idle","updated":false},{"cell":{"Wire":"Horizontal"},"signal":null,"state":"Idle","updated":false},{"cell":{"Wire":"Horizontal"},"signal":null,"state":"Idle","updated":false},{"cell":{"Wire":"Horizontal"},"signal":null,"state":"Idle","updated":false},{"cell":{"Wire":"Horizontal"},"signal":null,"state":"Idle","updated":false},{"cell":{"Diode":"Up"},"signal":null,"state":"Idle","updated":false},{"cell":null,"signal":null,"state":"Idle","updated":false},{"cell":{"Push":{"value":{"Number":2.0}}},"signal":null,"state":"Idle","updated":false},{"cell":null,"signal":null,"state":"Idle","updated":false},{"cell":null,"signal":null,"state":"Idle","updated":false},{"cell":{"Diode":"Up"},"signal":null,"state":"Idle","updated":false},{"cell":{"Wire":"Any"},"signal":null,"state":"Idle","updated":false},{"cell":{"If":{"invert":false}},"signal":null,"state":"Idle","updated":false},{"cell":{"Cmp":"Gt"},"signal":null,"state":"Idle","updated":false},{"cell":{"Duplicate":{"offset":2}},"signal":null,"state":"Idle","updated":false},{"cell":{"Mul":null},"signal":null,"state":"Idle","updated":false},{"cell":{"Duplicate":{"offset":0}},"signal":null,"state":"Idle","updated":false},{"cell":{"Duplicate":{"offset":0}},"signal":null,"state":"Idle","updated":false},{"cell":{"Diode":"Left"},"signal":null,"state":"Idle","updated":false},{"cell":null,"signal":null,"state":"Idle","updated":false},{"cell":null,"signal":null,"state":"Idle","updated":false},{"cell":{"Wire":"Vertical"},"signal":null,"state":"Idle","updated":false},{"cell":{"Pop":{"amount":1}},"signal":null,"state":"Idle","updated":false},{"cell":{"Duplicate":{"offset":1}},"signal":null,"state":"Idle","updated":false},{"cell":null,"signal":null,"state":"Idle","updated":false},{"cell":null,"signal":null,"state":"Idle","updated":false},{"cell":null,"signal":null,"state":"Idle","updated":false},{"cell":null,"signal":null,"state":"Idle","updated":false},{"cell":null,"signal":null,"state":"Idle","updated":false},{"cell":{"Wire":"Vertical"},"signal":null,"state":"Idle","updated":false},{"cell":null,"signal":null,"state":"Idle","updated":false},{"cell":null,"signal":null,"state":"Idle","updated":false},{"cell":{"Wire":"Vertical"},"signal":null,"state":"Idle","updated":false},{"cell":{"Teleporter":{"coordinates":["main",3,1]}},"signal":null,"state":"Idle","updated":false},{"cell":{"Diode":"Right"},"signal":null,"state":"Idle","updated":false},{"cell":{"Duplicate":{"offset":1}},"signal":null,"state":"Idle","updated":false},{"cell":{"Mod":null},"signal":null,"state":"Idle","updated":false},{"cell":{"If":{"invert":false}},"signal":null,"state":"Idle","updated":false},{"cell":{"Push":{"value":{"Number":1.0}}},"signal":null,"state":"Idle","updated":false},{"cell":{"Add":null},"signal":null,"state":"Idle","updated":false},{"cell":{"Diode":"Up"},"signal":null,"state":"Idle","updated":false},{"cell":null,"signal":null,"state":"Idle","updated":false},{"cell":null,"signal":null,"state":"Idle","updated":false},{"cell":{"Diode":"Up"},"signal":null,"state":"Idle","updated":false},{"cell":{"Wire":"Horizontal"},"signal":null,"state":"Idle","updated":false},{"cell":{"Wire":"Horizontal"},"signal":null,"state":"Idle","updated":false},{"cell":{"Wire":"Horizontal"},"signal":null,"state":"Idle","updated":false},{"cell":{"Wire":"Horizontal"},"signal":null,"state":"Idle","updated":false},{"cell":{"Diode":"Left"},"signal":null,"state":"Idle","updated":false},{"cell":null,"signal":null,"state":"Idle","updated":false},{"cell":null,"signal":null,"state":"Idle","updated":false},{"cell":null,"signal":null,"state":"Idle","updated":false}],"width":11,"height":8,"position":[0,0]}},"blink_speed":{"secs":0,"nanos":250000000}} \ No newline at end of file diff --git a/stackline/tests/storage.rs b/stackline/tests/storage.rs index c5e5199..1221241 100644 --- a/stackline/tests/storage.rs +++ b/stackline/tests/storage.rs @@ -37,3 +37,21 @@ fn test_counter() { assert_stored!(world, 0, 2, signal!([n])); } } + +#[test] +fn test_natural() { + for n in 1..10 { + let mut world = load_test!("tests/storage/natural.json"); + world.set_signal((0, 5), signal!()); + world.init(); + + run!(world, n * 11 + 6); + + let mut signal = signal!(); + for x in 0..n { + signal.push(Value::Number(x.into())); + } + + assert_stored!(world, 3, 4, signal); + } +} diff --git a/stackline/tests/storage/natural.json b/stackline/tests/storage/natural.json new file mode 100644 index 0000000..87937cd --- /dev/null +++ b/stackline/tests/storage/natural.json @@ -0,0 +1 @@ +{"panes":{"main":{"tiles":[{"cell":{"Diode":"Down"},"signal":null,"state":"Idle","updated":false},{"cell":{"Add":null},"signal":null,"state":"Idle","updated":false},{"cell":{"Diode":"Left"},"signal":null,"state":"Idle","updated":false},{"cell":null,"signal":null,"state":"Idle","updated":false},{"cell":null,"signal":null,"state":"Idle","updated":false},{"cell":{"Wire":"Vertical"},"signal":null,"state":"Idle","updated":false},{"cell":null,"signal":null,"state":"Idle","updated":false},{"cell":{"Push":{"value":{"Number":1.0}}},"signal":null,"state":"Idle","updated":false},{"cell":{"Diode":"Right"},"signal":null,"state":"Idle","updated":false},{"cell":{"Diode":"Down"},"signal":null,"state":"Idle","updated":false},{"cell":{"Store":{"signal":{"direction":"Right","stack":[{"Number":0.0}]}}},"signal":null,"state":"Idle","updated":false},{"cell":{"Reader":"Right"},"signal":null,"state":"Idle","updated":false},{"cell":{"Wire":"Any"},"signal":null,"state":"Idle","updated":false},{"cell":{"Stacker":{"signal":null,"orientation":"Vertical"}},"signal":null,"state":"Idle","updated":false},{"cell":{"Wire":"Vertical"},"signal":null,"state":"Idle","updated":false},{"cell":{"Diode":"Right"},"signal":null,"state":"Idle","updated":false},{"cell":{"Diode":"Up"},"signal":null,"state":"Idle","updated":false},{"cell":{"Resistor":{"direction":"Right","signal":null}},"signal":null,"state":"Idle","updated":false},{"cell":{"Reader":"Up"},"signal":null,"state":"Idle","updated":false},{"cell":{"Wire":"Vertical"},"signal":null,"state":"Idle","updated":false},{"cell":{"Wire":"Any"},"signal":null,"state":"Idle","updated":false},{"cell":{"Resistor":{"direction":"Right","signal":null}},"signal":null,"state":"Idle","updated":false},{"cell":{"Resistor":{"direction":"Down","signal":null}},"signal":null,"state":"Idle","updated":false},{"cell":{"Store":{"signal":{"direction":"Right","stack":[]}}},"signal":null,"state":"Idle","updated":false},{"cell":{"Diode":"Left"},"signal":null,"state":"Idle","updated":false},{"cell":{"Resistor":{"direction":"Up","signal":null}},"signal":null,"state":"Idle","updated":false},{"cell":{"Resistor":{"direction":"Left","signal":null}},"signal":null,"state":"Idle","updated":false},{"cell":{"Resistor":{"direction":"Left","signal":null}},"signal":null,"state":"Idle","updated":false},{"cell":null,"signal":null,"state":"Idle","updated":false},{"cell":null,"signal":null,"state":"Idle","updated":false}],"width":5,"height":6,"position":[0,0]}},"blink_speed":{"secs":0,"nanos":250000000}} \ No newline at end of file diff --git a/stackline/tiles/arithmetic.rs b/stackline/tiles/arithmetic.rs index c87181d..a2b0b79 100644 --- a/stackline/tiles/arithmetic.rs +++ b/stackline/tiles/arithmetic.rs @@ -145,3 +145,71 @@ impl Tile for Mod { TextChar::from_state('\u{2299}', ctx.state) // CIRCLED DOT OPERATOR } } + +/// Compares the top two values: `[..., a, b] -> [..., a ?? b]` +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct Cmp(Operation); + +#[derive(Debug, Clone, Serialize, Deserialize, Copy, PartialEq, Eq)] +pub enum Operation { + Eq, + Neq, + Gt, + Gte, + Lt, + Lte, +} + +impl Cmp { + fn binary_op(&self, left: Value, right: Value) -> Option { + use Value::*; + use Operation::*; + + let res = match (self.0, left, right) { + (Eq, Number(x), Number(y)) => Some(x == y), + (Eq, String(x), String(y)) => Some(x == y), + + (Neq, Number(x), Number(y)) => Some(x != y), + (Neq, String(x), String(y)) => Some(x != y), + + (Gt, Number(x), Number(y)) => Some(x > y), + (Gt, String(x), String(y)) => Some(x > y), + + (Gte, Number(x), Number(y)) => Some(x >= y), + (Gte, String(x), String(y)) => Some(x >= y), + + (Lt, Number(x), Number(y)) => Some(x < y), + (Lt, String(x), String(y)) => Some(x < y), + + (Lte, Number(x), Number(y)) => Some(x <= y), + (Lte, String(x), String(y)) => Some(x <= y), + + _ => None + }?; + + Some(Number(if res { 1.0 } else { 0.0 })) + } +} + +impl Tile for Cmp { + binary_op!(); + + fn draw_simple(&self, ctx: DrawContext) -> TextChar { + use Operation::*; + + match self.0 { + Eq => TextChar::from_state('\u{229c}', ctx.state), // CIRCLED EQUALS + Neq => TextChar::from_state('\u{2260}', ctx.state), // NOT EQUALS (the circled version doesn't exist) + Lt => TextChar::from_state('\u{29c0}', ctx.state), // CIRCLED LESS-THAN + Lte => TextChar::from_state('\u{2264}', ctx.state), // LESS-THAN OR EQUAL TO (no circled version) + Gt => TextChar::from_state('\u{29c1}', ctx.state), // CIRCLED GREATER-THAN + Gte => TextChar::from_state('\u{2265}', ctx.state), // GREATER-THAN OR EQUAL TO (no circled version) + } + } +} + +impl Default for Operation { + fn default() -> Self { + Self::Eq + } +} diff --git a/stackline/tiles/logic.rs b/stackline/tiles/logic.rs new file mode 100644 index 0000000..2b58322 --- /dev/null +++ b/stackline/tiles/logic.rs @@ -0,0 +1,69 @@ +//! Logical operations: if, etc. + +use crate::prelude::*; +use crate::tile::prelude::*; + +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct If { + invert: bool, +} + +impl If { + pub fn new(invert: bool) -> Self { + Self { + invert + } + } +} + +impl Tile for If { + fn update<'b>(&'b mut self, mut context: UpdateContext<'b>) { + if let Some(mut signal) = context.take_signal() { + let truthy = match signal.pop() { + Some(Value::Number(x)) => x != 0.0, + Some(Value::String(s)) => !s.is_empty(), + _ => false + }; + + let reorient = if self.invert { + truthy + } else { + !truthy + }; + + if reorient { + if matches!(signal.direction(), Direction::Left | Direction::Right) { + if let Some(up) = context.offset((0, -1)) { + let _ = context.send(up, Direction::Up, signal.clone()); + } + if let Some(down) = context.offset((0, 1)) { + let _ = context.send(down, Direction::Down, signal); + } + } else { + if let Some(left) = context.offset((-1, 0)) { + let _ = context.send(left, Direction::Left, signal.clone()); + } + if let Some(right) = context.offset((1, 0)) { + let _ = context.send(right, Direction::Right, signal); + } + } + } else { + if let Some(target_position) = context.offset(signal.direction().into_offset()) { + let _ = context.send(target_position, signal.direction(), signal); + } + } + } + + if context.state() != State::Idle { + context.next_state(); + } + } + + fn draw_simple(&self, ctx: DrawContext) -> TextChar { + if self.invert { + TextChar::from_state('¿', ctx.state) + } else { + TextChar::from_state('?', ctx.state) + } + } +} diff --git a/stackline/tiles/stack.rs b/stackline/tiles/stack.rs index df8cc56..1be8a35 100644 --- a/stackline/tiles/stack.rs +++ b/stackline/tiles/stack.rs @@ -29,7 +29,7 @@ impl Tile for Push { signal.push(self.value.clone()); if let Some(coords) = context.offset(signal.direction().into_offset()) { - context.send(coords, signal.direction(), signal).unwrap_or_else(|_| unreachable!()); + let _ = context.send(coords, signal.direction(), signal); } } @@ -71,7 +71,7 @@ impl Tile for Pop { } if let Some(coords) = context.offset(signal.direction().into_offset()) { - context.send(coords, signal.direction(), signal).unwrap_or_else(|_| unreachable!()); + let _ = context.send(coords, signal.direction(), signal); } } @@ -122,7 +122,7 @@ impl Tile for Swap { last_elements.reverse(); if let Some(coords) = context.offset(signal.direction().into_offset()) { - context.send(coords, signal.direction(), signal).unwrap_or_else(|_| unreachable!()); + let _ = context.send(coords, signal.direction(), signal); } } @@ -140,4 +140,45 @@ impl Tile for Swap { } } +/// Duplicates the nth element from the stack, pushing it back on top +#[derive(Clone, Debug, Serialize, Deserialize, Default)] +pub struct Duplicate { + offset: usize, +} + +impl Duplicate { + pub fn new(offset: usize) -> Self { + Self { + offset + } + } +} + +impl Tile for Duplicate { + fn update<'b>(&'b mut self, mut context: UpdateContext<'b>) { + if let Some(mut signal) = context.take_signal() { + if self.offset < signal.len() { + let index = signal.len() - self.offset - 1; + signal.push(signal.stack()[index].clone()); + } + + if let Some(coords) = context.offset(signal.direction().into_offset()) { + let _ = context.send(coords, signal.direction(), signal); + } + } + + if context.state() != State::Idle { + context.next_state(); + } + } + + fn draw_simple(&self, ctx: DrawContext) -> TextChar { + if self.offset == 0 { + TextChar::from_state('d', ctx.state) + } else { + TextChar::from_state(ctx.blink.scroll(&format!("ddd{}", self.offset)), ctx.state) + } + } +} + // TODO: SwapN, RotateLeft, RotateRight, RotateLeftN, RotateRightN diff --git a/stackline/tiles/storage.rs b/stackline/tiles/storage.rs index ba3d436..d2b06b1 100644 --- a/stackline/tiles/storage.rs +++ b/stackline/tiles/storage.rs @@ -63,6 +63,12 @@ impl Tile for Store { #[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct Reader(Direction); +impl Reader { + pub fn new(direction: Direction) -> Self { + Self(direction) + } +} + impl Tile for Reader { fn update<'b>(&'b mut self, mut context: UpdateContext<'b>) { if let Some(_signal) = context.take_signal() { @@ -74,7 +80,7 @@ impl Tile for Reader { let target_position = context.offset(self.0.into_offset())?; - context.send(target_position, self.0, signal); + let _ = context.send(target_position, self.0, signal); }; } @@ -93,6 +99,96 @@ impl Tile for Reader { } } +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct StorageCount; + +impl Tile for StorageCount { + fn update<'b>(&'b mut self, mut context: UpdateContext<'b>) { + if let Some(mut signal) = context.take_signal() { + let mut count = 0; + for dir in Orientation::Any.into_directions() { + if let Some(store_position) = context.offset(dir.into_offset()) { + if let Some(store) = context.get(store_position).and_then(get_store) { + count += store.signal.is_some() as u8; + } + } + } + + signal.push(Value::Number(count.into())); + + if let Some(target_position) = context.offset(signal.direction().into_offset()) { + let _ = context.send(target_position, signal.direction(), signal); + } + + if context.state() != State::Idle { + context.next_state(); + } + } + } + + fn draw_simple(&self, ctx: DrawContext) -> TextChar { + TextChar::from_state('\u{2058}', ctx.state) // FOUR DOT PUNCTUATION + } +} + +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct Stacker { + signal: Option, + /// May only be Horizontal or Vertical + orientation: Orientation, +} + +impl Stacker { + pub fn new(orientation: Orientation) -> Self { + Self { + signal: None, + orientation, + } + } + + pub fn signal(&self) -> Option<&Signal> { + self.signal.as_ref() + } +} + +impl Tile for Stacker { + fn update<'b>(&'b mut self, mut context: UpdateContext<'b>) { + if let Some(mut signal) = context.take_signal() { + if self.orientation.contains(signal.direction()) { + // Stack the stored signal on top of the incomming signal + if let Some(ref stored) = self.signal { + signal.append(stored); + } + + if let Some(target_position) = context.offset(signal.direction().into_offset()) { + let _ = context.send(target_position, signal.direction(), signal); + } + + if context.state() != State::Idle { + context.next_state(); + } + } else { + // Store the signal inside of ourselves + self.signal = Some(signal); + + context.set_state(State::Idle); + } + } else { + if context.state() != State::Idle { + context.next_state(); + } + } + } + + fn draw_simple(&self, ctx: DrawContext) -> TextChar { + match self.orientation { + Orientation::Vertical => TextChar::from_state('\u{03a6}', ctx.state), // GREEK CAPITAL LETTER PHI + Orientation::Horizontal => TextChar::from_state('\u{a74a}', ctx.state), // LATIN CAPITAL LETTER O WITH LONG STROKE + _ => TextChar::from_state('+', ctx.state), + } + } +} + /// Tries to convert a [`FullTile`] to a [`Store`] fn get_store<'a>(full: VecRef<'a, FullTile>) -> Option> { VecRef::try_map(full, |tile| { diff --git a/stackline/tiles/transmit.rs b/stackline/tiles/transmit.rs index b79fe4b..4733878 100644 --- a/stackline/tiles/transmit.rs +++ b/stackline/tiles/transmit.rs @@ -29,7 +29,7 @@ impl Tile for Teleporter { } fn draw_simple(&self, ctx: DrawContext) -> TextChar { - TextChar::from_state('P', ctx.state) + TextChar::from_state('T', ctx.state) } } diff --git a/stackline/tiles/wire.rs b/stackline/tiles/wire.rs index c01b5f9..d4f6ed8 100644 --- a/stackline/tiles/wire.rs +++ b/stackline/tiles/wire.rs @@ -59,6 +59,7 @@ impl Tile for Diode { if let Some(signal) = context.take_signal() { // Block signals coming from where the diode is looking if signal.direction().opposite() == self.0 { + context.set_state(State::Idle); return; } @@ -112,6 +113,11 @@ impl Tile for Resistor { } if let Some(signal) = context.take_signal() { + if signal.direction().opposite() == self.direction { + context.set_state(State::Idle); + return; + } + self.signal = Some(signal); context.set_state(State::Active); } else if context.state() != State::Idle {