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.

294 lines
7.6 KiB

//! Wires and diodes
// use super::*;
use crate::prelude::*;
use crate::tile::prelude::*;
#[derive(Clone, Debug)]
pub struct Wire(Orientation);
impl Wire {
pub fn new(orientation: Orientation) -> Self {
Self(orientation)
}
}
impl Tile for Wire {
fn update<'b>(&'b mut self, mut context: UpdateContext<'b>) {
if let Some(signal) = context.take_signal() {
for &direction in self.0.into_directions() {
if direction == signal.direction().opposite() {
continue;
}
if let Some(pos) = context.accepts_direction(direction) {
context.force_send(pos, signal.clone_move(direction));
}
}
}
if context.state() != State::Idle {
context.next_state();
}
}
fn accepts_signal(&self, direction: Direction) -> bool {
self.0.contains(direction)
}
fn draw(&self, x: usize, y: usize, state: State, surface: &mut TextSurface) {
let ch = match self.0 {
Orientation::Horizontal => '-',
Orientation::Vertical => '|',
Orientation::Any => '+',
};
surface.set(x, y, TextChar::from_state(ch, state));
}
}
#[derive(Clone, Debug)]
pub struct Diode(Direction);
impl Diode {
pub fn new(direction: Direction) -> Self {
Self(direction)
}
}
impl Tile for Diode {
fn update<'b>(&'b mut self, mut context: UpdateContext<'b>) {
if let Some(signal) = context.take_signal() {
// Block signals coming from where the diode is looking
if signal.direction().opposite() == self.0 {
return;
}
if let Some(pos) = context.offset(self.0.into_offset()) {
context.send(pos, signal.moved(self.0));
}
}
if context.state() != State::Idle {
context.next_state();
}
}
fn draw(&self, x: usize, y: usize, state: State, surface: &mut TextSurface) {
let ch = match self.0 {
Direction::Up => '^',
Direction::Down => 'v',
Direction::Left => '<',
Direction::Right => '>',
};
surface.set(x, y, TextChar::from_state(ch, state));
}
}
#[derive(Clone, Debug)]
pub struct Resistor {
direction: Direction,
signal: Option<Signal>,
}
impl Resistor {
pub fn new(direction: Direction) -> Self {
Self {
direction,
signal: None,
}
}
}
impl Tile for Resistor {
fn update<'b>(&'b mut self, mut context: UpdateContext<'b>) {
if let Some(signal) = std::mem::take(&mut self.signal) {
if let Some(pos) = context.offset(self.direction.into_offset()) {
context.send(pos, signal.moved(self.direction));
}
}
if let Some(signal) = context.take_signal() {
self.signal = Some(signal);
context.set_state(State::Active);
} else {
if context.state() != State::Idle {
context.next_state();
}
}
}
fn draw(&self, x: usize, y: usize, state: State, surface: &mut TextSurface) {
let ch = match self.direction {
Direction::Up => '\u{219f}', // Upwards Two Headed Arrow
Direction::Down => '\u{21a1}', // Downwards Two Headed Arrow
Direction::Left => '\u{219e}', // Leftwards Two Headed Arrow
Direction::Right => '\u{21a0}', // Rightwards Two Headed Arrow
};
surface.set(x, y, TextChar::from_state(ch, state));
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_wire_transmit() {
use crate::Orientation::*;
let mut pane = test_tile_setup!(
3,
2,
[
Wire::new(Horizontal),
Wire::new(Any),
Wire::new(Horizontal),
(),
Wire::new(Vertical),
()
]
);
// Test the signal going from left to right
test_set_signal!(pane, (0, 0), Direction::Right);
pane.step();
println!("{:#?}", pane);
assert_signal!(pane, (1, 0));
assert_no_signal!(pane, (0, 0));
assert_no_signal!(pane, (2, 0));
assert_no_signal!(pane, (1, 1));
pane.step();
assert_signal!(pane, (2, 0));
assert_signal!(pane, (1, 1));
assert_no_signal!(pane, (0, 0));
assert_no_signal!(pane, (1, 0));
pane.step();
for (_, _, tile) in pane.tiles() {
assert!(tile.signal().is_none());
}
// Let the simulation cool down
pane.step();
pane.step();
// Test the signal going from right to left
test_set_signal!(pane, (2, 0), Direction::Left);
pane.step();
assert_signal!(pane, (1, 0));
assert_no_signal!(pane, (0, 0));
assert_no_signal!(pane, (2, 0));
assert_no_signal!(pane, (1, 1));
pane.step();
assert_signal!(pane, (0, 0));
assert_signal!(pane, (1, 1));
assert_no_signal!(pane, (2, 0));
assert_no_signal!(pane, (1, 0));
}
#[test]
fn test_diode_transmit() {
use crate::Direction::*;
let mut pane = test_tile_setup!(
3,
2,
[
Diode::new(Right),
Diode::new(Right),
Diode::new(Down),
(),
Diode::new(Up),
Diode::new(Left)
]
);
// Test the signal going from left to right
test_set_signal!(pane, (0, 0), Direction::Right);
pane.step();
assert_signal!(pane, (1, 0));
assert_no_signal!(pane, (0, 0));
let positions = [(2, 0), (2, 1), (1, 1), (1, 0)];
for &pos in positions.iter().cycle().take(16) {
pane.step();
println!("{:#?}", pane);
assert_signal!(pane, pos);
for &pos2 in positions.iter() {
if pos == pos2 {
continue;
}
assert_no_signal!(pane, pos2);
}
}
}
#[test]
fn test_resistor_transmit() {
use crate::Direction::*;
let mut pane = test_tile_setup!(
4,
1,
[
Diode::new(Right),
Resistor::new(Right),
Resistor::new(Right),
Diode::new(Right)
]
);
test_set_signal!(pane, (0, 0), Direction::Right);
pane.step();
assert_no_signal!(pane, (0, 0));
assert_signal!(pane, (1, 0));
assert_no_signal!(pane, (2, 0));
assert_no_signal!(pane, (3, 0));
pane.step();
assert_no_signal!(pane, (0, 0));
assert_no_signal!(pane, (1, 0));
assert_no_signal!(pane, (2, 0));
assert_no_signal!(pane, (3, 0));
pane.step();
assert_no_signal!(pane, (0, 0));
assert_no_signal!(pane, (1, 0));
assert_signal!(pane, (2, 0));
assert_no_signal!(pane, (3, 0));
pane.step();
assert_no_signal!(pane, (0, 0));
assert_no_signal!(pane, (1, 0));
assert_no_signal!(pane, (2, 0));
assert_no_signal!(pane, (3, 0));
pane.step();
assert_no_signal!(pane, (0, 0));
assert_no_signal!(pane, (1, 0));
assert_no_signal!(pane, (2, 0));
assert_signal!(pane, (3, 0));
pane.step();
assert_no_signal!(pane, (0, 0));
assert_no_signal!(pane, (1, 0));
assert_no_signal!(pane, (2, 0));
assert_no_signal!(pane, (3, 0));
}
}