Rewrote path selection for conflict avoidance.

This commit is contained in:
hal8174 2024-02-17 02:42:38 +01:00
parent 8f6809c17f
commit 7fdf32342a
8 changed files with 514 additions and 439 deletions

View file

@ -1,12 +1,15 @@
use std::{fmt::Display, ops::RangeInclusive};
use std::{
fmt::Display,
ops::{RangeBounds, RangeInclusive},
};
use clap::builder::PathBufValueParser;
use colored::Colorize;
use termcolor::ColorSpec;
use crate::{belt_finding::brute_force::BruteforceBuilder, misc::Map};
use super::{
common::{Dimension, Direction, PathField, Position, PositionType},
common::{print_map, Dimension, Direction, PathField, Position, PositionType},
Problem, COLORS,
};
@ -18,6 +21,7 @@ struct Field {
pub struct ConflictAvoidance {
map: Map<Field>,
belts: Vec<Vec<PathField>>,
range: Option<(RangeInclusive<i32>, RangeInclusive<i32>)>,
}
#[derive(Debug, PartialEq, Eq)]
@ -89,7 +93,11 @@ impl ConflictAvoidance {
belts.push(p);
}
Self { map, belts }
Self {
map,
belts,
range: None,
}
}
pub fn remove_conflict(&mut self) -> bool {
@ -153,10 +161,12 @@ impl ConflictAvoidance {
loop {
candidates.sort_by_key(|c| -c.area());
let c = candidates.pop().unwrap();
let mut xrange = c.min.x as usize..=c.max.x as usize;
let mut yrange = c.min.y as usize..=c.max.y as usize;
let xrange = c.min.x as usize..=c.max.x as usize;
let yrange = c.min.y as usize..=c.max.y as usize;
println!("x: {:?}, y: {:?}", xrange, yrange);
self.range = Some((c.min.x..=c.max.x, c.min.y..=c.max.y));
// println!("x: {:?}, y: {:?}", xrange, yrange);
let xsize = xrange.end() - xrange.start() + 1;
let ysize = yrange.end() - yrange.start() + 1;
@ -180,63 +190,66 @@ impl ConflictAvoidance {
}
let mut mapping = Vec::new();
let offset = Position::new(*xrange.start() as i32 - 1, *yrange.start() as i32 - 1);
dbg!(&xrange, &yrange);
for (i, path) in self.belts.iter().enumerate() {
let s = path
// index of first PathField where the next position is in the area
let start_index = path
.iter()
.map(|p| p.pos())
.position(|p| {
xrange.contains(&(p.x as usize)) && yrange.contains(&(p.y as usize))
.map(|p| {
let (pos, dir) = p.end_pos();
pos.in_direction(&dir, 1)
})
.map(|i| i.saturating_sub(1));
let e = path
.iter()
.map(|p| p.pos())
.rev()
.position(|p| {
xrange.contains(&(p.x as usize)) && yrange.contains(&(p.y as usize))
})
.map(|i| usize::min(path.len() - 1, path.len() - i));
.position(|pos| {
xrange.contains(&(pos.x as usize)) && yrange.contains(&(pos.y as usize))
});
if let Some((start, mut end)) = s.zip(e) {
// dbg!(start, end);
if matches!(path[end - 1], PathField::Underground { .. }) {
end -= 1;
// index of last PathField that ends inside the area
let end_index = path
.iter()
.rev()
.map(|p| p.end_pos().0)
.position(|pos| {
xrange.contains(&(pos.x as usize)) && yrange.contains(&(pos.y as usize))
})
.map(|rev_i| path.len() - rev_i - 1);
if let Some((start_index, end_index)) = Option::zip(start_index, end_index) {
// dbg!(start_index, end_index, path[start_index], path[end_index]);
let (start_pos, start_dir) = path[start_index].end_pos();
let (end_pos, end_dir) = path[end_index].end_pos();
// edge cases with underground into and end
if let Some(PathField::Underground {
pos,
dir: _,
len: _,
}) = path.get(end_index + 1)
{
if xrange.contains(&(pos.x as usize)) && yrange.contains(&(pos.y as usize))
{
let p = *pos - offset;
println!("Blocked {:?}", p);
b.set_blocked(p.x as usize, p.y as usize, true);
}
}
mapping.push((i, start, end));
b.add_path((start_pos - offset, start_dir), (end_pos - offset, end_dir));
let (start_pos, start_dir) = path[start].end_pos();
let end_pos = path[end].pos();
let end_dir = path[end].dir();
// dbg!(start_pos, end_pos);
// dbg!(i, path[start], path[end]);
b.add_path(
(
Position::new(
start_pos.x - (*xrange.start() as i32 - 1),
start_pos.y - (*yrange.start() as i32 - 1),
),
start_dir,
),
(
Position::new(
end_pos.x - (*xrange.start() as i32 - 1),
end_pos.y - (*yrange.start() as i32 - 1),
),
*end_dir,
),
);
mapping.push((i, start_index, end_index));
}
}
println!("{:?}", mapping);
// println!("{:?}", mapping);
let mut b = b.build();
println!("{}", b);
println!("{}", self);
let mut min_cost = f64::INFINITY;
let mut solutions = Vec::new();
@ -250,26 +263,38 @@ impl ConflictAvoidance {
}
}
println!("{}", b.solution_count());
// dbg!(&solutions);
// println!("{}", b.solution_count());
if b.solution_count() > 0 {
for (p, (index, start, end)) in solutions.iter().zip(mapping) {
let mut t = Vec::new();
// println!("{:?}", p);
t.extend_from_slice(&self.belts[index][0..=start]);
t.extend(p[1..].iter().map(|p| {
p.offset(&Position::new(
(*xrange.start() as i32) - 1,
(*yrange.start() as i32) - 1,
))
}));
t.extend_from_slice(&self.belts[index][end..]);
t.extend(p[1..].iter().map(|p| p.offset(&offset)));
let (end_pos, end_dir) = self.belts[index][end].end_pos();
t.push(PathField::Belt {
pos: end_pos,
dir: end_dir,
});
if end + 1 < self.belts[index].len() {
t.extend_from_slice(&self.belts[index][end + 1..]);
}
// println!("{:?}", &t);
// println!();
// println!("{:?}", &self.belts[index]);
self.belts[index] = t;
}
for (i, path) in self.belts.iter().enumerate() {
if path.iter().any(|p| (p.pos().x == 18) && p.pos().y == 18) {
dbg!(i);
}
}
return true;
} else {
let mut candidate = Candidate::new(
@ -333,29 +358,6 @@ impl ConflictAvoidance {
impl Display for ConflictAvoidance {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let width_digits = self.map.width.ilog10() + 1;
let height_digits = self.map.height.ilog10() + 1;
// print header
for i in 0..width_digits {
let d = width_digits - i - 1;
// padding
for _ in 0..height_digits {
write!(f, " ")?;
}
for x in 0..self.map.width {
let digits = x / (usize::pow(10, d));
if digits == 0 && d > 0 {
write!(f, " ")?;
} else {
write!(f, "{}", char::from_u32((digits % 10) as u32 + 48).unwrap())?;
}
}
writeln!(f)?;
}
let mut m: Map<Option<(usize, &str)>> = Map::new(self.map.width, self.map.height);
for (i, problem) in self.belts.iter().enumerate() {
@ -402,25 +404,57 @@ impl Display for ConflictAvoidance {
m.set(last_pos.x as usize, last_pos.y as usize, Some((i, "T")));
}
// create conflict map
let mut conflicts: Map<usize> = Map::new(self.map.width, self.map.height);
for x in 0..self.map.width {
for y in 0..self.map.height {
if self.map.get(x, y).blocked {
*conflicts.get_mut(x, y) += 1;
}
}
}
for path in &self.belts {
for p in path {
match p {
PathField::Belt { pos, dir: _ } => {
*conflicts.get_mut(pos.x as usize, pos.y as usize) += 1
}
PathField::Underground { pos, dir, len } => {
*conflicts.get_mut(pos.x as usize, pos.y as usize) += 1;
let end = pos.in_direction(dir, *len as PositionType);
*conflicts.get_mut(end.x as usize, end.y as usize) += 1;
}
}
}
}
// Print body
for y in 0..self.map.height {
write!(f, "{:1$}", y, height_digits as usize)?;
print_map(self.map.width as i32, self.map.height as i32, |x, y| {
let mut color = ColorSpec::new();
for x in 0..self.map.width {
if let Some((i, c)) = m.get(x, y) {
write!(f, "{}", c.color(COLORS[*i]))?;
} else if self.map.get(x, y).blocked {
write!(f, "#")?;
} else if x % 8 == 0 || y % 8 == 0 {
write!(f, "")?;
} else {
write!(f, " ")?;
if let Some((xrange, yrange)) = &self.range {
if xrange.contains(&x) && yrange.contains(&y) {
color.set_bg(Some(termcolor::Color::Rgb(96, 96, 0)));
}
}
writeln!(f)?;
}
if conflicts.get(x as usize, y as usize) > &1 {
color.set_bg(Some(termcolor::Color::Black));
}
if let Some((i, c)) = m.get(x as usize, y as usize) {
color.set_fg(Some(COLORS[*i]));
(color, c)
} else if self.map.get(x as usize, y as usize).blocked {
(color, "#")
} else if x % 8 == 0 || y % 8 == 0 {
(color, "")
} else {
(color, " ")
}
});
Ok(())
}