If, Cmp, Stacker

main
Shad Amethyst 2 years ago
parent 0945010f0f
commit 21ae3bee01
Signed by: amethyst
GPG Key ID: D970C8DD1D6DEE36

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

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

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

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

File diff suppressed because one or more lines are too long

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

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

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

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

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

@ -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<Signal>,
/// 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<'a, Store>> {
VecRef::try_map(full, |tile| {

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

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

Loading…
Cancel
Save