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
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));
|
|
}
|
|
}
|