You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

206 lines
6.3 KiB

//! 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 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, 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),
}
}
}
/// 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()
}