From 6f6a7f978bfd0f63e27ff01445ea4daca2e4e071 Mon Sep 17 00:00:00 2001 From: hal8174 Date: Thu, 7 Dec 2023 14:01:31 +0100 Subject: [PATCH] Simple Path --- Cargo.lock | 162 ++++++++++++- Cargo.toml | 1 + examples/path.rs | 14 -- examples/solve_belt.rs | 39 +++ src/belt_finding/mod.rs | 270 +++++++++++---------- src/graph/wheighted_graph/shortest_path.rs | 8 +- 6 files changed, 333 insertions(+), 161 deletions(-) delete mode 100644 examples/path.rs create mode 100644 examples/solve_belt.rs diff --git a/Cargo.lock b/Cargo.lock index dee727d..798b908 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,7 +43,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" dependencies = [ - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -53,7 +53,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -62,6 +62,12 @@ version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + [[package]] name = "cfg-if" version = "1.0.0" @@ -114,6 +120,17 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "colored" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6" +dependencies = [ + "is-terminal", + "lazy_static", + "windows-sys 0.48.0", +] + [[package]] name = "crc32fast" version = "1.3.2" @@ -123,12 +140,23 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "factorio_blueprint" version = "0.1.0" dependencies = [ "base64", "clap", + "colored", "flate2", "serde", "serde_json", @@ -150,12 +178,47 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +dependencies = [ + "hermit-abi", + "rustix", + "windows-sys 0.48.0", +] + [[package]] name = "itoa" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" + +[[package]] +name = "linux-raw-sys" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" + [[package]] name = "miniz_oxide" version = "0.7.1" @@ -183,6 +246,19 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rustix" +version = "0.38.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9470c4bf8246c8daf25f9598dca807fb6510347b1e1cfa55749113850c79d88a" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + [[package]] name = "ryu" version = "1.0.15" @@ -255,7 +331,16 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", ] [[package]] @@ -264,13 +349,28 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", ] [[package]] @@ -279,38 +379,80 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" diff --git a/Cargo.toml b/Cargo.toml index 6e00d4a..a0c2c6d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" [dependencies] base64 = "0.21.5" clap = { version = "4.4.8", features = ["derive"] } +colored = "2.0.4" flate2 = "1.0.28" serde = { version = "1.0.192", features = ["derive"] } serde_json = "1.0.108" diff --git a/examples/path.rs b/examples/path.rs deleted file mode 100644 index cad4b17..0000000 --- a/examples/path.rs +++ /dev/null @@ -1,14 +0,0 @@ -use factorio_blueprint::{ - belt_finding::{find_path, Field, Position, QueueObject}, - misc::Map, - priority_queue::BinaryHeap, -}; - -fn main() { - let mut map: Map = Map::new(5, 5); - map.get_mut(2, 0).blocked = true; - map.get_mut(2, 1).blocked = true; - map.get_mut(2, 2).blocked = true; - - find_path::>(map, Position { x: 0, y: 0 }, Position { x: 4, y: 0 }); -} diff --git a/examples/solve_belt.rs b/examples/solve_belt.rs new file mode 100644 index 0000000..a6ea4ca --- /dev/null +++ b/examples/solve_belt.rs @@ -0,0 +1,39 @@ +use factorio_blueprint::belt_finding::{Position, Problem}; + +fn main() { + let mut p = Problem::new(17, 13, Position::new(3, 8), Position::new(13, 5)); + + p.set_blocked(0, 3, true); + + p.set_blocked(1, 4, true); + p.set_blocked(2, 4, true); + p.set_blocked(1, 5, true); + p.set_blocked(2, 5, true); + + p.set_blocked(1, 7, true); + p.set_blocked(2, 7, true); + p.set_blocked(1, 8, true); + p.set_blocked(2, 8, true); + + p.set_blocked(0, 9, true); + + p.set_blocked(16, 3, true); + + p.set_blocked(14, 4, true); + p.set_blocked(15, 4, true); + p.set_blocked(14, 5, true); + p.set_blocked(15, 5, true); + + p.set_blocked(14, 7, true); + p.set_blocked(15, 7, true); + p.set_blocked(14, 8, true); + p.set_blocked(15, 8, true); + + p.set_blocked(16, 9, true); + + println!("{}", p); + + p.find_path(); + + println!("{}", p); +} diff --git a/src/belt_finding/mod.rs b/src/belt_finding/mod.rs index 8e6d44c..1980f29 100644 --- a/src/belt_finding/mod.rs +++ b/src/belt_finding/mod.rs @@ -1,4 +1,10 @@ +use crate::graph::wheighted_graph::shortest_path::dijkstra; +use crate::graph::wheighted_graph::WheightedGraph; +use crate::priority_queue::BinaryHeap; use crate::{misc::Map, priority_queue::PriorityQueue}; +use colored::Colorize; +use std::fmt::{write, Display}; +use std::io::Write; #[derive(Clone, Copy)] pub enum Direction { @@ -8,163 +14,163 @@ pub enum Direction { Left, } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct Position { pub x: usize, pub y: usize, } +impl Position { + pub fn new(x: usize, y: usize) -> Self { + Self { x, y } + } +} #[derive(Default, Clone, Copy)] pub struct Field { pub blocked: bool, } -#[derive(Debug, Clone)] -pub struct QueueObject { - pos: Position, - score: usize, +pub struct Problem { + map: Map, + start: Position, + end: Position, + path: Vec, } -impl QueueObject { - fn new(pos: Position, score: usize) -> Self { - Self { pos, score } - } -} - -impl Eq for QueueObject {} - -impl Ord for QueueObject { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.score.cmp(&other.score) - } -} - -impl PartialOrd for QueueObject { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.score.cmp(&other.score)) - } -} - -impl PartialEq for QueueObject { - fn eq(&self, other: &Self) -> bool { - self.score == other.score - } -} - -struct FieldInternal { - score: usize, - marked: bool, - key: Option, - parent: Option, -} - -impl Default for FieldInternal { - fn default() -> Self { +impl Problem { + pub fn new(width: usize, height: usize, start: Position, end: Position) -> Self { Self { - score: usize::MAX, - marked: false, - key: None, - parent: None, + map: Map::new(width, height), + start, + end, + path: Vec::new(), } } + + pub fn set_blocked(&mut self, x: usize, y: usize, blocked: bool) { + self.map.get_mut(x, y).blocked = blocked; + } } -fn relax

