diff --git a/factorio-blueprint-generator/src/factory.rs b/factorio-blueprint-generator/src/factory.rs index f598365..ffc9f93 100644 --- a/factorio-blueprint-generator/src/factory.rs +++ b/factorio-blueprint-generator/src/factory.rs @@ -537,6 +537,8 @@ pub fn generate_factory Self { + Self::new(EntityType::Roboport, position, Direction::Up) + } + pub fn new_unknown( name: impl AsRef, position: Position, @@ -299,6 +305,7 @@ impl Entity { size: _, } => name.clone(), EntityType::Rail { rail_type } => rail_type.string(), + EntityType::Roboport => "roboport".to_string(), } } @@ -348,6 +355,7 @@ impl Entity { } => *size, EntityType::ElectricPole(electric_pole_type) => electric_pole_type.size(), EntityType::Rail { rail_type } => rail_type.size(), + EntityType::Roboport => Position::new(8, 8), _ => Position::new(2, 2), } } diff --git a/factorio-blueprint/src/abstraction/roboports.rs b/factorio-blueprint/src/abstraction/roboports.rs new file mode 100644 index 0000000..ebc8af2 --- /dev/null +++ b/factorio-blueprint/src/abstraction/roboports.rs @@ -0,0 +1,50 @@ +use factorio_core::{misc::PositionMap, prelude::Position}; +use factorio_graph::{ + priority_queue::binary_heap::FastBinaryHeap, set_cover::greedy_set_cover_priority_queue, +}; + +use crate::abstraction::Entity; + +use super::Blueprint; + +impl Blueprint { + pub fn add_roboport_network(&mut self) { + let aabb = self.get_aabb().unwrap(); + + let universe = self.entities.len(); + + let map = self.placibility_map(); + + let mut roboports = Vec::new(); + + let mut sets = Vec::new(); + + for (x, y) in (aabb.min().x..=aabb.max().x) + .filter(move |x| x.rem_euclid(2) == 0) + .flat_map(|x| { + (aabb.min().y..=aabb.max().y) + .filter(move |y| y.rem_euclid(2) == 0) + .map(move |y| (x, y)) + }) + { + if map.placeable(Position::new(x, y), Position::new(8, 8)) { + let mut coverage = Vec::new(); + for (i, (_, e)) in self.entities.iter().enumerate() { + if e.position.x.abs_diff(x) <= 110 && e.position.y.abs_diff(y) <= 110 { + coverage.push(i); + } + } + + roboports.push(Position::new(x, y)); + sets.push(coverage); + } + } + + dbg!(universe, sets.len()); + let res = greedy_set_cover_priority_queue::<_, FastBinaryHeap<_>>(universe, &sets); + + for r in res { + self.add_entity(Entity::new_roboport(roboports[r])); + } + } +} diff --git a/factorio-graph/src/lib.rs b/factorio-graph/src/lib.rs index 77d31fb..95149f6 100644 --- a/factorio-graph/src/lib.rs +++ b/factorio-graph/src/lib.rs @@ -1,2 +1,3 @@ pub mod priority_queue; +pub mod set_cover; pub mod wheighted_graph; diff --git a/factorio-graph/src/priority_queue/mod.rs b/factorio-graph/src/priority_queue/mod.rs index 5e02acf..9b0d68f 100644 --- a/factorio-graph/src/priority_queue/mod.rs +++ b/factorio-graph/src/priority_queue/mod.rs @@ -18,9 +18,9 @@ where #[cfg(test)] mod tests { + use super::PriorityQueue; use super::binary_heap::{BinaryHeap, FastBinaryHeap}; use super::fibonacci_heap::FibonacciHeap; - use super::PriorityQueue; macro_rules! test_generics { ($($fun_mul:ident )+ ; $gen:ident $($gen_mul:ident)+) => { diff --git a/factorio-graph/src/set_cover.rs b/factorio-graph/src/set_cover.rs new file mode 100644 index 0000000..d0d168b --- /dev/null +++ b/factorio-graph/src/set_cover.rs @@ -0,0 +1,141 @@ +use std::ops::Deref; + +use crate::priority_queue::PriorityQueue; + +#[derive(Debug, Clone, Copy)] +pub struct SetUncovered { + setIndex: usize, + uncoveredElements: usize, +} + +impl PartialEq for SetUncovered { + fn eq(&self, other: &Self) -> bool { + self.uncoveredElements.eq(&other.uncoveredElements) + } +} + +impl PartialOrd for SetUncovered { + fn partial_cmp(&self, other: &Self) -> Option { + self.uncoveredElements + .partial_cmp(&other.uncoveredElements) + .map(|o| o.reverse()) + } +} + +pub fn greedy_set_cover_simple(universe: usize, sets: &[S]) -> Vec +where + S: Deref, +{ + assert!(sets.len() > 0); + + let mut r = Vec::new(); + + let mut covered = vec![false; universe]; + let mut covered_count = 0; + + while covered_count < universe { + let i = sets + .iter() + .enumerate() + .filter(|(i, _)| !r.contains(i)) + .max_by_key(|&(_, s)| s.iter().filter(|&&e| !covered[e]).count()) + .unwrap() + .0; + + r.push(i); + + for &e in sets[i].deref() { + if !covered[e] { + covered_count += 1; + covered[e] = true; + } + } + } + + r +} + +pub fn greedy_set_cover_priority_queue(universe: usize, sets: &[S]) -> Vec +where + S: Deref, + P: PriorityQueue, +{ + let mut covered = vec![false; universe]; + let mut covered_count = 0; + + let mut p = P::new(); + + let mut handles = sets + .iter() + .enumerate() + .map(|(i, s)| { + Some(p.insert(SetUncovered { + setIndex: i, + uncoveredElements: s.len(), + })) + }) + .collect::>(); + + let mut r = Vec::new(); + + while covered_count < universe { + let SetUncovered { + setIndex: i, + uncoveredElements: _, + } = p.pop_min().unwrap(); + + r.push(i); + handles[i] = None; + + for (h, s) in handles + .iter() + .zip(sets.iter()) + .filter_map(|(h, s)| h.as_ref().map(|h| (h, s))) + { + let mut decrease = 0; + for &e in sets[i].deref() { + if !covered[e] && s.contains(&e) { + decrease += 1; + } + } + + // dbg!(decrease, i, s.deref()); + p.decrease_key(h, |e| e.uncoveredElements -= decrease); + } + + for &e in sets[i].deref() { + if !covered[e] { + covered_count += 1; + covered[e] = true; + } + } + } + + r +} + +#[cfg(test)] +mod test { + use crate::{ + priority_queue::binary_heap::FastBinaryHeap, set_cover::greedy_set_cover_priority_queue, + }; + + use super::greedy_set_cover_simple; + + #[test] + fn wikipedia() { + let u = 5; + + let s = vec![vec![0, 1, 2], vec![1, 3], vec![2, 3], vec![3, 4]]; + + let mut r = greedy_set_cover_simple::<_>(u, &s); + + r.sort(); + + assert_eq!(&r, &[0, 3]); + + let mut r2 = greedy_set_cover_priority_queue::<_, FastBinaryHeap<_>>(u, &s); + r2.sort(); + assert_eq!(&r, &r2); + } +}