Rename Portal to Teleporter, new Sender tile [WIP]

main
Shad Amethyst 2 years ago
parent 7175e65570
commit 941de10ea3
Signed by: amethyst
GPG Key ID: D970C8DD1D6DEE36

@ -91,10 +91,30 @@ fn main() {
res += "#[enum_dispatch]\n"; res += "#[enum_dispatch]\n";
res += "pub enum AnyTile {\n"; res += "pub enum AnyTile {\n";
for name in names { for name in names.iter() {
res += &format!(" {0}({0}),\n", name); res += &format!(" {0}({0}),\n", name);
} }
res += "}\n"; res += "}\n";
// impl<T: Tile> TryInto<&T> for &AnyTile
res += "\n";
for name in names {
res += &format!(
concat!(
"impl<'a> TryInto<&'a {0}> for &'a AnyTile {{\n",
" type Error = ();\n",
" fn try_into(self) -> Result<&'a {0}, Self::Error> {{\n",
" match self {{\n",
" AnyTile::{0}(tile) => Ok(tile),\n",
" _ => Err(()),\n",
" }}\n",
" }}\n",
"}}\n",
),
name
);
}
fs::write(dest_path.clone(), &res).expect(&format!("Couldn't write to {:?}", dest_path)); fs::write(dest_path.clone(), &res).expect(&format!("Couldn't write to {:?}", dest_path));
} }

@ -7,6 +7,8 @@ This library is the rust implementation of the core logic of the language.
*/ */
#![feature(drain_filter)]
#[macro_use] #[macro_use]
extern crate contracts; extern crate contracts;

@ -7,6 +7,8 @@ pub struct Pane {
width: NonZeroUsize, width: NonZeroUsize,
height: NonZeroUsize, height: NonZeroUsize,
position: (i32, i32),
pub(crate) signals: Vec<(usize, usize)>, pub(crate) signals: Vec<(usize, usize)>,
} }
@ -49,6 +51,8 @@ impl Pane {
height: height.try_into().ok()?, height: height.try_into().ok()?,
tiles, tiles,
position: (0, 0),
signals: Vec::new(), signals: Vec::new(),
}) })
} }
@ -83,6 +87,19 @@ impl Pane {
self.height self.height
} }
/// Returns the position of the `Pane` in its [`World`].
/// This property is used for drawing the `Pane` and calculating the distances between cross-pane tiles.
#[inline]
pub fn position(&self) -> (i32, i32) {
self.position
}
/// Sets the position of the `Pane` in its [`World`].
#[inline]
pub fn set_position(&mut self, position: (i32, i32)) {
self.position = position;
}
/// Given a `position = (x, y)` and an `offset = (Δx, Δy)`, /// Given a `position = (x, y)` and an `offset = (Δx, Δy)`,
/// returns `Some((x + Δx, y + Δy))` if `(x + Δx, y + Δy)` is inside the `Pane`. /// returns `Some((x + Δx, y + Δy))` if `(x + Δx, y + Δy)` is inside the `Pane`.
/// ///
@ -378,10 +395,12 @@ impl Pane {
.filter_map(move |(i, v)| Some((i % self.width, i / self.width, v))) .filter_map(move |(i, v)| Some((i % self.width, i / self.width, v)))
} }
/// Draws the Pane at `(dx + self.position.0, dy + self.position.1)` on a [`TextSurface`].
/// Empty tiles will leave the `TextSurface` untouched, but tiles are free to modify the characters around them.
pub fn draw(&self, dx: isize, dy: isize, surface: &mut TextSurface) { pub fn draw(&self, dx: isize, dy: isize, surface: &mut TextSurface) {
for (x, y, tile) in self.tiles() { for (x, y, tile) in self.tiles() {
let x = x as isize + dx; let x = x as isize + dx + self.position.0 as isize;
let y = y as isize + dy; let y = y as isize + dy + self.position.1 as isize;
if x >= 0 && y >= 0 { if x >= 0 && y >= 0 {
tile.draw(x as usize, y as usize, surface); tile.draw(x as usize, y as usize, surface);
@ -468,4 +487,38 @@ mod test {
} }
} }
} }
#[test]
fn test_pane_draw_offset() {
use crate::tile::Wire;
use Orientation::*;
let mut pane = test_tile_setup!(
2,
2,
[
Wire::new(Horizontal),
Wire::new(Vertical),
Wire::new(Any),
()
]
);
pane.set_position((2, 1));
let mut surface = TextSurface::new(9, 9);
pane.draw(5, 3, &mut surface);
assert_eq!(surface.get(5 + 2, 3 + 1).unwrap().ch, '-');
assert_eq!(surface.get(5 + 3, 3 + 1).unwrap().ch, '|');
assert_eq!(surface.get(5 + 2, 3 + 2).unwrap().ch, '+');
for y in 0..9 {
for x in 0..9 {
if (x, y) != (5 + 2, 3 + 1) && (x, y) != (5 + 3, 3 + 1) && (x, y) != (5 + 2, 3 + 2) {
assert_eq!(surface.get(x, y), Some(TextChar::default()));
}
}
}
}
} }

