|
|
|
//! 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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn schema(&self) -> TileSchema {
|
|
|
|
TileSchema::map()
|
|
|
|
.add("signal", TileSchema::value("Stored signal", "Signal"))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 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 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() {
|
|
|
|
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())?;
|
|
|
|
|
|
|
|
let _ = 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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn schema(&self) -> TileSchema {
|
|
|
|
TileSchema::value("Direction", "Direction")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[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, 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 Default for Stacker {
|
|
|
|
fn default() -> Self {
|
|
|
|
Stacker::new(Orientation::Horizontal)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn schema(&self) -> TileSchema {
|
|
|
|
TileSchema::map()
|
|
|
|
.add("signal", TileSchema::value("Signal to stack", "Signal"))
|
|
|
|
.add("orientation", TileSchema::value("Orientation", "Orientation"))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 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()
|
|
|
|
}
|