parent
399883d8ba
commit
8654d25c65
@ -0,0 +1,39 @@
|
||||
#[allow(unused_imports)]
|
||||
use stackline::prelude::*;
|
||||
use stackline::signal;
|
||||
mod common;
|
||||
|
||||
#[test]
|
||||
fn test_reader() {
|
||||
let mut world = load_test!("tests/storage/reader.json");
|
||||
world.init();
|
||||
|
||||
world.set_signal((0, 0), signal!(Direction::Down, [0]));
|
||||
|
||||
run!(world, 5);
|
||||
assert_stored!(world, 0, 1, signal!([0]));
|
||||
assert_no_stored!(world, 2, 0);
|
||||
|
||||
world.set_signal((1, 2), signal!(Direction::Up, [-1]));
|
||||
run!(world, 5);
|
||||
assert_stored!(world, 2, 0, signal!([0]));
|
||||
|
||||
world.set_signal((0, 0), signal!(Direction::Down, [1]));
|
||||
|
||||
run!(world, 5);
|
||||
assert_stored!(world, 0, 1, signal!([1]));
|
||||
assert_stored!(world, 2, 0, signal!([0]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_counter() {
|
||||
let mut world = load_test!("tests/storage/counter.json");
|
||||
world.init();
|
||||
|
||||
for n in 1..10 {
|
||||
world.set_signal((1, 3), signal!(Direction::Up));
|
||||
println!("{}", world);
|
||||
run!(world, 11);
|
||||
assert_stored!(world, 0, 2, signal!([n]));
|
||||
}
|
||||
}
|
@ -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":{"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":null,"signal":null,"state":"Idle","updated":false},{"cell":{"Store":{"signal":{"direction":"Up","stack":[{"Number":0.0}]}}},"signal":null,"state":"Idle","updated":false},{"cell":{"Reader":"Right"},"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":null,"signal":null,"state":"Idle","updated":false},{"cell":null,"signal":null,"state":"Idle","updated":false}],"width":4,"height":4,"position":[0,0]}},"blink_speed":{"secs":0,"nanos":250000000}}
|
@ -0,0 +1 @@
|
||||
{"panes":{"main":{"tiles":[{"cell":{"Diode":"Down"},"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},{"cell":{"Reader":"Right"},"signal":null,"state":"Idle","updated":false},{"cell":{"Diode":"Up"},"signal":null,"state":"Idle","updated":false},{"cell":null,"signal":null,"state":"Idle","updated":false},{"cell":{"Diode":"Up"},"signal":null,"state":"Idle","updated":false},{"cell":null,"signal":null,"state":"Idle","updated":false}],"width":3,"height":3,"position":[0,0]}},"blink_speed":{"secs":0,"nanos":250000000}}
|
@ -0,0 +1,147 @@
|
||||
//! Arithmetic operations: add, subtract, etc.
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::tile::prelude::*;
|
||||
|
||||
macro_rules! binary_op {
|
||||
() => {
|
||||
fn update<'b>(&'b mut self, mut context: UpdateContext<'b>) {
|
||||
if let Some(mut signal) = context.take_signal() {
|
||||
if signal.len() >= 2 {
|
||||
let first = signal.pop().unwrap_or_else(|| unreachable!());
|
||||
let second = signal.pop().unwrap_or_else(|| unreachable!());
|
||||
|
||||
if let Some(res) = self.binary_op(second, first) {
|
||||
signal.push(res);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(coords) = context.offset(signal.direction().into_offset()) {
|
||||
context.send(coords, signal.direction(), signal).unwrap_or_else(|_| unreachable!());
|
||||
}
|
||||
}
|
||||
|
||||
if context.state() != State::Idle {
|
||||
context.next_state();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds two values together: `[..., a, b] -> [..., a+b]`
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct Add;
|
||||
|
||||
impl Add {
|
||||
fn binary_op(&self, left: Value, right: Value) -> Option<Value> {
|
||||
Some(left + right)
|
||||
}
|
||||
}
|
||||
|
||||
impl Tile for Add {
|
||||
binary_op!();
|
||||
|
||||
fn draw_simple(&self, ctx: DrawContext) -> TextChar {
|
||||
TextChar::from_state('\u{2295}', ctx.state) // CIRCLED PLUS
|
||||
}
|
||||
}
|
||||
|
||||
/// Subtracts two values: `[..., a, b] -> [..., a-b]`
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct Sub;
|
||||
|
||||
impl Sub {
|
||||
fn binary_op(&self, left: Value, right: Value) -> Option<Value> {
|
||||
use Value::*;
|
||||
|
||||
match (left, right) {
|
||||
(Number(x), Number(y)) => Some(Number(x - y)),
|
||||
(String(mut x), Number(y)) => {
|
||||
x.truncate(y as usize);
|
||||
Some(String(x))
|
||||
}
|
||||
(String(x), String(y)) => {
|
||||
Some(String(x.split(&y).collect()))
|
||||
}
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Tile for Sub {
|
||||
binary_op!();
|
||||
|
||||
fn draw_simple(&self, ctx: DrawContext) -> TextChar {
|
||||
TextChar::from_state('\u{2296}', ctx.state) // CIRCLED MINUS
|
||||
}
|
||||
}
|
||||
|
||||
/// Multiplies two values together: `[..., a, b] -> [..., a*b]`
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct Mul;
|
||||
|
||||
impl Mul {
|
||||
fn binary_op(&self, left: Value, right: Value) -> Option<Value> {
|
||||
use Value::*;
|
||||
|
||||
match (left, right) {
|
||||
(Number(x), Number(y)) => Some(Number(x * y)),
|
||||
(String(x), Number(y)) => Some(String(x.repeat(y as usize))),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Tile for Mul {
|
||||
binary_op!();
|
||||
|
||||
fn draw_simple(&self, ctx: DrawContext) -> TextChar {
|
||||
TextChar::from_state('\u{2297}', ctx.state) // CIRCLED TIMES
|
||||
}
|
||||
}
|
||||
|
||||
/// Divides two values together: `[..., a, b] -> [..., a/b]`
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct Div;
|
||||
|
||||
impl Div {
|
||||
fn binary_op(&self, left: Value, right: Value) -> Option<Value> {
|
||||
use Value::*;
|
||||
|
||||
match (left, right) {
|
||||
(Number(x), Number(y)) => Some(Number(x / y)),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Tile for Div {
|
||||
binary_op!();
|
||||
|
||||
fn draw_simple(&self, ctx: DrawContext) -> TextChar {
|
||||
TextChar::from_state('\u{2298}', ctx.state) // CIRCLED DIVISION SLASH
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes the modulo of two values: `[..., a, b] -> [..., a%b]`
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct Mod;
|
||||
|
||||
impl Mod {
|
||||
fn binary_op(&self, left: Value, right: Value) -> Option<Value> {
|
||||
use Value::*;
|
||||
|
||||
match (left, right) {
|
||||
(Number(x), Number(y)) => Some(Number(x % y)),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Tile for Mod {
|
||||
binary_op!();
|
||||
|
||||
fn draw_simple(&self, ctx: DrawContext) -> TextChar {
|
||||
TextChar::from_state('\u{2299}', ctx.state) // CIRCLED DOT OPERATOR
|
||||
}
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
//! Arithmetic operations: add, subtract, etc.
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::tile::prelude::*;
|
||||
|
||||
use veccell::VecRef;
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
pub struct Store {
|
||||
signal: Option<Signal>,
|
||||
}
|
||||
|
||||
impl Store {
|
||||
pub fn signal(&self) -> Option<&Signal> {
|
||||
self.signal.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl Tile for Store {
|
||||
fn update<'b>(&'b mut self, mut context: UpdateContext<'b>) {
|
||||
if let Some(signal) = context.take_signal() {
|
||||
let position = context.position();
|
||||
|
||||
// NOTE: we *could* write the signal immediately,
|
||||
// but by delaying the write we can read from a `Store` without being order-dependent
|
||||
context.callback(move |pane| {
|
||||
if let Some(mut this) = pane.borrow_mut_as::<Self>(position) {
|
||||
this.signal = Some(signal);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if context.state() != State::Idle {
|
||||
context.next_state();
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_simple(&self, ctx: DrawContext) -> TextChar {
|
||||
if self.signal.is_some() {
|
||||
TextChar::from_state('\u{25c9}', ctx.state) // FISHEYE
|
||||
} else {
|
||||
TextChar::from_state('\u{25cb}', ctx.state) // WHITE CIRCLE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// When a signal is received, reads a signal from a [`Store`] (at the reader's tail),
|
||||
/// then outputs that signal (at the reader's head).
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// The following circuit can receive a value in `B`, then store it indefinitely.
|
||||
/// Sending a signal in `A` will output the stored signal from `B` in `OUT`.
|
||||
///
|
||||
/// ```text
|
||||
/// o = Store
|
||||
/// ▸ = Reader(Right)
|
||||
///
|
||||
/// (A) ---+
|
||||
/// |
|
||||
/// (B) --o▸-- (OUT)
|
||||
/// ```
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
pub struct Reader(Direction);
|
||||
|
||||
impl Tile for Reader {
|
||||
fn update<'b>(&'b mut self, mut context: UpdateContext<'b>) {
|
||||
if let Some(_signal) = context.take_signal() {
|
||||
let _: Option<()> = try {
|
||||
let store_position = context.offset(self.0.opposite().into_offset())?;
|
||||
let store = context.get(store_position).and_then(get_store)?;
|
||||
let signal = store.signal.clone()?;
|
||||
drop(store);
|
||||
|
||||
let target_position = context.offset(self.0.into_offset())?;
|
||||
|
||||
context.send(target_position, self.0, signal);
|
||||
};
|
||||
}
|
||||
|
||||
if context.state() != State::Idle {
|
||||
context.next_state();
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_simple(&self, ctx: DrawContext) -> TextChar {
|
||||
match self.0 {
|
||||
Direction::Down => TextChar::from_state('\u{25bd}', ctx.state), // WHITE DOWN-POINTING TRIANGLE
|
||||
Direction::Left => TextChar::from_state('\u{25c1}', ctx.state), // WHITE LEFT-POINTING TRIANGLE
|
||||
Direction::Right => TextChar::from_state('\u{25b7}', ctx.state), // WHITE RIGHT-POINTING TRIANGLE
|
||||
Direction::Up => TextChar::from_state('\u{25b3}', ctx.state), // WHITE UP-POINTING TRIANGLE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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| {
|
||||
let tile = tile.get().ok_or(())?;
|
||||
let store = tile.try_into().map_err(|_| ())?;
|
||||
Ok::<&Store, ()>(store)
|
||||
}).ok()
|
||||
}
|
Loading…
Reference in new issue