diff --git a/factorio-blueprint-generator/src/factory.rs b/factorio-blueprint-generator/src/factory.rs index ffc9f93..4e0b1ed 100644 --- a/factorio-blueprint-generator/src/factory.rs +++ b/factorio-blueprint-generator/src/factory.rs @@ -538,7 +538,10 @@ pub fn generate_factory { - for (dx, dy) in [(-1, -1), (1, -1), (-1, 1), (1, 1)] { + for (dx, dy) in [(-2, -2), (0, -2), (-2, 0), (0, 0)] { v.add_symbol( (self.position + Position::new(dx, dy)) / 2 + offset, factorio_core::visualize::Symbol::Char('S'), @@ -451,6 +451,18 @@ impl Entity { } } }, + EntityType::Roboport => { + for dx in -2..2 { + for dy in -2..2 { + v.add_symbol( + (self.position) / 2 + Position::new(dx, dy) + offset, + factorio_core::visualize::Symbol::Char('R'), + Some(Color::yellow()), + None, + ) + } + } + } _ => (), } } diff --git a/factorio-blueprint/src/abstraction/power_connection.rs b/factorio-blueprint/src/abstraction/power_connection.rs index c483f6c..111dc47 100644 --- a/factorio-blueprint/src/abstraction/power_connection.rs +++ b/factorio-blueprint/src/abstraction/power_connection.rs @@ -1,6 +1,10 @@ +use std::ops::Index; + +use factorio_core::visualize::Visualize; use factorio_graph::{ + graph::Graph, priority_queue::binary_heap::FastBinaryHeap, - wheighted_graph::{WheightedGraph, steiner_tree}, + wheighted_graph::{WheightedGraph, shortest_path::dijkstra, steiner_tree}, }; use super::*; @@ -155,16 +159,6 @@ impl Blueprint { if let Some(cost) = cost { let v = power_connections(Position::new(x, y), electric_pole_type) - // .filter(|&(pos, _)| aabb.contains_pos(pos)) - // .inspect(|d| { - // if y == 261 { - // dbg!(d); - // } - // }) - // .filter(|&(pos, electric_pole_type)| { - // placibility.placeable(pos, electric_pole_type.size()) - // || power_pole_map.contains_key(&(pos, electric_pole_type)) - // }) .collect(); nodes.insert((Position::new(x, y), electric_pole_type), (v, cost)); } @@ -230,3 +224,228 @@ impl Blueprint { } } } + +struct MultistartGraph<'a, G: WheightedGraph> { + graph: &'a G, + start_nodes: Vec, +} + +impl WheightedGraph for MultistartGraph<'_, G> +where + G::Node: Clone, +{ + type Node = Option; + + fn edge(&self, node: &Self::Node, num: usize) -> Option<(Self::Node, f64)> { + match node { + Some(n) => self.graph.edge(n, num).map(|(e, w)| (Some(e), w)), + None => self.start_nodes.get(num).map(|n| (Some(n.clone()), 0.0)), + } + } +} + +impl Blueprint { + pub fn connect_roboport_power(&mut self) { + let mut roboportmap = self + .entities + .iter() + .filter_map(|(k, e)| match e.entity { + EntityType::Roboport => Some(e.position), + _ => None, + }) + .collect::>(); + + if roboportmap.len() == 0 { + return; + } else if roboportmap.len() == 1 { + let roboport = roboportmap.into_iter().next().unwrap(); + for (x, y) in ((roboport.x - 8)..=(roboport.x + 8)) + .filter(move |x| x.rem_euclid(2) == 1) + .flat_map(|x| { + ((roboport.y - 8)..=(roboport.y + 8)) + .filter(move |y| y.rem_euclid(2) == 1) + .map(move |y| (x, y)) + }) + { + if self.placeable(Position::new(x, y), Position::new(2, 2)) { + self.add_entity(Entity::new_electric_pole( + super::ElectricPoleType::Small, + Position::new(x, y), + )); + return; + } + } + panic!("unable to place power pole around roboport"); + } + + let aabb = self.get_aabb().unwrap(); + + let start_roboport = roboportmap.pop().unwrap(); + + let mut start_nodes = Vec::new(); + + { + let placibility = self.placibility_map(); + for (electric_pole_type, dist) in [ + (ElectricPoleType::Small, 9), + (ElectricPoleType::Medium, 11), + (ElectricPoleType::Big, 6), + ] { + for (x, y) in + ((start_roboport.x - dist)..=(start_roboport.x + dist)).flat_map(|x| { + ((start_roboport.y - dist)..=(start_roboport.y + dist)).map(move |y| (x, y)) + }) + { + // dbg!(x, y, electric_pole_type); + let alignment = electric_pole_type.alignment(); + if x.rem_euclid(2) == alignment && y.rem_euclid(2) == alignment { + if placibility.placeable(Position::new(x, y), electric_pole_type.size()) { + start_nodes.push(Node { + pos: Position::new(x, y), + electric_pole_type, + node_type: NodeType::In, + }); + } + } + } + } + } + + let mut visited_roboports = vec![start_roboport]; + + while !roboportmap.is_empty() { + let placibility = self.placibility_map(); + let mut power_pole_map = self + .entities + .iter() + .filter_map(|(k, e)| match e.entity { + EntityType::ElectricPole(electric_pole_type) => { + Some(((e.position, electric_pole_type), *k)) + } + _ => None, + }) + .collect::>(); + + let mut nodes = HashMap::new(); + for y in aabb.min().y..=aabb.max().y { + for x in aabb.min().x..=aabb.max().x { + for electric_pole_type in [ + ElectricPoleType::Small, + ElectricPoleType::Medium, + ElectricPoleType::Big, + ] { + // dbg!(x, y, electric_pole_type); + let alignment = electric_pole_type.alignment(); + if x.rem_euclid(2) == alignment + && y.rem_euclid(2) == alignment + && visited_roboports + .iter() + .any(|p| p.x.abs_diff(x) < 110 && p.y.abs_diff(y) < 110) + { + let cost = if placibility + .placeable(Position::new(x, y), electric_pole_type.size()) + { + Some(match electric_pole_type { + ElectricPoleType::Small => 0.8, + ElectricPoleType::Medium => 1.0, + ElectricPoleType::Big => 1.5, + ElectricPoleType::Substation => todo!(), + }) + } else if power_pole_map + .contains_key(&(Position::new(x, y), electric_pole_type)) + { + Some(0.0) + } else { + None + }; + + if let Some(cost) = cost { + let v = power_connections(Position::new(x, y), electric_pole_type) + .collect(); + nodes.insert((Position::new(x, y), electric_pole_type), (v, cost)); + } + } + } + } + } + + let power_graph = PowerGraph { nodes }; + + let multistart_graph = MultistartGraph { + graph: &power_graph, + start_nodes: start_nodes.clone(), + }; + + let p = dijkstra::<_, FastBinaryHeap<_>, _>(&multistart_graph, None, |p| { + if let Some(n) = p { + match n.node_type { + NodeType::In => false, + NodeType::Out => { + let dist = match n.electric_pole_type { + ElectricPoleType::Small => 7, + ElectricPoleType::Medium => 9, + ElectricPoleType::Big => 6, + ElectricPoleType::Substation => todo!(), + }; + roboportmap.iter().any(|r| { + r.x.abs_diff(n.pos.x) <= dist && r.y.abs_diff(n.pos.y) <= dist + }) + } + } + } else { + false + } + }); + + if let Some(path) = p { + start_nodes = vec![path[1].unwrap()]; + + let path_iter = path.iter().filter_map(|n| n.as_ref()).filter_map( + |Node { + pos, + electric_pole_type, + node_type, + }| match node_type { + NodeType::In => None, + NodeType::Out => Some((*pos, *electric_pole_type)), + }, + ); + + for p in path_iter.clone() { + match power_pole_map.entry(p) { + std::collections::hash_map::Entry::Occupied(_occupied_entry) => (), + std::collections::hash_map::Entry::Vacant(vacant_entry) => { + let k = self.add_entity(Entity::new_electric_pole(p.1, p.0)); + vacant_entry.insert(k); + } + } + } + for (p, n) in path_iter.clone().zip(path_iter.skip(1)) { + self.add_wire(power_pole_map[&n], 5, power_pole_map[&p], 5); + } + + let last_node = path.last().unwrap().unwrap(); + + let dist = match last_node.electric_pole_type { + ElectricPoleType::Small => 7, + ElectricPoleType::Medium => 9, + ElectricPoleType::Big => 6, + ElectricPoleType::Substation => todo!(), + }; + + let r = roboportmap + .iter() + .position(|r| { + r.x.abs_diff(last_node.pos.x) <= dist + && r.y.abs_diff(last_node.pos.y) <= dist + }) + .unwrap(); + + let p = roboportmap.swap_remove(r); + visited_roboports.push(p); + } else { + panic!("No path found"); + } + } + } +} diff --git a/factorio-blueprint/src/abstraction/roboports.rs b/factorio-blueprint/src/abstraction/roboports.rs index 542fbb3..fb9c87b 100644 --- a/factorio-blueprint/src/abstraction/roboports.rs +++ b/factorio-blueprint/src/abstraction/roboports.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use factorio_core::{misc::PositionMap, prelude::Position}; use factorio_graph::{ graph::Graph, @@ -7,7 +9,7 @@ use factorio_graph::{ use crate::abstraction::Entity; -use super::Blueprint; +use super::{Blueprint, EntityType}; struct RoboportGraph<'a> { roboports: &'a [Position], @@ -60,7 +62,6 @@ impl Blueprint { } } - dbg!(universe, sets.len()); let res = greedy_connected_set_cover_priority_queue::<_, _, FastBinaryHeap<_>>( universe, &sets, @@ -70,7 +71,11 @@ impl Blueprint { ); for r in res { - self.add_entity(Entity::new_roboport(roboports[r])); + if self.placeable(roboports[r], Position::new(8, 8)) { + self.add_entity(Entity::new_roboport(roboports[r])); + } else { + panic!("Roboport not placeable"); + } } } } diff --git a/factorio-core/src/visualize/mod.rs b/factorio-core/src/visualize/mod.rs index 7b8025d..e083a00 100644 --- a/factorio-core/src/visualize/mod.rs +++ b/factorio-core/src/visualize/mod.rs @@ -89,6 +89,10 @@ impl Color { Self::new(0x0f, 0xf0, 0xfc) } + pub fn yellow() -> Self { + Self::new(0xff, 0xff, 0x00) + } + pub fn gray(l: u8) -> Self { Self::new(l, l, l) }