From 236c97d2bcc449c6efd5a439df46fdad33efe388 Mon Sep 17 00:00:00 2001 From: Adrien Burgun Date: Fri, 8 Jul 2022 13:34:08 +0200 Subject: [PATCH] :bug: :sparkles: CI to nightly, Sender::calculate_path --- .github/workflows/rustdoc.yaml | 2 +- stackline/Cargo.toml | 1 + stackline/build.rs | 15 ++++ stackline/src/world.rs | 17 ++++- stackline/tiles/transmit.rs | 123 +++++++++++++++++++++++++++++++-- 5 files changed, 152 insertions(+), 6 deletions(-) diff --git a/.github/workflows/rustdoc.yaml b/.github/workflows/rustdoc.yaml index 669386b..26ad227 100644 --- a/.github/workflows/rustdoc.yaml +++ b/.github/workflows/rustdoc.yaml @@ -21,7 +21,7 @@ jobs: - name: Install Rust toolchain uses: actions-rs/toolchain@v1 with: - toolchain: stable + toolchain: nightly profile: minimal override: true components: rustfmt, rust-src diff --git a/stackline/Cargo.toml b/stackline/Cargo.toml index c0c4f6e..1cf1658 100755 --- a/stackline/Cargo.toml +++ b/stackline/Cargo.toml @@ -11,6 +11,7 @@ palette = "0.6" enum_dispatch = "0.3" contracts = { version = "0.6.3", features = ["override_debug"] } veccell = "0.2" +pathfinding = "3.0" [dev-dependencies] colored = "2.0" diff --git a/stackline/build.rs b/stackline/build.rs index afa3fdc..0e0861b 100644 --- a/stackline/build.rs +++ b/stackline/build.rs @@ -114,6 +114,21 @@ fn main() { ), name ); + + res += &format!( + concat!( + "impl<'a> TryInto<&'a mut {0}> for &'a mut AnyTile {{\n", + " type Error = ();\n", + " fn try_into(self) -> Result<&'a mut {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)); diff --git a/stackline/src/world.rs b/stackline/src/world.rs index 7989b89..91b7d82 100644 --- a/stackline/src/world.rs +++ b/stackline/src/world.rs @@ -15,7 +15,7 @@ impl World { pub fn step(&mut self) { let mut outbound_signals = Vec::new(); - for (_, pane) in self.panes.iter_mut() { + for pane in self.panes.values_mut() { let mut res = pane.step(); outbound_signals.append(&mut res.outbound_signals); } @@ -38,6 +38,21 @@ impl World { pub fn get_pane_mut<'b>(&'b mut self, name: &str) -> Option<&'b mut Pane> { self.panes.get_mut(name) } + + pub fn in_pane(&self, x: i32, y: i32) -> bool { + for pane in self.panes.values() { + if + x >= pane.position().0 + && y >= pane.position().1 + && x < pane.position().0 + pane.width().get() as i32 + && y < pane.position().1 + pane.height().get() as i32 + { + return true; + } + } + + false + } } #[cfg(test)] diff --git a/stackline/tiles/transmit.rs b/stackline/tiles/transmit.rs index ac1d492..c5e37cd 100644 --- a/stackline/tiles/transmit.rs +++ b/stackline/tiles/transmit.rs @@ -1,6 +1,7 @@ //! Transmission tiles: allow for inter-Pane communication use crate::prelude::*; +// use crate::tile::prelude::*; /// Instantly sends any incomming signals to `coordinates` #[derive(Clone, Debug)] @@ -52,9 +53,81 @@ impl Sender { } // TODO: implement WorldMask, calculate_path and a method of Tile to call this method automatically - // pub fn calculate_path(&mut self, mask: &WorldMask) { - // - // } + pub fn calculate_path(&mut self, origin: (i32, i32), world: &World) { + use pathfinding::directed::astar::astar; + + // A* search + #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + struct Pos(i32, i32); + + impl Pos { + fn neighbors(&self, world: &World) -> [(Pos, i32); 4] { + let x = self.0; + let y = self.1; + + [ + Pos(x + 1, y).with_weight(world), + Pos(x - 1, y).with_weight(world), + Pos(x, y + 1).with_weight(world), + Pos(x, y - 1).with_weight(world), + ] + } + + fn with_weight(self, world: &World) -> (Self, i32) { + if world.in_pane(self.0, self.1) { + (self, 100) + } else { + (self, 1) + } + } + + fn heuristic(&self, target: Pos) -> i32 { + (self.0 - target.0).abs() + (self.1 - target.1).abs() + } + } + + impl From for (i32, i32) { + fn from(pos: Pos) -> (i32, i32) { + (pos.0, pos.1) + } + } + + impl From<&Pos> for (i32, i32) { + fn from(pos: &Pos) -> (i32, i32) { + (pos.0, pos.1) + } + } + + if let Some(pane) = world.get_pane(&self.coordinates.0) { + let target = Pos( + pane.position().0 + self.coordinates.1 as i32, + pane.position().1 + self.coordinates.2 as i32, + ); + + if let Some((best_path, _)) = astar( + &Pos(origin.0, origin.1), + |node| node.neighbors(world), + |node| node.heuristic(target), + |&node| node == target, + ) { + self.path = Vec::new(); + self.path.push((best_path[0].0, best_path[0].1)); + + for (prev, current) in best_path.iter().zip(best_path.iter().skip(1)).skip(1) { + // If self.path.last(), prev, current aren't aligned, push prev to self.path + if self.path[self.path.len() - 1].0 != prev.0 && prev.0 == current.0 { + self.path.push(prev.into()); + } + } + + if self.path[self.path.len() - 1] != best_path[best_path.len() - 1].into() { + self.path.push(best_path[best_path.len() - 1].into()); + } + + self.length = best_path.len() - 1; + } + } + } } impl Tile for Sender { @@ -137,7 +210,7 @@ impl Tile for Sender { #[cfg(test)] mod test { use super::*; - use veccell::VecRef; + use veccell::{VecRef, VecRefMut}; #[test] fn test_teleporter_transmit_same_pane() { @@ -307,4 +380,46 @@ mod test { assert_no_signal!(world.get_pane("main").unwrap(), (0, 0)); assert_signal!(world.get_pane("main").unwrap(), (0, 2)); } + + #[test] + fn test_sender_pathfinding() { + use crate::Wire; + + let mut main_pane = test_tile_setup!(1, 1, [Sender::new(String::from("second"), 0, 0),]); + + main_pane.set_position((0, 0)); + + let mut second_pane = test_tile_setup!(1, 1, [Wire::new(Orientation::Any)]); + + second_pane.set_position((2, 0)); + + let mut world = World::new(); + world.set_pane(String::from("main"), main_pane); + world.set_pane(String::from("second"), second_pane); + + // TODO: borrow_mut_as::(coords) + let mut tile: VecRefMut = VecRefMut::map( + world.get_pane("main").unwrap().borrow_mut((0, 0)).unwrap(), + |tile| tile.get_mut().unwrap().try_into().unwrap(), + ); + + tile.calculate_path((0, 0), &world); + + assert_eq!(tile.path, [(0, 0), (2, 0)]); + assert_eq!(tile.length, 2); + + drop(tile); + + world.get_pane_mut("second").unwrap().set_position((2, 2)); + + let mut tile: VecRefMut = VecRefMut::map( + world.get_pane("main").unwrap().borrow_mut((0, 0)).unwrap(), + |tile| tile.get_mut().unwrap().try_into().unwrap(), + ); + + tile.calculate_path((0, 0), &world); + + assert!(tile.path == [(0, 0), (2, 0), (2, 2)] || tile.path == [(0, 0), (0, 2), (2, 2)]); + assert_eq!(tile.length, 4); + } }