diff --git a/factorio-blueprint-generator/src/bin/balancer_blueprint.rs b/factorio-blueprint-generator/src/bin/balancer_blueprint.rs index 5e4ace4..06f97e8 100644 --- a/factorio-blueprint-generator/src/bin/balancer_blueprint.rs +++ b/factorio-blueprint-generator/src/bin/balancer_blueprint.rs @@ -1,5 +1,5 @@ use factorio_blueprint::{BlueprintBook, BlueprintBookEntry, BlueprintString, encode}; -use factorio_blueprint_generator::balancer::{binary_balancer, generate_4_lane_balancer}; +use factorio_blueprint_generator::balancer::binary_balancer; fn main() { let mut v = Vec::new(); diff --git a/factorio-blueprint-generator/src/multistation.rs b/factorio-blueprint-generator/src/multistation.rs index 4b9676b..af71762 100644 --- a/factorio-blueprint-generator/src/multistation.rs +++ b/factorio-blueprint-generator/src/multistation.rs @@ -1,4 +1,4 @@ -use factorio_blueprint::abstraction::{Blueprint, Entity, EntityType, RailType, UndergroundType}; +use factorio_blueprint::abstraction::{Blueprint, Entity, RailType, UndergroundType}; use factorio_core::{beltoptions::Beltspeed, prelude::*}; use crate::{balancer::binary_balancer, station::basic_station}; diff --git a/factorio-core/src/position.rs b/factorio-core/src/position.rs index 6023cbf..5ac6e22 100644 --- a/factorio-core/src/position.rs +++ b/factorio-core/src/position.rs @@ -67,12 +67,6 @@ impl std::ops::Sub for Position { } } -impl From<(i32, i32)> for Position { - fn from(value: (i32, i32)) -> Self { - Position::new(value.0, value.1) - } -} - impl std::ops::Mul for Position { type Output = Position; @@ -91,3 +85,9 @@ impl std::ops::Mul for i32 { rhs } } + +impl From<(PositionType, PositionType)> for Position { + fn from(value: (PositionType, PositionType)) -> Self { + Self::new(value.0, value.1) + } +} diff --git a/factorio-core/src/visualize/mod.rs b/factorio-core/src/visualize/mod.rs index d0a77cb..48f960e 100644 --- a/factorio-core/src/visualize/mod.rs +++ b/factorio-core/src/visualize/mod.rs @@ -26,6 +26,7 @@ pub trait Visualize { } } +#[derive(Debug, Clone, Copy)] pub enum Symbol { Arrow(Direction), ArrowEnter(Direction), @@ -131,6 +132,16 @@ impl Visualization { } } + pub fn overlay_visualization(&mut self, other: &Self) { + self.size = Position::new( + PositionType::max(self.size.x, other.size.x), + PositionType::max(self.size.y, other.size.y), + ); + + self.symbols + .extend(other.symbols.iter().map(|(&k, &v)| (k, v))); + } + pub fn size(&self) -> Position { self.size } diff --git a/factorio-pathfinding/src/belt_finding/mod.rs b/factorio-pathfinding/src/belt_finding/mod.rs index 2b8a4dd..53fa4dc 100644 --- a/factorio-pathfinding/src/belt_finding/mod.rs +++ b/factorio-pathfinding/src/belt_finding/mod.rs @@ -1,7 +1,10 @@ -use crate::graph::wheighted_graph::shortest_path::dijkstra; +use crate::examples::HashMapMap; use crate::graph::wheighted_graph::WheightedGraph; use crate::misc::Map; use crate::priority_queue::BinaryHeap; +use crate::Connection; +use crate::Map as _; +use crate::{graph::wheighted_graph::shortest_path::dijkstra, SinglePathfinder}; use factorio_core::{prelude::*, visualize::Visualize}; use serde::{Deserialize, Serialize}; @@ -9,6 +12,45 @@ pub mod brute_force; pub mod conflict_avoidance; +pub struct ConflictAvoidance { + timeout: Option, +} + +impl SinglePathfinder for ConflictAvoidance { + fn find_paths( + &self, + input: &[crate::SingleConnection], + map: impl crate::Map, + ) -> Option>> { + let (pos, size) = map.area(); + let mut p = Problem::new(size.x as usize, size.y as usize); + + for x in 0..size.x { + for y in 0..size.y { + if !map.get_position(Position::new(x - pos.x, y - pos.y)) { + p.set_blocked(x as usize, y as usize, true); + } + } + } + + for i in input { + p.add_connection((i.start_pos, i.start_dir), (i.end_pos, i.end_dir)); + } + + if p.find_path() { + let mut c = conflict_avoidance::ConflictAvoidance::new(&p); + + if c.remove_all_conflicts(self.timeout) { + Some(c.get_paths().to_vec()) + } else { + None + } + } else { + None + } + } +} + #[derive(Debug, Default, Serialize, Deserialize, Clone, Copy)] pub struct Field { pub blocked: bool, @@ -33,45 +75,6 @@ impl Problem { } } - // pub fn from_layout(l: &Layout) -> Self { - // let mut p = Self::new(l.problem.size.x as usize, l.problem.size.y as usize); - - // for b in &l.blocks { - // let aabb = b.get_aabb(); - - // p.set_blocked_range( - // aabb.min().x as usize, - // aabb.min().y as usize, - // aabb.max().x as usize, - // aabb.max().y as usize, - // true, - // ); - // } - - // for c in &l.problem.connections { - // let start_transform = l.blocks[c.startblock].block_to_world(); - // let startpos = l.problem.blocks[c.startblock].output[c.startpoint] - // .offset - // .transform(start_transform); - // let startdir = l.problem.blocks[c.startblock].output[c.startpoint] - // .dir - // .transform(start_transform); - // let end_transform = l.blocks[c.endblock].block_to_world(); - // let endpos = l.problem.blocks[c.endblock].input[c.endpoint] - // .offset - // .transform(end_transform); - // let enddir = l.problem.blocks[c.endblock].input[c.endpoint] - // .dir - // .transform(end_transform); - // p.add_connection( - // (startpos, startdir), - // (endpos.in_direction(&enddir, -1), enddir), - // ); - // } - - // p - // } - pub fn add_connection(&mut self, start: (Position, Direction), end: (Position, Direction)) { self.start.push(start); self.end.push(end); @@ -125,6 +128,27 @@ impl Problem { } } +impl From<(Vec, HashMapMap)> for Problem { + fn from((input, map): (Vec, HashMapMap)) -> Self { + let (pos, size) = map.area(); + let mut p = Problem::new(size.x as usize, size.y as usize); + + for x in 0..size.x { + for y in 0..size.y { + if !map.get_position(Position::new(x - pos.x, y - pos.y)) { + p.set_blocked(x as usize, y as usize, true); + } + } + } + + for i in input { + p.add_connection((i.start_pos, i.start_dir), (i.end_pos, i.end_dir)); + } + + p + } +} + impl Visualize for Problem { fn visualize(&self) -> factorio_core::visualize::Visualization { let mut v = factorio_core::visualize::Visualization::new(Position::new( @@ -247,321 +271,3 @@ impl Problem { true } } - -pub mod problems { - use super::Problem; - use factorio_core::prelude::*; - - pub fn simple() -> Problem { - let mut p = Problem::new(5, 3); - - p.set_blocked_range(2, 0, 2, 2, true); - - p.add_connection( - (Position::new(0, 1), Direction::Right), - (Position::new(4, 1), Direction::Right), - ); - - p - } - - pub fn belt_madness_level_1() -> Problem { - let mut p = Problem::new(17, 13); - - 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); - - p.add_connection( - (Position::new(2, 7), Direction::Right), - (Position::new(13, 4), Direction::Right), - ); - p.add_connection( - (Position::new(2, 8), Direction::Right), - (Position::new(13, 5), Direction::Right), - ); - - p.add_connection( - (Position::new(2, 4), Direction::Right), - (Position::new(13, 8), Direction::Right), - ); - p.add_connection( - (Position::new(2, 5), Direction::Right), - (Position::new(13, 7), Direction::Right), - ); - - p - } - - pub fn belt_madness_level_2() -> Problem { - let mut p = Problem::new(17, 13); - p.set_blocked(0, 3, true); - - p.set_blocked_range(1, 2, 2, 5, true); - p.set_blocked_range(1, 7, 2, 10, true); - - p.set_blocked(0, 9, true); - - p.set_blocked(16, 3, true); - - p.set_blocked_range(14, 2, 15, 5, true); - p.set_blocked_range(14, 7, 15, 10, true); - - p.set_blocked(16, 9, true); - - p.add_connection( - (Position::new(2, 4), Direction::Right), - (Position::new(13, 2), Direction::Right), - ); - p.add_connection( - (Position::new(2, 7), Direction::Right), - (Position::new(13, 3), Direction::Right), - ); - p.add_connection( - (Position::new(2, 10), Direction::Right), - (Position::new(13, 4), Direction::Right), - ); - p.add_connection( - (Position::new(2, 9), Direction::Right), - (Position::new(13, 5), Direction::Right), - ); - - p.add_connection( - (Position::new(2, 2), Direction::Right), - (Position::new(13, 7), Direction::Right), - ); - p.add_connection( - (Position::new(2, 3), Direction::Right), - (Position::new(13, 8), Direction::Right), - ); - p.add_connection( - (Position::new(2, 5), Direction::Right), - (Position::new(13, 9), Direction::Right), - ); - p.add_connection( - (Position::new(2, 8), Direction::Right), - (Position::new(13, 10), Direction::Right), - ); - - p - } - - pub fn belt_madness_level_3() -> Problem { - let mut p = Problem::new(33, 13); - - p.set_blocked_range(1, 3, 2, 5, true); - p.set_blocked_range(1, 7, 2, 9, true); - - p.set_blocked(0, 3, true); - p.set_blocked(0, 8, true); - - p.set_blocked_range(10, 0, 21, 2, true); - p.set_blocked_range(10, 5, 21, 7, true); - p.set_blocked_range(10, 10, 21, 12, true); - - p.set_blocked_range(30, 3, 31, 5, true); - p.set_blocked_range(30, 7, 31, 9, true); - p.set_blocked(32, 3, true); - p.set_blocked(32, 9, true); - - p.add_connection( - (Position::new(2, 3), Direction::Right), - (Position::new(29, 7), Direction::Right), - ); - p.add_connection( - (Position::new(2, 4), Direction::Right), - (Position::new(29, 9), Direction::Right), - ); - p.add_connection( - (Position::new(2, 5), Direction::Right), - (Position::new(29, 8), Direction::Right), - ); - - p.add_connection( - (Position::new(2, 7), Direction::Right), - (Position::new(29, 3), Direction::Right), - ); - p.add_connection( - (Position::new(2, 8), Direction::Right), - (Position::new(29, 5), Direction::Right), - ); - p.add_connection( - (Position::new(2, 9), Direction::Right), - (Position::new(29, 4), Direction::Right), - ); - - p - } - - pub fn belt_madness_level_5() -> Problem { - let mut p = Problem::new(31, 29); - - // power stations - p.set_blocked_range(8, 8, 9, 9, true); - p.set_blocked_range(21, 8, 22, 9, true); - p.set_blocked_range(8, 19, 9, 20, true); - p.set_blocked_range(21, 19, 22, 20, true); - - // solar panels - p.set_blocked_range(12, 11, 14, 13, true); - p.set_blocked_range(16, 11, 18, 13, true); - p.set_blocked_range(12, 15, 14, 17, true); - p.set_blocked_range(16, 15, 18, 17, true); - - // Top - p.set_blocked_range(7, 0, 8, 2, true); - p.set_blocked(8, 3, true); - p.set_blocked(9, 4, true); - p.set_blocked_range(10, 5, 20, 5, true); - p.set_blocked(21, 4, true); - p.set_blocked(22, 3, true); - p.set_blocked_range(22, 0, 23, 2, true); - - p.set_blocked_range(2, 1, 2, 2, true); - p.set_blocked_range(4, 1, 4, 2, true); - p.set_blocked_range(1, 4, 2, 4, true); - - p.set_blocked_range(12, 1, 12, 2, true); - p.set_blocked_range(14, 1, 14, 2, true); - p.set_blocked_range(16, 1, 16, 2, true); - p.set_blocked_range(18, 1, 18, 2, true); - - p.set_blocked_range(28, 4, 29, 4, true); - p.set_blocked_range(26, 1, 26, 2, true); - p.set_blocked_range(28, 1, 28, 2, true); - - // Bottom - p.set_blocked_range(7, 26, 8, 28, true); - p.set_blocked(8, 25, true); - p.set_blocked(9, 24, true); - p.set_blocked_range(10, 23, 20, 23, true); - p.set_blocked(21, 24, true); - p.set_blocked(22, 25, true); - p.set_blocked_range(22, 26, 23, 28, true); - - p.set_blocked_range(1, 26, 2, 26, true); - p.set_blocked_range(4, 26, 4, 27, true); - p.set_blocked_range(1, 24, 2, 24, true); - - p.set_blocked_range(12, 26, 12, 27, true); - p.set_blocked_range(14, 26, 14, 27, true); - p.set_blocked_range(16, 26, 16, 27, true); - p.set_blocked_range(18, 26, 18, 27, true); - - p.set_blocked_range(28, 24, 29, 24, true); - p.set_blocked_range(26, 26, 26, 27, true); - p.set_blocked_range(28, 26, 29, 26, true); - - // Left - p.set_blocked_range(0, 7, 2, 8, true); - p.set_blocked(3, 8, true); - p.set_blocked(4, 9, true); - p.set_blocked_range(5, 10, 5, 18, true); - p.set_blocked(4, 19, true); - p.set_blocked(3, 20, true); - p.set_blocked_range(0, 20, 2, 21, true); - - p.set_blocked_range(1, 11, 2, 11, true); - p.set_blocked_range(1, 13, 2, 13, true); - p.set_blocked_range(1, 15, 2, 15, true); - p.set_blocked_range(1, 17, 2, 17, true); - - // Right - p.set_blocked_range(28, 7, 30, 8, true); - p.set_blocked(27, 8, true); - p.set_blocked(26, 9, true); - p.set_blocked_range(25, 10, 25, 18, true); - p.set_blocked(26, 19, true); - p.set_blocked(27, 20, true); - p.set_blocked_range(28, 20, 30, 21, true); - - p.set_blocked_range(28, 11, 29, 11, true); - p.set_blocked_range(28, 13, 29, 13, true); - p.set_blocked_range(28, 15, 29, 15, true); - p.set_blocked_range(28, 17, 29, 17, true); - - // Path - p.add_connection( - (Position::new(4, 2), Direction::Down), - (Position::new(26, 25), Direction::Down), - ); - p.add_connection( - (Position::new(12, 2), Direction::Down), - (Position::new(18, 25), Direction::Down), - ); - p.add_connection( - (Position::new(14, 2), Direction::Down), - (Position::new(3, 26), Direction::Left), - ); - p.add_connection( - (Position::new(16, 2), Direction::Down), - (Position::new(14, 25), Direction::Down), - ); - p.add_connection( - (Position::new(2, 4), Direction::Right), - (Position::new(27, 24), Direction::Right), - ); - p.add_connection( - (Position::new(2, 11), Direction::Right), - (Position::new(27, 17), Direction::Right), - ); - p.add_connection( - (Position::new(2, 13), Direction::Right), - (Position::new(27, 26), Direction::Right), - ); - p.add_connection( - (Position::new(2, 15), Direction::Right), - (Position::new(27, 13), Direction::Right), - ); - p.add_connection( - (Position::new(28, 15), Direction::Left), - (Position::new(2, 3), Direction::Up), - ); - p.add_connection( - (Position::new(2, 17), Direction::Right), - (Position::new(18, 3), Direction::Up), - ); - p.add_connection( - (Position::new(2, 24), Direction::Right), - (Position::new(26, 3), Direction::Up), - ); - p.add_connection( - (Position::new(4, 26), Direction::Up), - (Position::new(27, 4), Direction::Right), - ); - p.add_connection( - (Position::new(12, 26), Direction::Up), - (Position::new(27, 11), Direction::Right), - ); - p.add_connection( - (Position::new(16, 26), Direction::Up), - (Position::new(28, 3), Direction::Up), - ); - - p - } -} diff --git a/factorio-pathfinding/src/bin/beltfinding.rs b/factorio-pathfinding/src/bin/beltfinding.rs index c8a1903..2d8bd96 100644 --- a/factorio-pathfinding/src/bin/beltfinding.rs +++ b/factorio-pathfinding/src/bin/beltfinding.rs @@ -1,8 +1,9 @@ use clap::{Parser, Subcommand, ValueEnum}; use factorio_blueprint::{encode, Blueprint, BlueprintString}; use factorio_core::{beltoptions::Beltspeed, visualize::Visualize}; -use factorio_pathfinding::belt_finding::{ - conflict_avoidance::ConflictAvoidance, problems, Problem, +use factorio_pathfinding::{ + belt_finding::{conflict_avoidance::ConflictAvoidance, Problem}, + examples, }; use std::{io, path::PathBuf}; @@ -26,11 +27,11 @@ enum ProblemCase { impl ProblemCase { fn get_problem(&self) -> Problem { match self { - ProblemCase::Simple => problems::simple(), - ProblemCase::Level1 => problems::belt_madness_level_1(), - ProblemCase::Level2 => problems::belt_madness_level_2(), - ProblemCase::Level3 => problems::belt_madness_level_3(), - ProblemCase::Level5 => problems::belt_madness_level_5(), + ProblemCase::Simple => examples::simple().into(), + ProblemCase::Level1 => examples::belt_madness_level_1().into(), + ProblemCase::Level2 => examples::belt_madness_level_2().into(), + ProblemCase::Level3 => examples::belt_madness_level_3().into(), + ProblemCase::Level5 => examples::belt_madness_level_5().into(), ProblemCase::File { filename } => { let file = std::fs::File::open(filename).unwrap(); serde_json::from_reader(file).unwrap() diff --git a/factorio-pathfinding/src/examples.rs b/factorio-pathfinding/src/examples.rs new file mode 100644 index 0000000..a4daa32 --- /dev/null +++ b/factorio-pathfinding/src/examples.rs @@ -0,0 +1,527 @@ +use crate::{Connection, Map}; +use factorio_core::{ + beltoptions::Beltspeed, + prelude::*, + visualize::{Visualization, Visualize}, +}; +use std::collections::HashSet; + +pub struct HashMapMap { + map: HashSet, + size: Position, +} + +impl HashMapMap { + fn new(size: impl Into) -> Self { + Self { + map: HashSet::new(), + size: size.into(), + } + } + + fn set_blocked_range( + &mut self, + min_pos: impl Into, + max_pos: impl Into, + block: bool, + ) { + let min_pos = min_pos.into(); + let max_pos = max_pos.into(); + + for x in min_pos.x..=max_pos.x { + for y in min_pos.y..=max_pos.y { + self.set_blocked(Position::new(x, y), block); + } + } + } + + fn set_blocked(&mut self, pos: impl Into, block: bool) { + let pos = pos.into(); + if block { + self.map.insert(pos); + } else { + self.map.remove(&pos); + } + } +} + +impl Visualize for HashMapMap { + fn visualize(&self) -> factorio_core::visualize::Visualization { + let mut v = Visualization::new(self.size); + + for &p in &self.map { + v.add_symbol(p, factorio_core::visualize::Symbol::Block, None, None); + } + + v + } +} + +impl Map for HashMapMap { + fn get_position(&self, pos: Position) -> bool { + if pos.x < 0 || pos.y < 0 || pos.x >= self.size.x || pos.y >= self.size.y { + false + } else { + !self.map.contains(&pos) + } + } + + fn area(&self) -> (Position, Position) { + (Position::new(0, 0), self.size) + } +} + +pub fn simple() -> (Vec, HashMapMap) { + let mut m = HashMapMap::new((5, 3)); + + m.set_blocked_range((2, 0), (2, 2), true); + + ( + vec![Connection { + start_pos: Position::new(0, 1), + start_dir: Direction::Right, + end_pos: Position::new(4, 1), + end_dir: Direction::Right, + beltspeed: Beltspeed::Normal, + lanes: 1, + }], + m, + ) +} + +pub fn belt_madness_level_1() -> (Vec, HashMapMap) { + let mut m = HashMapMap::new((17, 13)); + + m.set_blocked((0, 3), true); + + m.set_blocked((1, 4), true); + m.set_blocked((2, 4), true); + m.set_blocked((1, 5), true); + m.set_blocked((2, 5), true); + + m.set_blocked((1, 7), true); + m.set_blocked((2, 7), true); + m.set_blocked((1, 8), true); + m.set_blocked((2, 8), true); + + m.set_blocked((0, 9), true); + + m.set_blocked((16, 3), true); + + m.set_blocked((14, 4), true); + m.set_blocked((15, 4), true); + m.set_blocked((14, 5), true); + m.set_blocked((15, 5), true); + + m.set_blocked((14, 7), true); + m.set_blocked((15, 7), true); + m.set_blocked((14, 8), true); + m.set_blocked((15, 8), true); + + m.set_blocked((16, 9), true); + + ( + vec![ + Connection { + start_pos: Position::new(2, 7), + start_dir: Direction::Right, + end_pos: Position::new(13, 4), + end_dir: Direction::Right, + beltspeed: Beltspeed::Normal, + lanes: 1, + }, + Connection { + start_pos: Position::new(2, 8), + start_dir: Direction::Right, + end_pos: Position::new(13, 5), + end_dir: Direction::Right, + beltspeed: Beltspeed::Normal, + lanes: 1, + }, + Connection { + start_pos: Position::new(2, 4), + start_dir: Direction::Right, + end_pos: Position::new(13, 8), + end_dir: Direction::Right, + beltspeed: Beltspeed::Normal, + lanes: 1, + }, + Connection { + start_pos: Position::new(2, 5), + start_dir: Direction::Right, + end_pos: Position::new(13, 7), + end_dir: Direction::Right, + beltspeed: Beltspeed::Normal, + lanes: 1, + }, + ], + m, + ) +} + +pub fn belt_madness_level_2() -> (Vec, HashMapMap) { + let mut m = HashMapMap::new((17, 13)); + m.set_blocked((0, 3), true); + + m.set_blocked_range((1, 2), (2, 5), true); + m.set_blocked_range((1, 7), (2, 10), true); + + m.set_blocked((0, 9), true); + + m.set_blocked((16, 3), true); + + m.set_blocked_range((14, 2), (15, 5), true); + m.set_blocked_range((14, 7), (15, 10), true); + + m.set_blocked((16, 9), true); + + ( + vec![ + Connection { + start_pos: Position::new(2, 4), + start_dir: Direction::Right, + end_pos: Position::new(13, 2), + end_dir: Direction::Right, + beltspeed: Beltspeed::Normal, + lanes: 1, + }, + Connection { + start_pos: Position::new(2, 7), + start_dir: Direction::Right, + end_pos: Position::new(13, 3), + end_dir: Direction::Right, + beltspeed: Beltspeed::Normal, + lanes: 1, + }, + Connection { + start_pos: Position::new(2, 10), + start_dir: Direction::Right, + end_pos: Position::new(13, 4), + end_dir: Direction::Right, + beltspeed: Beltspeed::Normal, + lanes: 1, + }, + Connection { + start_pos: Position::new(2, 9), + start_dir: Direction::Right, + end_pos: Position::new(13, 5), + end_dir: Direction::Right, + beltspeed: Beltspeed::Normal, + lanes: 1, + }, + Connection { + start_pos: Position::new(2, 2), + start_dir: Direction::Right, + end_pos: Position::new(13, 7), + end_dir: Direction::Right, + beltspeed: Beltspeed::Normal, + lanes: 1, + }, + Connection { + start_pos: Position::new(2, 3), + start_dir: Direction::Right, + end_pos: Position::new(13, 8), + end_dir: Direction::Right, + beltspeed: Beltspeed::Normal, + lanes: 1, + }, + Connection { + start_pos: Position::new(2, 5), + start_dir: Direction::Right, + end_pos: Position::new(13, 9), + end_dir: Direction::Right, + beltspeed: Beltspeed::Normal, + lanes: 1, + }, + Connection { + start_pos: Position::new(2, 8), + start_dir: Direction::Right, + end_pos: Position::new(13, 10), + end_dir: Direction::Right, + beltspeed: Beltspeed::Normal, + lanes: 1, + }, + ], + m, + ) +} + +pub fn belt_madness_level_3() -> (Vec, HashMapMap) { + let mut m = HashMapMap::new((33, 13)); + + m.set_blocked_range((1, 3), (2, 5), true); + m.set_blocked_range((1, 7), (2, 9), true); + + m.set_blocked((0, 3), true); + m.set_blocked((0, 8), true); + + m.set_blocked_range((10, 0), (21, 2), true); + m.set_blocked_range((10, 5), (21, 7), true); + m.set_blocked_range((10, 10), (21, 12), true); + + m.set_blocked_range((30, 3), (31, 5), true); + m.set_blocked_range((30, 7), (31, 9), true); + m.set_blocked((32, 3), true); + m.set_blocked((32, 9), true); + + ( + vec![ + Connection { + start_pos: Position::new(2, 3), + start_dir: Direction::Right, + end_pos: Position::new(29, 7), + end_dir: Direction::Right, + beltspeed: Beltspeed::Normal, + lanes: 1, + }, + Connection { + start_pos: Position::new(2, 4), + start_dir: Direction::Right, + end_pos: Position::new(29, 9), + end_dir: Direction::Right, + beltspeed: Beltspeed::Normal, + lanes: 1, + }, + Connection { + start_pos: Position::new(2, 5), + start_dir: Direction::Right, + end_pos: Position::new(29, 8), + end_dir: Direction::Right, + beltspeed: Beltspeed::Normal, + lanes: 1, + }, + Connection { + start_pos: Position::new(2, 7), + start_dir: Direction::Right, + end_pos: Position::new(29, 3), + end_dir: Direction::Right, + beltspeed: Beltspeed::Normal, + lanes: 1, + }, + Connection { + start_pos: Position::new(2, 8), + start_dir: Direction::Right, + end_pos: Position::new(29, 5), + end_dir: Direction::Right, + beltspeed: Beltspeed::Normal, + lanes: 1, + }, + Connection { + start_pos: Position::new(2, 9), + start_dir: Direction::Right, + end_pos: Position::new(29, 4), + end_dir: Direction::Right, + beltspeed: Beltspeed::Normal, + lanes: 1, + }, + ], + m, + ) +} + +pub fn belt_madness_level_5() -> (Vec, HashMapMap) { + let mut m = HashMapMap::new((31, 29)); + + // power stations + m.set_blocked_range((8, 8), (9, 9), true); + m.set_blocked_range((21, 8), (22, 9), true); + m.set_blocked_range((8, 19), (9, 20), true); + m.set_blocked_range((21, 19), (22, 20), true); + + // solar panels + m.set_blocked_range((12, 11), (14, 13), true); + m.set_blocked_range((16, 11), (18, 13), true); + m.set_blocked_range((12, 15), (14, 17), true); + m.set_blocked_range((16, 15), (18, 17), true); + + // Top + m.set_blocked_range((7, 0), (8, 2), true); + m.set_blocked((8, 3), true); + m.set_blocked((9, 4), true); + m.set_blocked_range((10, 5), (20, 5), true); + m.set_blocked((21, 4), true); + m.set_blocked((22, 3), true); + m.set_blocked_range((22, 0), (23, 2), true); + + m.set_blocked_range((2, 1), (2, 2), true); + m.set_blocked_range((4, 1), (4, 2), true); + m.set_blocked_range((1, 4), (2, 4), true); + + m.set_blocked_range((12, 1), (12, 2), true); + m.set_blocked_range((14, 1), (14, 2), true); + m.set_blocked_range((16, 1), (16, 2), true); + m.set_blocked_range((18, 1), (18, 2), true); + + m.set_blocked_range((28, 4), (29, 4), true); + m.set_blocked_range((26, 1), (26, 2), true); + m.set_blocked_range((28, 1), (28, 2), true); + + // Bottom + m.set_blocked_range((7, 26), (8, 28), true); + m.set_blocked((8, 25), true); + m.set_blocked((9, 24), true); + m.set_blocked_range((10, 23), (20, 23), true); + m.set_blocked((21, 24), true); + m.set_blocked((22, 25), true); + m.set_blocked_range((22, 26), (23, 28), true); + + m.set_blocked_range((1, 26), (2, 26), true); + m.set_blocked_range((4, 26), (4, 27), true); + m.set_blocked_range((1, 24), (2, 24), true); + + m.set_blocked_range((12, 26), (12, 27), true); + m.set_blocked_range((14, 26), (14, 27), true); + m.set_blocked_range((16, 26), (16, 27), true); + m.set_blocked_range((18, 26), (18, 27), true); + + m.set_blocked_range((28, 24), (29, 24), true); + m.set_blocked_range((26, 26), (26, 27), true); + m.set_blocked_range((28, 26), (29, 26), true); + + // Left + m.set_blocked_range((0, 7), (2, 8), true); + m.set_blocked((3, 8), true); + m.set_blocked((4, 9), true); + m.set_blocked_range((5, 10), (5, 18), true); + m.set_blocked((4, 19), true); + m.set_blocked((3, 20), true); + m.set_blocked_range((0, 20), (2, 21), true); + + m.set_blocked_range((1, 11), (2, 11), true); + m.set_blocked_range((1, 13), (2, 13), true); + m.set_blocked_range((1, 15), (2, 15), true); + m.set_blocked_range((1, 17), (2, 17), true); + + // Right + m.set_blocked_range((28, 7), (30, 8), true); + m.set_blocked((27, 8), true); + m.set_blocked((26, 9), true); + m.set_blocked_range((25, 10), (25, 18), true); + m.set_blocked((26, 19), true); + m.set_blocked((27, 20), true); + m.set_blocked_range((28, 20), (30, 21), true); + + m.set_blocked_range((28, 11), (29, 11), true); + m.set_blocked_range((28, 13), (29, 13), true); + m.set_blocked_range((28, 15), (29, 15), true); + m.set_blocked_range((28, 17), (29, 17), true); + + // Path + ( + vec![ + Connection { + start_pos: Position::new(4, 2), + start_dir: Direction::Down, + end_pos: Position::new(26, 25), + end_dir: Direction::Down, + beltspeed: Beltspeed::Normal, + lanes: 1, + }, + Connection { + start_pos: Position::new(12, 2), + start_dir: Direction::Down, + end_pos: Position::new(18, 25), + end_dir: Direction::Down, + beltspeed: Beltspeed::Normal, + lanes: 1, + }, + Connection { + start_pos: Position::new(14, 2), + start_dir: Direction::Down, + end_pos: Position::new(3, 26), + end_dir: Direction::Left, + beltspeed: Beltspeed::Normal, + lanes: 1, + }, + Connection { + start_pos: Position::new(16, 2), + start_dir: Direction::Down, + end_pos: Position::new(14, 25), + end_dir: Direction::Down, + beltspeed: Beltspeed::Normal, + lanes: 1, + }, + Connection { + start_pos: Position::new(2, 4), + start_dir: Direction::Right, + end_pos: Position::new(27, 24), + end_dir: Direction::Right, + beltspeed: Beltspeed::Normal, + lanes: 1, + }, + Connection { + start_pos: Position::new(2, 11), + start_dir: Direction::Right, + end_pos: Position::new(27, 17), + end_dir: Direction::Right, + beltspeed: Beltspeed::Normal, + lanes: 1, + }, + Connection { + start_pos: Position::new(2, 13), + start_dir: Direction::Right, + end_pos: Position::new(27, 26), + end_dir: Direction::Right, + beltspeed: Beltspeed::Normal, + lanes: 1, + }, + Connection { + start_pos: Position::new(2, 15), + start_dir: Direction::Right, + end_pos: Position::new(27, 13), + end_dir: Direction::Right, + beltspeed: Beltspeed::Normal, + lanes: 1, + }, + Connection { + start_pos: Position::new(28, 15), + start_dir: Direction::Left, + end_pos: Position::new(2, 3), + end_dir: Direction::Up, + beltspeed: Beltspeed::Normal, + lanes: 1, + }, + Connection { + start_pos: Position::new(2, 17), + start_dir: Direction::Right, + end_pos: Position::new(18, 3), + end_dir: Direction::Up, + beltspeed: Beltspeed::Normal, + lanes: 1, + }, + Connection { + start_pos: Position::new(2, 24), + start_dir: Direction::Right, + end_pos: Position::new(26, 3), + end_dir: Direction::Up, + beltspeed: Beltspeed::Normal, + lanes: 1, + }, + Connection { + start_pos: Position::new(4, 26), + start_dir: Direction::Up, + end_pos: Position::new(27, 4), + end_dir: Direction::Right, + beltspeed: Beltspeed::Normal, + lanes: 1, + }, + Connection { + start_pos: Position::new(12, 26), + start_dir: Direction::Up, + end_pos: Position::new(27, 11), + end_dir: Direction::Right, + beltspeed: Beltspeed::Normal, + lanes: 1, + }, + Connection { + start_pos: Position::new(16, 26), + start_dir: Direction::Up, + end_pos: Position::new(28, 3), + end_dir: Direction::Up, + beltspeed: Beltspeed::Normal, + lanes: 1, + }, + ], + m, + ) +} diff --git a/factorio-pathfinding/src/lib.rs b/factorio-pathfinding/src/lib.rs index 13c2f56..8eeefdc 100644 --- a/factorio-pathfinding/src/lib.rs +++ b/factorio-pathfinding/src/lib.rs @@ -1,4 +1,74 @@ +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 Connection { + pub start_pos: Position, + pub start_dir: Direction, + pub end_pos: Position, + pub end_dir: Direction, + pub beltspeed: Beltspeed, + pub lanes: usize, +} + +pub trait Map { + /// Returns true if the position is useable + fn get_position(&self, pos: Position) -> bool; + /// Returns the relevant area as (pos, size) + fn area(&self) -> (Position, Position); +} + +pub trait Pathfinder { + fn find_paths(&self, input: &[Connection], map: impl Map) -> Option>>; +} + +struct SingleConnection { + pub start_pos: Position, + pub start_dir: Direction, + pub end_pos: Position, + pub end_dir: Direction, + pub beltspeed: Beltspeed, +} + +trait SinglePathfinder { + fn find_paths(&self, input: &[SingleConnection], map: impl Map) -> Option>>; +} + +impl

Pathfinder for P +where + P: SinglePathfinder, +{ + fn find_paths(&self, input: &[Connection], map: impl Map) -> Option>> { + let inner_input = input + .iter() + .flat_map(|c| { + (0..c.lanes).map(|i| SingleConnection { + start_pos: c + .start_pos + .in_direction(&c.start_dir.clockwise(), i as PositionType), + start_dir: c.start_dir, + end_pos: c + .end_pos + .in_direction(&c.start_dir.clockwise(), i as PositionType), + end_dir: c.end_dir, + beltspeed: c.beltspeed, + }) + }) + .collect::>(); + + let inner_result = self.find_paths(&inner_input, map)?; + + let mut inner_iterator = inner_result.into_iter(); + + Some( + input + .iter() + .map(|i| inner_iterator.by_ref().take(i.lanes).flatten().collect()) + .collect(), + ) + } +}