@ -117,3 +117,6 @@ impl From<()> for FullTile {
Self::new(None) Self::new(None)
} }
} }
// TODO: enum for <AnyTile as TryInto<T: Tile>>
// TODO: local trait for conversion to inner tiles

@ -4,19 +4,19 @@ use crate::prelude::*;
/// Instantly sends any incomming signals to `coordinates` /// Instantly sends any incomming signals to `coordinates`
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Portal { pub struct Teleporter {
pub coordinates: (String, usize, usize), pub coordinates: (String, usize, usize),
} }
impl Portal { impl Teleporter {
pub fn new(name: String, x: usize, y: usize) -> Self { pub fn new(name: String, x: usize, y: usize) -> Self {
Self { Self {
coordinates: (name, x, y) coordinates: (name, x, y),
} }
} }
} }
impl Tile for Portal { impl Tile for Teleporter {
fn update<'b>(&'b mut self, mut context: UpdateContext<'b>) { fn update<'b>(&'b mut self, mut context: UpdateContext<'b>) {
if let Some(signal) = context.take_signal() { if let Some(signal) = context.take_signal() {
context.send_outbound(self.coordinates.clone(), signal); context.send_outbound(self.coordinates.clone(), signal);
@ -32,12 +32,115 @@ impl Tile for Portal {
} }
} }
/// 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)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use veccell::VecRef;
#[test] #[test]
fn test_portal_transmit_same_pane() { fn test_teleporter_transmit_same_pane() {
use crate::{Diode, Wire}; use crate::{Diode, Wire};
use Direction::*; use Direction::*;
use Orientation::*; use Orientation::*;
@ -46,9 +149,15 @@ mod test {
3, 3,
3, 3,
[ [
Diode::new(Right), Portal::new(String::from("main"), 2, 2), (), Diode::new(Right),
(), (), (), Teleporter::new(String::from("main"), 2, 2),
(), (), Wire::new(Any) (),
(),
(),
(),
(),
(),
Wire::new(Any)
] ]
); );
@ -74,7 +183,7 @@ mod test {
} }
#[test] #[test]
fn test_portal_transmit_other_pane() { fn test_teleporter_transmit_other_pane() {
use crate::{Diode, Wire}; use crate::{Diode, Wire};
use Direction::*; use Direction::*;
use Orientation::*; use Orientation::*;
@ -83,19 +192,14 @@ mod test {
2, 2,
1, 1,
[ [
Diode::new(Right), Portal::new(String::from("sub"), 0, 0), Diode::new(Right),
Teleporter::new(String::from("sub"), 0, 0),
] ]
); );
test_set_signal!(main_pane, (0, 0), Right); test_set_signal!(main_pane, (0, 0), Right);
let sub_pane = test_tile_setup!( let sub_pane = test_tile_setup!(1, 1, [Wire::new(Any),]);
1,
1,
[
Wire::new(Any),
]
);
let mut world = World::new(); let mut world = World::new();
world.set_pane(String::from("main"), main_pane); world.set_pane(String::from("main"), main_pane);
@ -118,15 +222,33 @@ mod test {
} }
#[test] #[test]
fn test_portal_transmit_self() { fn test_teleporter_transmit_self() {
use Direction::*; 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!( let mut main_pane = test_tile_setup!(
1, 1,
1, 3,
[ [Sender::new(String::from("main"), 0, 2), (), Wire::new(Any)]
Portal::new(String::from("main"), 0, 0),
]
); );
test_set_signal!(main_pane, (0, 0), Right); test_set_signal!(main_pane, (0, 0), Right);
@ -134,10 +256,55 @@ mod test {
let mut world = World::new(); let mut world = World::new();
world.set_pane(String::from("main"), main_pane); world.set_pane(String::from("main"), main_pane);
for _ in 0..5 {
world.step(); world.step();
assert_signal!(world.get_pane("main").unwrap(), (0, 0)); 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::<Sender>(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));
} }
} }

Loading…
Cancel
Save