From 8f163269bde436553ae0e0266cb6a9236a4e520d Mon Sep 17 00:00:00 2001 From: hal8174 Date: Sun, 2 Mar 2025 22:51:25 +0100 Subject: [PATCH] Refactor graph and misc --- Cargo.lock | 8 +++ Cargo.toml | 2 +- factorio-core/src/lib.rs | 1 + .../src/misc/arena.rs | 0 .../src/misc/map.rs | 20 ++++++ .../src/misc/mod.rs | 0 factorio-core/src/position.rs | 11 ++++ factorio-core/src/visualize/mod.rs | 8 +++ factorio-graph/Cargo.toml | 7 ++ .../graph/mod.rs => factorio-graph/src/lib.rs | 1 + .../src/priority_queue/binary_heap.rs | 0 .../src/priority_queue/fibonacci_heap.rs | 0 .../src/priority_queue/mod.rs | 0 .../src}/wheighted_graph/adjacency_list.rs | 0 .../src}/wheighted_graph/mod.rs | 1 + .../src}/wheighted_graph/shortest_path.rs | 64 ++++++++++--------- .../src/wheighted_graph/steiner_tree.rs | 17 +++++ factorio-pathfinding/Cargo.toml | 1 + .../src/belt_finding/brute_force.rs | 2 +- .../src/belt_finding/conflict_avoidance.rs | 7 +- factorio-pathfinding/src/belt_finding/mod.rs | 18 ++++-- factorio-pathfinding/src/lib.rs | 3 - 22 files changed, 126 insertions(+), 45 deletions(-) rename {factorio-pathfinding => factorio-core}/src/misc/arena.rs (100%) rename {factorio-pathfinding => factorio-core}/src/misc/map.rs (82%) rename {factorio-pathfinding => factorio-core}/src/misc/mod.rs (100%) create mode 100644 factorio-graph/Cargo.toml rename factorio-pathfinding/src/graph/mod.rs => factorio-graph/src/lib.rs (51%) rename {factorio-pathfinding => factorio-graph}/src/priority_queue/binary_heap.rs (100%) rename {factorio-pathfinding => factorio-graph}/src/priority_queue/fibonacci_heap.rs (100%) rename {factorio-pathfinding => factorio-graph}/src/priority_queue/mod.rs (100%) rename {factorio-pathfinding/src/graph => factorio-graph/src}/wheighted_graph/adjacency_list.rs (100%) rename {factorio-pathfinding/src/graph => factorio-graph/src}/wheighted_graph/mod.rs (97%) rename {factorio-pathfinding/src/graph => factorio-graph/src}/wheighted_graph/shortest_path.rs (78%) create mode 100644 factorio-graph/src/wheighted_graph/steiner_tree.rs diff --git a/Cargo.lock b/Cargo.lock index 91b1153..06d5d5e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -581,6 +581,13 @@ dependencies = [ "termcolor", ] +[[package]] +name = "factorio-graph" +version = "0.1.0" +dependencies = [ + "tracing", +] + [[package]] name = "factorio-layout" version = "0.1.0" @@ -608,6 +615,7 @@ dependencies = [ "criterion", "factorio-blueprint", "factorio-core", + "factorio-graph", "flate2", "image", "miette", diff --git a/Cargo.toml b/Cargo.toml index a96e4f5..6608029 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = [ "factorio-blueprint", "factorio-blueprint-generator", "factorio-core", "factorio-layout", +members = [ "factorio-blueprint", "factorio-blueprint-generator", "factorio-core", "factorio-graph", "factorio-layout", "factorio-pathfinding" ] resolver = "2" diff --git a/factorio-core/src/lib.rs b/factorio-core/src/lib.rs index 6f37aff..f3d1cff 100644 --- a/factorio-core/src/lib.rs +++ b/factorio-core/src/lib.rs @@ -3,6 +3,7 @@ pub mod beltoptions; pub mod block; pub mod color; pub mod direction; +pub mod misc; pub mod pathfield; pub mod position; pub mod quaterdirection; diff --git a/factorio-pathfinding/src/misc/arena.rs b/factorio-core/src/misc/arena.rs similarity index 100% rename from factorio-pathfinding/src/misc/arena.rs rename to factorio-core/src/misc/arena.rs diff --git a/factorio-pathfinding/src/misc/map.rs b/factorio-core/src/misc/map.rs similarity index 82% rename from factorio-pathfinding/src/misc/map.rs rename to factorio-core/src/misc/map.rs index 18c8db1..d87a2cd 100644 --- a/factorio-pathfinding/src/misc/map.rs +++ b/factorio-core/src/misc/map.rs @@ -23,7 +23,27 @@ where data, } } +} +impl Map +where + T: Clone, +{ + pub fn new_with(width: usize, height: usize, value: T) -> Self { + let mut data = Vec::new(); + for _ in 0..(width * height) { + data.push(value.clone()); + } + + Self { + width, + height, + data, + } + } +} + +impl Map { fn index(&self, x: usize, y: usize) -> usize { x + y * self.width } diff --git a/factorio-pathfinding/src/misc/mod.rs b/factorio-core/src/misc/mod.rs similarity index 100% rename from factorio-pathfinding/src/misc/mod.rs rename to factorio-core/src/misc/mod.rs diff --git a/factorio-core/src/position.rs b/factorio-core/src/position.rs index 5ac6e22..14b029b 100644 --- a/factorio-core/src/position.rs +++ b/factorio-core/src/position.rs @@ -91,3 +91,14 @@ impl From<(PositionType, PositionType)> for Position { Self::new(value.0, value.1) } } + +impl std::ops::Div for Position { + type Output = Position; + + fn div(self, rhs: PositionType) -> Self::Output { + Self { + x: self.x / rhs, + y: self.y / rhs, + } + } +} diff --git a/factorio-core/src/visualize/mod.rs b/factorio-core/src/visualize/mod.rs index 2da8099..7b8025d 100644 --- a/factorio-core/src/visualize/mod.rs +++ b/factorio-core/src/visualize/mod.rs @@ -85,6 +85,14 @@ impl Color { Self::new(255, 255, 255) } + pub fn cyan() -> Self { + Self::new(0x0f, 0xf0, 0xfc) + } + + pub fn gray(l: u8) -> Self { + Self::new(l, l, l) + } + pub fn index(i: usize) -> Self { let c = [ Color::new(0xe6, 0x00, 0x49), diff --git a/factorio-graph/Cargo.toml b/factorio-graph/Cargo.toml new file mode 100644 index 0000000..c0be480 --- /dev/null +++ b/factorio-graph/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "factorio-graph" +version = "0.1.0" +edition = "2024" + +[dependencies] +tracing = "0.1.41" diff --git a/factorio-pathfinding/src/graph/mod.rs b/factorio-graph/src/lib.rs similarity index 51% rename from factorio-pathfinding/src/graph/mod.rs rename to factorio-graph/src/lib.rs index 48cb554..77d31fb 100644 --- a/factorio-pathfinding/src/graph/mod.rs +++ b/factorio-graph/src/lib.rs @@ -1 +1,2 @@ +pub mod priority_queue; pub mod wheighted_graph; diff --git a/factorio-pathfinding/src/priority_queue/binary_heap.rs b/factorio-graph/src/priority_queue/binary_heap.rs similarity index 100% rename from factorio-pathfinding/src/priority_queue/binary_heap.rs rename to factorio-graph/src/priority_queue/binary_heap.rs diff --git a/factorio-pathfinding/src/priority_queue/fibonacci_heap.rs b/factorio-graph/src/priority_queue/fibonacci_heap.rs similarity index 100% rename from factorio-pathfinding/src/priority_queue/fibonacci_heap.rs rename to factorio-graph/src/priority_queue/fibonacci_heap.rs diff --git a/factorio-pathfinding/src/priority_queue/mod.rs b/factorio-graph/src/priority_queue/mod.rs similarity index 100% rename from factorio-pathfinding/src/priority_queue/mod.rs rename to factorio-graph/src/priority_queue/mod.rs diff --git a/factorio-pathfinding/src/graph/wheighted_graph/adjacency_list.rs b/factorio-graph/src/wheighted_graph/adjacency_list.rs similarity index 100% rename from factorio-pathfinding/src/graph/wheighted_graph/adjacency_list.rs rename to factorio-graph/src/wheighted_graph/adjacency_list.rs diff --git a/factorio-pathfinding/src/graph/wheighted_graph/mod.rs b/factorio-graph/src/wheighted_graph/mod.rs similarity index 97% rename from factorio-pathfinding/src/graph/wheighted_graph/mod.rs rename to factorio-graph/src/wheighted_graph/mod.rs index bfe5975..d43e847 100644 --- a/factorio-pathfinding/src/graph/wheighted_graph/mod.rs +++ b/factorio-graph/src/wheighted_graph/mod.rs @@ -1,5 +1,6 @@ pub mod adjacency_list; pub mod shortest_path; +pub mod steiner_tree; pub trait WheightedGraph: Sized { type Node; diff --git a/factorio-pathfinding/src/graph/wheighted_graph/shortest_path.rs b/factorio-graph/src/wheighted_graph/shortest_path.rs similarity index 78% rename from factorio-pathfinding/src/graph/wheighted_graph/shortest_path.rs rename to factorio-graph/src/wheighted_graph/shortest_path.rs index c5cf266..58c7093 100644 --- a/factorio-pathfinding/src/graph/wheighted_graph/shortest_path.rs +++ b/factorio-graph/src/wheighted_graph/shortest_path.rs @@ -56,15 +56,16 @@ impl MapObject { } } -pub fn dijkstra(graph: &G, start: G::Node, end: G::Node) -> Option> +pub fn dijkstra(graph: &G, start: G::Node, end: E) -> Option> where P: PriorityQueue> + Debug, P::Handle: Debug, G::Node: Eq + Hash + Clone + Debug, G: WheightedGraph, + E: Fn(&'_ G::Node) -> bool, { let span = trace_span!("graph", seen_nodes = Empty, visited_nodes = Empty).entered(); - if start == end { + if end(&start) { return Some(vec![start]); } @@ -75,13 +76,15 @@ where let mut visited_nodes: usize = 1; + let mut end_node = None; while let Some(o) = q.pop_min() { visited_nodes += 1; if let Some(m) = map.get_mut(&o.node) { m.key = None; } - if o.node == end { + if end(&o.node) { + end_node = Some(o.node); break; } @@ -106,23 +109,27 @@ where span.record("seen_nodes", map.len()); span.record("visited_nodes", visited_nodes); - map.get(&end)?; + if let Some(end_node) = end_node { + map.get(&end_node)?; - let mut result = vec![end]; + let mut result = vec![end_node]; - // dbg!(&map); + // dbg!(&map); - loop { - let parent = map.get(result.last().expect("last")).expect("get"); - result.push(parent.parent.clone()); - if parent.parent == start { - break; + loop { + let parent = map.get(result.last().expect("last")).expect("get"); + result.push(parent.parent.clone()); + if parent.parent == start { + break; + } } + + result.reverse(); + + Some(result) + } else { + None } - - result.reverse(); - - Some(result) } struct GraphWrapper<'a, G, F> { @@ -141,27 +148,28 @@ impl f64> WheightedGraph for GraphWrapper< } } -pub fn a_star(graph: &G, start: G::Node, end: G::Node, dist: F) -> Option> +pub fn a_star(graph: &G, start: G::Node, end: E, dist: F) -> Option> where P: PriorityQueue> + Debug, P::Handle: Debug, G::Node: Eq + Hash + Clone + Debug, G: WheightedGraph, + E: Fn(&'_ G::Node) -> bool, F: Fn(&G::Node) -> f64, { let g = GraphWrapper { g: graph, f: dist }; - dijkstra::, P>(&g, start, end) + dijkstra::, P, E>(&g, start, end) } #[cfg(test)] mod test { use crate::{ - graph::wheighted_graph::{ - adjacency_list::WheightedAdjacencyList, shortest_path::dijkstra, WheightedGraph, - }, priority_queue::{binary_heap::BinaryHeap, fibonacci_heap::FibonacciHeap}, + wheighted_graph::{ + WheightedGraph, adjacency_list::WheightedAdjacencyList, shortest_path::dijkstra, + }, }; #[test] @@ -172,7 +180,7 @@ mod test { }; assert_eq!( - dijkstra::>(&a, 0, 1), + dijkstra::, _>(&a, 0, |&n| n == 1), Some(vec![0, 1]) ); } @@ -197,19 +205,19 @@ mod test { }; assert_eq!( - dijkstra::>(&a, 0, 4), + dijkstra::, _>(&a, 0, |&n| n == 4), Some(vec![0, 1, 2, 5, 4]) ); assert_eq!( - dijkstra::>(&a, 0, 6), + dijkstra::, _>(&a, 0, |&n| n == 6), None ); assert_eq!( - dijkstra::>(&a, 0, 4), + dijkstra::, _>(&a, 0, |&n| n == 4), Some(vec![0, 1, 2, 5, 4]) ); assert_eq!( - dijkstra::>(&a, 0, 6), + dijkstra::, _>(&a, 0, |&n| n == 6), None ); } @@ -264,10 +272,6 @@ mod test { height: 600, }; - dbg!(dijkstra::>( - &g, - (0, 0), - (g.width - 1, g.height - 1) - )); + dbg!(dijkstra::, _>(&g, (0, 0), |&n| n == (g.width - 1, g.height - 1))); } } diff --git a/factorio-graph/src/wheighted_graph/steiner_tree.rs b/factorio-graph/src/wheighted_graph/steiner_tree.rs new file mode 100644 index 0000000..f365f52 --- /dev/null +++ b/factorio-graph/src/wheighted_graph/steiner_tree.rs @@ -0,0 +1,17 @@ +use crate::priority_queue::PriorityQueue; +use std::hash::Hash; +use std::{collections::HashSet, fmt::Debug}; + +use super::{WheightedGraph, shortest_path::QueueObject}; + +pub fn takaheshi_matsuyama(graph: G, nodes: &[G::Node]) -> Option>> +where + P: PriorityQueue> + Debug, + P::Handle: Debug, + G::Node: Eq + Hash + Clone, + G: WheightedGraph, +{ + let end_nodes: HashSet = HashSet::from_iter(nodes.iter().cloned()); + + todo!() +} diff --git a/factorio-pathfinding/Cargo.toml b/factorio-pathfinding/Cargo.toml index 7d0af0c..f8b736a 100644 --- a/factorio-pathfinding/Cargo.toml +++ b/factorio-pathfinding/Cargo.toml @@ -15,6 +15,7 @@ harness = false [dependencies] factorio-core = { path = "../factorio-core" } +factorio-graph = { path = "../factorio-graph" } factorio-blueprint = { path = "../factorio-blueprint" } base64 = "0.22.1" bon = "3.0.2" diff --git a/factorio-pathfinding/src/belt_finding/brute_force.rs b/factorio-pathfinding/src/belt_finding/brute_force.rs index 581800a..2324332 100644 --- a/factorio-pathfinding/src/belt_finding/brute_force.rs +++ b/factorio-pathfinding/src/belt_finding/brute_force.rs @@ -1,7 +1,7 @@ use factorio_core::{pathfield::PathField, prelude::*, visualize::Visualize}; use std::time::Instant; -use crate::misc::Map; +use factorio_core::misc::Map; #[derive(Default, Debug, Clone)] pub struct BruteforceField { diff --git a/factorio-pathfinding/src/belt_finding/conflict_avoidance.rs b/factorio-pathfinding/src/belt_finding/conflict_avoidance.rs index 8e3284c..7489e7a 100644 --- a/factorio-pathfinding/src/belt_finding/conflict_avoidance.rs +++ b/factorio-pathfinding/src/belt_finding/conflict_avoidance.rs @@ -1,5 +1,6 @@ -use crate::{belt_finding::brute_force::BruteforceBuilder, misc::Map}; -use factorio_blueprint::{belt::convert_to_blueprint, BlueprintEntity}; +use crate::belt_finding::brute_force::BruteforceBuilder; +use factorio_blueprint::{BlueprintEntity, belt::convert_to_blueprint}; +use factorio_core::misc::Map; use factorio_core::{ beltoptions::Beltspeed, pathfield::PathField, prelude::*, visualize::Visualize, }; @@ -7,7 +8,7 @@ use std::{ ops::RangeInclusive, time::{Duration, Instant}, }; -use tracing::{span, Level}; +use tracing::{Level, span}; use super::Problem; diff --git a/factorio-pathfinding/src/belt_finding/mod.rs b/factorio-pathfinding/src/belt_finding/mod.rs index 186ad3c..8000d50 100644 --- a/factorio-pathfinding/src/belt_finding/mod.rs +++ b/factorio-pathfinding/src/belt_finding/mod.rs @@ -3,11 +3,11 @@ use crate::Map as _; use crate::SinglePathInput; use crate::SinglePathfinder; use crate::examples::HashMapMap; -use crate::graph::wheighted_graph::WheightedGraph; -use crate::graph::wheighted_graph::shortest_path::a_star; -use crate::misc::Map; -use crate::priority_queue::binary_heap::FastBinaryHeap; +use factorio_core::misc::Map; use factorio_core::{prelude::*, visualize::Visualize}; +use factorio_graph::priority_queue::binary_heap::FastBinaryHeap; +use factorio_graph::wheighted_graph::WheightedGraph; +use factorio_graph::wheighted_graph::shortest_path::a_star; use serde::{Deserialize, Serialize}; use tracing::Level; use tracing::span; @@ -222,7 +222,11 @@ struct MapInternal<'a> { impl WheightedGraph for MapInternal<'_> { type Node = (Position, Direction); - fn edge(&self, node: &Self::Node, num: usize) -> Option<(Self::Node, f64)> { + fn edge( + &self, + node: &(Position, Direction), + num: usize, + ) -> Option<((Position, Direction), f64)> { let next = node.0.in_direction(&node.1, 1); next.in_range( &Position::new(0, 0), @@ -276,10 +280,10 @@ impl Problem { }; let p = { // dijkstra::>(&m, self.start[i], self.end[i]) - a_star::, _>( + a_star::, _, _>( &m, self.start[i], - self.end[i], + |&n| n == self.end[i], |&(p, _)| { 1.5 * (PositionType::abs_diff(p.x, self.end[i].0.x) + PositionType::abs_diff(p.y, self.end[i].0.y)) diff --git a/factorio-pathfinding/src/lib.rs b/factorio-pathfinding/src/lib.rs index 80af563..33024c2 100644 --- a/factorio-pathfinding/src/lib.rs +++ b/factorio-pathfinding/src/lib.rs @@ -2,9 +2,6 @@ use factorio_core::{beltoptions::Beltspeed, pathfield::PathField, prelude::*}; pub mod belt_finding; pub mod examples; -pub mod graph; -pub mod misc; -pub mod priority_queue; pub struct PathInput<'c, M> { pub connections: &'c [Connection],