Single path brute force

This commit is contained in:
hal8174 2023-12-11 17:28:58 +01:00
parent c10843ad2f
commit 2bc323aa58
4 changed files with 413 additions and 2 deletions

39
examples/brute_force.rs Normal file
View file

@ -0,0 +1,39 @@
use factorio_blueprint::{
belt_finding::{
brute_force::{Bruteforce, PathField},
Direction, Position,
},
misc::Map,
};
fn main() {
let mut b = Bruteforce {
map: Map::new(5, 10),
path: vec![],
end_pos: Position::new(4, 9),
end_dir: Direction::Up,
count: 0,
solution_count: 0,
};
b.apply_path_field(PathField::Belt {
pos: Position::new(1, 0),
dir: Direction::Down,
});
b.map.get_mut(0, 2).blocked = true;
b.map.get_mut(1, 2).blocked = true;
b.map.get_mut(2, 2).blocked = true;
b.map.get_mut(3, 2).blocked = true;
b.map.get_mut(4, 2).blocked = true;
b.map.get_mut(0, 4).blocked = true;
b.map.get_mut(1, 4).blocked = true;
b.map.get_mut(2, 4).blocked = true;
b.map.get_mut(3, 4).blocked = true;
b.map.get_mut(4, 4).blocked = true;
while b.next_finish_state() {
println!("{}\n{}\n{}", b.count, b.solution_count, b);
}
}

View file

@ -0,0 +1,326 @@
use std::fmt::Display;
use colored::Colorize;
use crate::misc::Map;
use super::{Direction, Position, COLORS};
#[derive(Default)]
pub struct BruteforceField {
pub blocked: bool,
underground_vertical: bool,
underground_horizontal: bool,
}
#[derive(Clone)]
pub enum PathField {
Belt {
pos: Position,
dir: Direction,
},
Underground {
pos: Position,
dir: Direction,
len: u8,
},
}
impl PathField {
fn pos(&self) -> &Position {
match self {
PathField::Belt { pos, dir: _ } => pos,
PathField::Underground {
pos,
dir: _,
len: _,
} => pos,
}
}
fn dir(&self) -> &Direction {
match self {
PathField::Belt { pos: _, dir } => dir,
PathField::Underground {
pos: _,
dir,
len: _,
} => dir,
}
}
}
static MAX_UNDERGROUND_LENGTH: u8 = 5;
pub struct Bruteforce {
pub map: Map<BruteforceField>,
pub path: Vec<PathField>,
pub end_pos: Position,
pub end_dir: Direction,
pub solution_count: u128,
pub count: u128,
}
impl Bruteforce {
pub fn apply_path_field(&mut self, path_field: PathField) {
match &path_field {
PathField::Belt { pos, dir: _ } => self.map.get_mut(pos.x, pos.y).blocked = true,
PathField::Underground { pos, dir, len } => {
self.map.get_mut(pos.x, pos.y).blocked = true;
for i in 1..*len {
let mid_pos = self.pos_in_direction(pos, dir, i.into()).unwrap();
if dir.vertical() {
self.map.get_mut(mid_pos.x, mid_pos.y).underground_vertical = true;
} else {
self.map
.get_mut(mid_pos.x, mid_pos.y)
.underground_horizontal = true;
}
}
let end_pos = self.pos_in_direction(pos, dir, *len as usize).unwrap();
self.map.get_mut(end_pos.x, end_pos.y).blocked = true;
}
}
self.path.push(path_field);
}
fn apply_path_field_remove(&mut self) {
match self.path.pop().unwrap() {
PathField::Belt { pos, dir: _ } => self.map.get_mut(pos.x, pos.y).blocked = false,
PathField::Underground { pos, dir, len } => {
self.map.get_mut(pos.x, pos.y).blocked = false;
for i in 1..len {
let mid_pos = self.pos_in_direction(&pos, &dir, i.into()).unwrap();
if dir.vertical() {
self.map.get_mut(mid_pos.x, mid_pos.y).underground_vertical = false;
} else {
self.map
.get_mut(mid_pos.x, mid_pos.y)
.underground_horizontal = false;
}
}
let end_pos = self.pos_in_direction(&pos, &dir, len.into()).unwrap();
self.map.get_mut(end_pos.x, end_pos.y).blocked = false;
}
}
}
fn check_finish(&self) -> bool {
match self.path.last().unwrap() {
PathField::Belt { pos, dir } => self
.pos_in_direction(pos, dir, 1)
.filter(|p| p == &self.end_pos && dir != &self.end_dir.reverse())
.is_some(),
PathField::Underground { pos, dir, len } => self
.pos_in_direction(pos, dir, *len as usize + 1)
.filter(|p| p == &self.end_pos && dir != &self.end_dir.reverse())
.is_some(),
}
}
fn next_state_internal(&mut self, pos: Position, dir: Direction) -> bool {
if let Some(next_pos) = self
.pos_in_direction(&pos, &dir, 1)
.filter(|p| !self.map.get(p.x, p.y).blocked)
{
self.apply_path_field(PathField::Belt { pos: next_pos, dir });
true
} else {
false
}
}
fn path_field_end(&self, path_field: &PathField) -> (Position, Direction) {
match path_field {
PathField::Belt { pos, dir } => (*pos, *dir),
PathField::Underground { pos, dir, len } => (
self.pos_in_direction(pos, dir, *len as usize).unwrap(),
*dir,
),
}
}
fn modify_underground(&mut self, pos: &Position, dir: &Direction, len: u8) -> bool {
for l in len..MAX_UNDERGROUND_LENGTH {
if let Some(p) = self.pos_in_direction(pos, dir, l as usize) {
if !self.map.get(p.x, p.y).blocked {
self.apply_path_field_remove();
self.apply_path_field(PathField::Underground {
pos: *pos,
dir: *dir,
len: l,
});
return true;
}
}
}
false
}
fn modify_remove(&mut self) -> bool {
if let Some([second_last, last]) = self.path.last_chunk().cloned() {
match last {
PathField::Belt { pos, dir } => {
if second_last.dir() == &dir {
self.apply_path_field_remove();
self.apply_path_field(PathField::Belt {
pos,
dir: second_last.dir().clockwise(),
});
return true;
}
if second_last.dir().clockwise() == dir {
self.apply_path_field_remove();
self.apply_path_field(PathField::Belt {
pos,
dir: second_last.dir().counter_clockwise(),
});
return true;
}
if second_last.dir().counter_clockwise() == dir
&& self.modify_underground(&pos, second_last.dir(), 1)
{
return true;
}
}
PathField::Underground { pos, dir, len } => {
if self.modify_underground(&pos, &dir, len + 1) {
return true;
}
}
}
self.apply_path_field_remove();
self.modify_remove()
} else {
false
}
}
pub fn next_state(&mut self) -> bool {
self.count += 1;
let (pos, dir) = self.path_field_end(self.path.last().unwrap());
// try add
if let Some(next_pos) = self
.pos_in_direction(&pos, &dir, 1)
.filter(|p| !self.map.get(p.x, p.y).blocked)
{
self.apply_path_field(PathField::Belt { pos: next_pos, dir });
return true;
}
// modify and remove
self.modify_remove()
}
pub fn next_finish_state(&mut self) -> bool {
self.solution_count += 1;
while self.next_state() {
if self.check_finish() {
return true;
}
}
false
}
fn pos_in_direction(&self, pos: &Position, dir: &Direction, len: usize) -> Option<Position> {
match dir {
Direction::Up => pos.y.checked_sub(len).map(|y| Position::new(pos.x, y)),
Direction::Right => Some(pos.x + len)
.filter(|&x| x < self.map.width)
.map(|x| Position::new(x, pos.y)),
Direction::Down => Some(pos.y + len)
.filter(|&y| y < self.map.height)
.map(|y| Position::new(pos.x, y)),
Direction::Left => pos.x.checked_sub(len).map(|x| Position::new(x, pos.y)),
}
}
}
impl Display for Bruteforce {
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 p in &self.path {
match p {
PathField::Belt { pos, dir } => match dir {
Direction::Up => m.set(pos.x, pos.y, Some((0, ""))),
Direction::Right => m.set(pos.x, pos.y, Some((0, ""))),
Direction::Down => m.set(pos.x, pos.y, Some((0, ""))),
Direction::Left => m.set(pos.x, pos.y, Some((0, ""))),
},
PathField::Underground { pos, dir, len } => {
match dir {
Direction::Up => m.set(pos.x, pos.y, Some((0, ""))),
Direction::Right => m.set(pos.x, pos.y, Some((0, ""))),
Direction::Down => m.set(pos.x, pos.y, Some((0, ""))),
Direction::Left => m.set(pos.x, pos.y, Some((0, ""))),
};
let end_pos = self.pos_in_direction(pos, dir, *len as usize).unwrap();
match dir {
Direction::Up => m.set(end_pos.x, end_pos.y, Some((0, ""))),
Direction::Right => m.set(end_pos.x, end_pos.y, Some((0, ""))),
Direction::Down => m.set(end_pos.x, end_pos.y, Some((0, ""))),
Direction::Left => m.set(end_pos.x, end_pos.y, Some((0, ""))),
};
}
}
}
// Print body
for y in 0..self.map.height {
write!(f, "{:1$}", y, height_digits as usize)?;
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, " ")?;
}
}
writeln!(f)?;
}
Ok(())
}
}