( - internal_map: &mut Map>, - q: &mut P, - x: usize, - y: usize, - score: usize, - parent: Direction, -) where - P: PriorityQueue, -{ - let f = internal_map.get_mut(x, y); - if !f.marked { - if let Some(h) = &mut f.key { - if score < f.score { - f.score = score; - f.parent = Some(parent); - q.decrease_key(h, |o| o.score = score); +impl Display for Problem { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let width_digits = self.map.width.ilog10() + 1; + let height_digits = self.map.height.ilog10() + 1; + + // print header + for i in 0..width_digits { + let d = width_digits - i - 1; + // padding + for _ in 0..height_digits { + write!(f, " ")?; } + + for x in 0..self.map.width { + let digits = x / (usize::pow(10, d)); + if digits == 0 && d > 0 { + write!(f, " ")?; + } else { + write!(f, "{}", char::from_u32((digits % 10) as u32 + 48).unwrap())?; + } + } + + writeln!(f)?; + } + + // Print body + + for y in 0..self.map.height { + write!(f, "{:1$}", y, height_digits as usize)?; + + for x in 0..self.map.width { + if self.start == Position::new(x, y) { + write!(f, "{}", "s".blue())?; + } else if self.end == Position::new(x, y) { + write!(f, "{}", "t".blue())?; + } else if let Some(p) = self.path.iter().position(|p| p == &Position::new(x, y)) { + if self.path[p].x < self.path[p + 1].x { + write!(f, "{}", "→".blue())?; + } else if self.path[p].x > self.path[p + 1].x { + write!(f, "{}", "←".blue())?; + } else if self.path[p].y < self.path[p + 1].y { + write!(f, "{}", "↓".blue())?; + } else if self.path[p].y > self.path[p + 1].y { + write!(f, "{}", "↑".blue())?; + } + } else if self.map.get(x, y).blocked { + write!(f, "{}", "#")?; + } else { + write!(f, " ")?; + } + } + + writeln!(f)?; + } + + Ok(()) + } +} + +struct MapInternal<'a> { + map: &'a Map, +} + +impl<'a> MapInternal<'a> { + fn get_direction(&self, pos: Position) -> [Option; 4] { + [ + (pos.x > 0) + .then_some((pos.x.saturating_sub(1), pos.y)) + .map(|(x, y)| Position::new(x, y)), + (pos.x < self.map.width - 1) + .then_some((pos.x + 1, pos.y)) + .map(|(x, y)| Position::new(x, y)), + (pos.y > 0) + .then_some((pos.x, pos.y.saturating_sub(1))) + .map(|(x, y)| Position::new(x, y)), + (pos.y < self.map.height - 1) + .then_some((pos.x, pos.y + 1)) + .map(|(x, y)| Position::new(x, y)), + ] + } +} + +impl<'a> WheightedGraph for MapInternal<'a> { + type Node = Position; + + fn num_edges(&self, node: &Self::Node) -> usize { + if self.map.get(node.x, node.y).blocked { + 0 } else { - let key = q.insert(QueueObject::new(Position { x, y }, score)); - internal_map.set( - x, - y, - FieldInternal { - score, - marked: false, - key: Some(key), - parent: Some(parent), - }, - ) + let t = self.get_direction(*node); + + let v = t + .iter() + .flatten() + .filter_map(|&p| (!self.map.get(p.x, p.y).blocked).then_some(p)); + + v.count() } } + + fn edge(&self, node: &Self::Node, num: usize) -> Option<(Self::Node, f64)> { + let t = self.get_direction(*node); + + t.iter() + .flatten() + .filter_map(|&p| (!self.map.get(p.x, p.y).blocked).then_some(p)) + .nth(num) + .map(|p| (p, 1.0)) + } } -pub fn find_path

(map: Map, start: Position, end: Position) -> Option> -where - P: PriorityQueue + std::fmt::Debug, -{ - let mut q = P::new(); +impl Problem { + pub fn find_path(&mut self) { + let m = MapInternal { map: &self.map }; - let mut internal_map: Map> = Map::new(map.width, map.height); + let p = dijkstra::>(&m, self.start, self.end); - q.insert(QueueObject::new(start, 0)); - internal_map.get_mut(start.x, start.y).score = 0; - - while let Some(o) = q.pop_min() { - dbg!(&o); - if o.pos.x > 0 && !map.get(o.pos.x - 1, o.pos.y).blocked { - relax::

( - &mut internal_map, - &mut q, - o.pos.x - 1, - o.pos.y, - o.score + 1, - Direction::Right, - ); + if let Some(p) = p { + self.path = p; } - - if o.pos.x < map.width - 1 && !map.get(o.pos.x + 1, o.pos.y).blocked { - relax::

( - &mut internal_map, - &mut q, - o.pos.x + 1, - o.pos.y, - o.score + 1, - Direction::Left, - ); - } - - if o.pos.y > 0 && !map.get(o.pos.x, o.pos.y - 1).blocked { - relax::

( - &mut internal_map, - &mut q, - o.pos.x, - o.pos.y - 1, - o.score + 1, - Direction::Down, - ); - } - - if o.pos.y < map.height - 1 && !map.get(o.pos.x, o.pos.y + 1).blocked { - relax::

( - &mut internal_map, - &mut q, - o.pos.x, - o.pos.y + 1, - o.score + 1, - Direction::Up, - ); - } - // dbg!(&q); - if o.pos == end { - break; - } - internal_map.get_mut(o.pos.x, o.pos.y).marked = true; } - - None } diff --git a/src/graph/wheighted_graph/shortest_path.rs b/src/graph/wheighted_graph/shortest_path.rs index bde1aa6..61a353b 100644 --- a/src/graph/wheighted_graph/shortest_path.rs +++ b/src/graph/wheighted_graph/shortest_path.rs @@ -95,16 +95,14 @@ where map.insert(node, MapObject::new(o.node.clone(), o.score + wheight, h)); } } - dbg!(&q); + // dbg!(&q); } - if map.get(&end).is_none() { - return None; - } + map.get(&end)?; let mut result = vec![end]; - dbg!(&map); + // dbg!(&map); loop { let parent = map.get(result.last().expect("last")).expect("get");