//! 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, } 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::(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 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 } } } #[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| { let tile = tile.get().ok_or(())?; let store = tile.try_into().map_err(|_| ())?; Ok::<&Store, ()>(store) }).ok() }