View file

@ -6,7 +6,9 @@ use colored::{Color, Colorize};
use std::fmt::{write, Display};
use std::io::Write;
#[derive(Clone, Copy)]
pub mod brute_force;
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum Direction {
Up,
Right,
@ -14,6 +16,48 @@ pub enum Direction {
Left,
}
impl Direction {
fn vertical(&self) -> bool {
match self {
Direction::Up => true,
Direction::Right => false,
Direction::Down => true,
Direction::Left => false,
}
}
fn horizontal(&self) -> bool {
!self.vertical()
}
fn reverse(&self) -> Self {
match self {
Direction::Up => Direction::Down,
Direction::Right => Direction::Left,
Direction::Down => Direction::Up,
Direction::Left => Direction::Right,
}
}
fn clockwise(&self) -> Self {
match self {
Direction::Up => Direction::Right,
Direction::Right => Direction::Down,
Direction::Down => Direction::Left,
Direction::Left => Direction::Up,
}
}
fn counter_clockwise(&self) -> Self {
match self {
Direction::Up => Direction::Left,
Direction::Right => Direction::Up,
Direction::Down => Direction::Right,
Direction::Left => Direction::Down,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Position {
pub x: usize,
@ -156,7 +200,7 @@ impl Display for Problem {
write!(f, "{}", "".color(COLORS[i]))?;
}
} else if self.map.get(x, y).blocked {
write!(f, "{}", "#")?;
write!(f, "#")?;
} else if x % 8 == 0 || y % 8 == 0 {
write!(f, "")?;
} else {

View file

@ -1,3 +1,5 @@
#![feature(slice_first_last_chunk)]
pub mod belt_finding;
pub mod blueprint;
pub mod graph;