factorio_blueprint/src/belt_finding/mod.rs

535 lines
16 KiB
Rust

use crate::graph::wheighted_graph::WheightedGraph;
use crate::misc::Map;
use crate::{
graph::wheighted_graph::shortest_path::dijkstra, priority_queue::fibonacci_heap::FibonacciHeap,
};
use std::ops::Index;
use termcolor::{Color, ColorSpec};
use self::common::{print_map, Direction, Position, PositionType};
pub mod brute_force;
pub mod common;
pub mod conflict_avoidance;
#[derive(Default, Clone, Copy)]
pub struct Field {
pub blocked: bool,
weight: f64,
}
pub struct Problem {
map: Map<Field>,
start: Vec<(Position, Direction)>,
end: Vec<(Position, Direction)>,
path: Vec<Vec<(Position, Direction)>>,
}
impl Problem {
pub fn new(width: usize, height: usize) -> Self {
Self {
map: Map::new(width, height),
start: Vec::new(),
end: Vec::new(),
path: Vec::new(),
}
}
pub fn add_connection(&mut self, start: (Position, Direction), end: (Position, Direction)) {
self.start.push(start);
self.end.push(end);
self.path.push(Vec::new());
}
pub fn set_blocked(&mut self, x: usize, y: usize, blocked: bool) {
self.map.get_mut(x, y).blocked = blocked;
}
pub fn set_blocked_range(&mut self, x1: usize, y1: usize, x2: usize, y2: usize, blocked: bool) {
for x in x1..=x2 {
for y in y1..=y2 {
self.set_blocked(x, y, blocked);
}
}
}
fn calculate_wheights(&mut self, without: usize) {
for x in 0..self.map.width {
for y in 0..self.map.height {
let mut weight = 0.0;
if self.map.get(x, y).blocked {
weight += 100.0;
}
self.map.get_mut(x, y).weight = weight;
}
}
for (i, path) in self.path.iter().enumerate() {
if i != without {
for p in path {
let weight = 1.0;
let x = p.0.x as usize;
let y = p.0.y as usize;
self.map.get_mut(x, y).weight += weight;
}
}
}
// for p in &self.start {
// self.map.get_mut(p.0.x as usize, p.0.y as usize).weight = f64::INFINITY;
// }
// for p in &self.end {
// self.map.get_mut(p.0.x as usize, p.0.y as usize).weight = f64::INFINITY;
// }
}
pub fn print(&self) {
let _ = print_map(self.map.width as i32, self.map.height as i32, |x, y| {
let mut color = ColorSpec::new();
if let Some(i) = self
.start
.iter()
.position(|p| p.0 == Position::new(x as PositionType, y as PositionType))
{
color.set_fg(Some(COLORS[i]));
(color, "s")
} else if let Some(i) = self
.end
.iter()
.position(|p| p.0 == Position::new(x as PositionType, y as PositionType))
{
color.set_fg(Some(COLORS[i]));
(color, "t")
} else if let Some((i, p)) = self.path.iter().enumerate().find_map(|(i, v)| {
v.iter()
.position(|p| p.0 == Position::new(x as PositionType, y as PositionType))
.map(|j| (i, j))
}) {
color.set_fg(Some(COLORS[i]));
let c = &self.path[i][p];
match c.1 {
Direction::Up => (color, ""),
Direction::Right => (color, ""),
Direction::Down => (color, ""),
Direction::Left => (color, ""),
}
} else if self.map.get(x as usize, y as usize).blocked {
(color, "#")
} else if x % 8 == 0 || y % 8 == 0 {
(color, "")
} else {
(color, " ")
}
});
}
}
pub static COLORS: Cyclic<Color, 6> = Cyclic([
Color::Red,
Color::Green,
Color::Yellow,
Color::Blue,
Color::Magenta,
Color::Cyan,
]);
pub struct Cyclic<T, const N: usize>([T; N]);
impl<T, const N: usize> Index<usize> for Cyclic<T, N> {
type Output = T;
fn index(&self, index: usize) -> &Self::Output {
&self.0[index % N]
}
}
struct MapInternal<'a> {
map: &'a Map<Field>,
end: (Position, Direction),
}
impl<'a> WheightedGraph for MapInternal<'a> {
type Node = (Position, Direction);
fn edge(&self, node: &Self::Node, num: usize) -> Option<(Self::Node, f64)> {
let next = node.0.in_direction(&node.1, 1);
next.in_range(
&Position::new(0, 0),
&Position::new(
self.map.width as PositionType,
self.map.height as PositionType,
),
)?;
if self.map.get(next.x as usize, next.y as usize).blocked && self.end != (next, node.1) {
return None;
}
let penalty = self.map.get(next.x as usize, next.y as usize).weight;
match num {
0 => Some(((next, node.1), 1.5 + penalty)),
1 => Some(((next, node.1.counter_clockwise()), 1.5 + penalty)),
2 => Some(((next, node.1.clockwise()), 1.5 + penalty)),
_ => {
let mut count = 2;
for l in 2..=6 {
let n = node.0.in_direction(&node.1, l);
n.in_range(
&Position::new(0, 0),
&Position::new(
self.map.width as PositionType,
self.map.height as PositionType,
),
)?;
if !self.map.get(n.x as usize, n.y as usize).blocked {
count += 1;
if count == num {
let penalty = penalty + self.map.get(n.x as usize, n.y as usize).weight;
return Some(((n, node.1), 17.5 + penalty));
}
}
}
None
}
}
}
}
impl Problem {
pub fn find_path(&mut self) {
for i in 0..self.start.len() {
self.calculate_wheights(i);
let m = MapInternal {
map: &self.map,
end: self.end[i],
};
let p = dijkstra::<MapInternal, FibonacciHeap<_>>(&m, self.start[i], self.end[i]);
if let Some(p) = p {
self.path[i] = p;
}
}
}
}
pub mod problems {
use super::{
common::{Direction, Position},
Problem,
};
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
}
}