Remove RefCell and Resistor tile

main
Shad Amethyst 2 years ago
parent 9b63640e06
commit 842508226e
Signed by: amethyst
GPG Key ID: D970C8DD1D6DEE36

@ -1,8 +1,12 @@
use super::*; use super::*;
use std::cell::{Ref, RefMut};
/** Provides an interface between a [`Tile`] and its parent [`Pane`] during [`Tile::update`]. /** Provides an interface between a [`Tile`] and its parent [`Pane`] during [`Tile::update`].
All actions performed through `UpdateContext` will be executed *after* all the tiles have updated. All actions performed through `UpdateContext` will be executed *after* all the tiles have updated.
## Safety
Because [`Tile::update`] requires a `&mut self` reference, the current [`Tile`] cannot be accessed through [`UpdateContext::get`]
This structure stores the state and signal of the [`FullTile`] containing the current tile, and it is still possible and safe to call [`UpdateContext::send`] on the current position.
**/ **/
pub struct UpdateContext<'a> { pub struct UpdateContext<'a> {
position: (usize, usize), position: (usize, usize),
@ -12,31 +16,32 @@ pub struct UpdateContext<'a> {
commit: &'a mut UpdateCommit commit: &'a mut UpdateCommit
} }
// SAFETY: self.pane.tiles[self.position] may not be accessed from any method
impl<'a> UpdateContext<'a> { impl<'a> UpdateContext<'a> {
/// Returns `None` if the tile was already updated or is empty /// Returns `None` if the tile was already updated or is empty
pub(crate) fn new(pane: &'a Pane, position: (usize, usize), commit: &'a mut UpdateCommit) -> Option<(Self, RefMut<'a, AnyTile>)> { pub(crate) fn new(pane: &'a mut Pane, position: (usize, usize), commit: &'a mut UpdateCommit) -> Option<(UpdateContext<'a>, &'a mut AnyTile)> {
let mut guard = pane.get_mut(position)?; let mut tile = pane.get_mut(position)?;
if guard.updated { if tile.updated {
return None return None
} }
guard.updated = true; // prevent duplicate updates tile.updated = true; // prevent duplicate updates
commit.updates.push(position); commit.updates.push(position);
let (tile, signal, state) = guard.get_raw_mut(); let ptr: *mut AnyTile = &mut **(tile.get_mut().as_mut()?);
if tile.is_none() {
return None
}
let res = Self { let res = Self {
position, position,
state: tile.state(),
signal: tile.take_signal(),
pane, pane,
state: *state,
signal: std::mem::take(signal),
commit commit
}; };
Some((res, RefMut::map(guard, |tile| tile.get_mut().unwrap_or_else(|| unreachable!())))) // SAFETY: ptr is a valid pointer
// SAFETY: aliasing is prevented by the invariants of UpdateContext
Some((res, unsafe {
&mut *ptr
}))
} }
/// Returns the position of the currently updated tile. /// Returns the position of the currently updated tile.
@ -81,10 +86,14 @@ impl<'a> UpdateContext<'a> {
} }
/// Returns an immutable reference to the [FullTile] at `pos` in the current [Pane]. /// Returns an immutable reference to the [FullTile] at `pos` in the current [Pane].
/// Returns `None` if the tile is borrowed mutably or if it does not exist. /// Returns `None` if the tile is borrowed mutably, if it is the current tile or if it does not exist.
#[inline] #[inline]
pub fn get<'b>(&'b self, pos: (usize, usize)) -> Option<Ref<'b, FullTile>> where 'a: 'b { pub fn get<'b>(&'b self, pos: (usize, usize)) -> Option<&'b FullTile> where 'a: 'b {
self.pane.get(pos) if self.position == pos {
None
} else {
self.pane.get(pos)
}
} }
/// Returns `Some((position.x + Δx, position.y + Δy))` iff `(x + Δx, y + Δy)` is inside the pane /// Returns `Some((position.x + Δx, position.y + Δy))` iff `(x + Δx, y + Δy)` is inside the pane
@ -95,7 +104,7 @@ impl<'a> UpdateContext<'a> {
/// Shortcut for calling both `ctx.offset(offset)` and `ctx.get(pos)` /// Shortcut for calling both `ctx.offset(offset)` and `ctx.get(pos)`
#[inline] #[inline]
pub fn get_offset<'b>(&'b self, offset: (i8, i8)) -> Option<((usize, usize), Ref<'b, FullTile>)> where 'a: 'b { pub fn get_offset<'b>(&'b self, offset: (i8, i8)) -> Option<((usize, usize), &'b FullTile)> where 'a: 'b {
self.offset(offset).and_then(|pos| self.get(pos).map(|tile| (pos, tile))) self.offset(offset).and_then(|pos| self.get(pos).map(|tile| (pos, tile)))
} }
@ -103,7 +112,7 @@ impl<'a> UpdateContext<'a> {
/// If the tile does not exist, then this function will return `false`. /// If the tile does not exist, then this function will return `false`.
#[inline] #[inline]
pub fn accepts_signal(&self, pos: (usize, usize), direction: Direction) -> bool { pub fn accepts_signal(&self, pos: (usize, usize), direction: Direction) -> bool {
match self.pane.get(pos) { match self.get(pos) {
Some(tile) => tile.accepts_signal(direction), Some(tile) => tile.accepts_signal(direction),
None => false None => false
} }
@ -151,19 +160,19 @@ impl UpdateCommit {
pub(crate) fn apply(self, pane: &mut Pane) { pub(crate) fn apply(self, pane: &mut Pane) {
for (x, y) in self.updates { for (x, y) in self.updates {
if let Some(mut tile) = pane.get_mut((x, y)) { if let Some(tile) = pane.get_mut((x, y)) {
tile.updated = false; tile.updated = false;
} }
} }
for (x, y, state) in self.states { for (x, y, state) in self.states {
if let Some(mut tile) = pane.get_mut((x, y)) { if let Some(tile) = pane.get_mut((x, y)) {
tile.set_state(state); tile.set_state(state);
} }
} }
for (x, y, signal) in self.signals { for (x, y, signal) in self.signals {
let push_signal = if let Some(mut tile) = pane.get_mut((x, y)) { let push_signal = if let Some(tile) = pane.get_mut((x, y)) {
tile.set_signal(signal); tile.set_signal(signal);
tile.set_state(State::Active); tile.set_state(State::Active);
// For some reason std::mem::drop(tile) isn't enough here // For some reason std::mem::drop(tile) isn't enough here

@ -1,9 +1,8 @@
use super::*; use super::*;
use std::cell::{RefCell, Ref, RefMut};
#[derive(Debug)] #[derive(Debug)]
pub struct Pane { pub struct Pane {
tiles: Vec<RefCell<FullTile>>, tiles: Vec<FullTile>,
width: NonZeroUsize, width: NonZeroUsize,
height: NonZeroUsize, height: NonZeroUsize,
@ -18,7 +17,7 @@ impl Pane {
Some(Self { Some(Self {
width: width.try_into().ok()?, width: width.try_into().ok()?,
height: height.try_into().ok()?, height: height.try_into().ok()?,
tiles: vec![RefCell::new(FullTile::default()); length], tiles: vec![FullTile::default(); length],
signals: Vec::new(), signals: Vec::new(),
}) })
@ -48,21 +47,21 @@ impl Pane {
// TODO: Have a Result instead of an Option // TODO: Have a Result instead of an Option
#[inline] #[inline]
pub fn get<'b>(&'b self, position: (usize, usize)) -> Option<Ref<'b, FullTile>> { pub fn get<'b>(&'b self, position: (usize, usize)) -> Option<&'b FullTile> {
if !self.in_bounds(position) { if !self.in_bounds(position) {
return None; return None;
} }
self.tiles.get(position.1 * self.width.get() + position.0).map(|tile| tile.try_borrow().ok()).flatten() self.tiles.get(position.1 * self.width.get() + position.0)
} }
#[inline] #[inline]
pub fn get_mut<'b>(&'b self, position: (usize, usize)) -> Option<RefMut<'b, FullTile>> { pub fn get_mut<'b>(&'b mut self, position: (usize, usize)) -> Option<&'b mut FullTile> {
if !self.in_bounds(position) { if !self.in_bounds(position) {
return None; return None;
} }
self.tiles.get(position.1 * self.width.get() + position.0).map(|tile| tile.try_borrow_mut().ok()).flatten() self.tiles.get_mut(position.1 * self.width.get() + position.0)
} }
#[inline] #[inline]
@ -89,7 +88,7 @@ impl Pane {
#[inline] #[inline]
fn update(&mut self, position: (usize, usize), commit: &mut UpdateCommit) -> Option<()> { fn update(&mut self, position: (usize, usize), commit: &mut UpdateCommit) -> Option<()> {
let (ctx, mut tile) = UpdateContext::new(self, position, commit)?; let (ctx, tile) = UpdateContext::new(self, position, commit)?;
tile.update(ctx); tile.update(ctx);
@ -117,7 +116,7 @@ impl Pane {
/// Returns an iterator over the tiles and their coordinates /// Returns an iterator over the tiles and their coordinates
#[inline] #[inline]
pub fn tiles<'b>(&'b self) -> impl Iterator<Item=(usize, usize, &RefCell<FullTile>)> + 'b { pub fn tiles<'b>(&'b self) -> impl Iterator<Item=(usize, usize, &FullTile)> + 'b {
self.tiles.iter().enumerate().filter_map(move |(i, v)| { self.tiles.iter().enumerate().filter_map(move |(i, v)| {
Some((i % self.width, i / self.width, v)) Some((i % self.width, i / self.width, v))
}) })

@ -14,16 +14,17 @@ impl Signal {
} }
} }
pub fn clone_move(&self, direction: Direction) -> Option<Self> { pub fn clone_move(&self, direction: Direction) -> Self {
let mut res = self.clone(); let mut res = self.clone();
res.direction = direction; res.direction = direction;
let (dx, dy) = direction.into_offset(); res
}
res.position.0 = (res.position.0 as isize + dx as isize).try_into().ok()?; pub fn moved(mut self, direction: Direction) -> Self {
res.position.1 = (res.position.1 as isize + dy as isize).try_into().ok()?; self.direction = direction;
Some(res) self
} }
pub fn direction(&self) -> Direction { pub fn direction(&self) -> Direction {

@ -164,7 +164,7 @@ mod crate_macros {
( $width:expr, $height:expr, [ $( $x:expr ),* ] ) => {{ ( $width:expr, $height:expr, [ $( $x:expr ),* ] ) => {{
assert!($width > 0); assert!($width > 0);
assert!($height > 0); assert!($height > 0);
let pane = Pane::empty($width, $height).unwrap(); let mut pane = Pane::empty($width, $height).unwrap();
let mut index = 0; let mut index = 0;
$( $(

@ -21,7 +21,7 @@ impl Tile for Wire {
if let Some(pos) = context.offset(direction.into_offset()) { if let Some(pos) = context.offset(direction.into_offset()) {
if context.accepts_signal(pos, direction) { if context.accepts_signal(pos, direction) {
context.send(pos, signal.clone_move(direction).unwrap_or_else(|| unreachable!())); context.send(pos, signal.clone_move(direction));
} }
} }
} }
@ -56,7 +56,7 @@ impl Tile for Diode {
if let Some(pos) = context.offset(self.0.into_offset()) { if let Some(pos) = context.offset(self.0.into_offset()) {
if context.accepts_signal(pos, self.0) { if context.accepts_signal(pos, self.0) {
context.send(pos, signal.clone_move(self.0).unwrap_or_else(|| unreachable!())); context.send(pos, signal.moved(self.0));
} }
} }
} }
@ -67,6 +67,42 @@ impl Tile for Diode {
} }
} }
#[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()) {
if context.accepts_signal(pos, self.direction) {
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();
}
}
}
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
@ -101,7 +137,7 @@ mod test {
pane.step(); pane.step();
for (_, _, tile) in pane.tiles() { for (_, _, tile) in pane.tiles() {
assert!(tile.borrow().signal().is_none()); assert!(tile.signal().is_none());
} }
// Let the simulation cool down // Let the simulation cool down
@ -161,4 +197,51 @@ mod test {
} }
} }
} }
#[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));
}
} }

Loading…
Cancel
Save