//! Transmission tiles: allow for inter-Pane communication use crate::prelude::*; /// Instantly sends any incomming signals to `coordinates` #[derive(Clone, Debug)] pub struct Teleporter { pub coordinates: (String, usize, usize), } impl Teleporter { pub fn new(name: String, x: usize, y: usize) -> Self { Self { coordinates: (name, x, y), } } } impl Tile for Teleporter { fn update<'b>(&'b mut self, mut context: UpdateContext<'b>) { if let Some(signal) = context.take_signal() { context.send_outbound(self.coordinates.clone(), signal); } if context.state() != State::Idle { context.next_state(); } } fn draw(&self, x: usize, y: usize, state: State, surface: &mut TextSurface) { surface.set(x, y, TextChar::from_state('P', state)); } } /// Sends a signal through a virtual wire towards `coordinates`. #[derive(Clone, Debug)] pub struct Sender { pub coordinates: (String, usize, usize), pub path: Vec<(i32, i32)>, // x, y pub length: usize, pub signals: Vec<(Signal, usize)>, } impl Sender { pub fn new(name: String, x: usize, y: usize) -> Self { Self { coordinates: (name, x, y), path: Vec::new(), length: 0, signals: Vec::new(), } } // TODO: implement WorldMask, calculate_path and a method of Tile to call this method automatically // pub fn calculate_path(&mut self, mask: &WorldMask) { // // } } impl Tile for Sender { fn update<'b>(&'b mut self, mut context: UpdateContext<'b>) { let mut needs_sending = false; for (_signal, ref mut time) in self.signals.iter_mut() { *time += 1; if *time >= self.length { needs_sending = true; } } if let Some(signal) = context.take_signal() { self.signals.push((signal, 0)); if self.length == 0 { needs_sending = true; } } if needs_sending { for (signal, _time) in self.signals.drain_filter(|(_, time)| *time >= self.length) { context.send_outbound(self.coordinates.clone(), signal); } } if context.state() == State::Active { context.next_state(); } else if context.state() == State::Dormant && self.signals.len() == 0 { context.next_state(); } } // TODO: read self.signals to determine the state of each char // TODO: automated test fn draw(&self, x: usize, y: usize, _state: State, surface: &mut TextSurface) { for (prev, next) in self.path.iter().zip(self.path.iter().skip(1)) { if prev.0 != next.0 { // Draw the diode of the corner let ch = if next.0 > prev.0 { '>' } else { '<' }; surface.set( (x as i32 + prev.0) as usize, (y as i32 + prev.1) as usize, TextChar::from_state(ch, State::Idle), ); // Draw the horizontal line for dx in (prev.0 + 1)..(next.0) { surface.set( (x as i32 + dx) as usize, (y as i32 + prev.1) as usize, TextChar::from_state('-', State::Idle), ); } } else { // Draw the diode of the corner let ch = if next.1 > prev.1 { 'v' } else { '^' }; surface.set( (x as i32 + prev.0) as usize, (y as i32 + prev.1) as usize, TextChar::from_state(ch, State::Idle), ); // Draw the vertical line for dy in (prev.1 + 1)..(next.1) { surface.set( (y as i32 + prev.0) as usize, (y as i32 + dy) as usize, TextChar::from_state('|', State::Idle), ); } } } } } #[cfg(test)] mod test { use super::*; use veccell::VecRef; #[test] fn test_teleporter_transmit_same_pane() { use crate::{Diode, Wire}; use Direction::*; use Orientation::*; let mut main_pane = test_tile_setup!( 3, 3, [ Diode::new(Right), Teleporter::new(String::from("main"), 2, 2), (), (), (), (), (), (), Wire::new(Any) ] ); test_set_signal!(main_pane, (0, 0), Right); let mut world = World::new(); world.set_pane(String::from("main"), main_pane); world.step(); assert_signal!(world.get_pane("main").unwrap(), (1, 0)); world.step(); assert_no_signal!(world.get_pane("main").unwrap(), (1, 0)); assert_signal!(world.get_pane("main").unwrap(), (2, 2)); world.step(); assert_no_signal!(world.get_pane("main").unwrap(), (0, 0)); assert_no_signal!(world.get_pane("main").unwrap(), (1, 0)); assert_no_signal!(world.get_pane("main").unwrap(), (2, 2)); } #[test] fn test_teleporter_transmit_other_pane() { use crate::{Diode, Wire}; use Direction::*; use Orientation::*; let mut main_pane = test_tile_setup!( 2, 1, [ Diode::new(Right), Teleporter::new(String::from("sub"), 0, 0), ] ); test_set_signal!(main_pane, (0, 0), Right); let sub_pane = test_tile_setup!(1, 1, [Wire::new(Any),]); let mut world = World::new(); world.set_pane(String::from("main"), main_pane); world.set_pane(String::from("sub"), sub_pane); world.step(); assert_signal!(world.get_pane("main").unwrap(), (1, 0)); world.step(); assert_no_signal!(world.get_pane("main").unwrap(), (1, 0)); assert_signal!(world.get_pane("sub").unwrap(), (0, 0)); world.step(); assert_no_signal!(world.get_pane("main").unwrap(), (0, 0)); assert_no_signal!(world.get_pane("main").unwrap(), (1, 0)); assert_no_signal!(world.get_pane("sub").unwrap(), (0, 0)); } #[test] fn test_teleporter_transmit_self() { use Direction::*; let mut main_pane = test_tile_setup!(1, 1, [Teleporter::new(String::from("main"), 0, 0),]); test_set_signal!(main_pane, (0, 0), Right); let mut world = World::new(); world.set_pane(String::from("main"), main_pane); for _ in 0..5 { world.step(); assert_signal!(world.get_pane("main").unwrap(), (0, 0)); } } #[test] fn test_sender_instantaneous() { use crate::Wire; use Direction::*; use Orientation::*; let mut main_pane = test_tile_setup!( 1, 3, [Sender::new(String::from("main"), 0, 2), (), Wire::new(Any)] ); test_set_signal!(main_pane, (0, 0), Right); let mut world = World::new(); world.set_pane(String::from("main"), main_pane); world.step(); assert_signal!(world.get_pane("main").unwrap(), (0, 2)); } #[test] fn test_sender_delay() { use crate::Wire; use Direction::*; use Orientation::*; let mut sender = Sender::new(String::from("main"), 0, 2); sender.length = 2; let mut main_pane = test_tile_setup!(1, 3, [sender, (), Wire::new(Any)]); test_set_signal!(main_pane, (0, 0), Right); let mut world = World::new(); world.set_pane(String::from("main"), main_pane); for n in 0..2 { world.step(); // TODO: pane.get_as::(coords) let sender: VecRef<'_, Sender> = VecRef::map( world.get_pane("main").unwrap().get((0, 0)).unwrap(), |tile| tile.get().unwrap().try_into().unwrap(), ); assert!(sender.signals.len() == 1); assert!(sender.signals[0].1 == n); drop(sender); assert_no_signal!(world.get_pane("main").unwrap(), (0, 0)); assert_no_signal!(world.get_pane("main").unwrap(), (0, 2)); } world.step(); let sender: VecRef<'_, Sender> = VecRef::map( world.get_pane("main").unwrap().get((0, 0)).unwrap(), |tile| tile.get().unwrap().try_into().unwrap(), ); assert!(sender.signals.len() == 0); assert_no_signal!(world.get_pane("main").unwrap(), (0, 0)); assert_signal!(world.get_pane("main").unwrap(), (0, 2)); } }