Single path brute force
This commit is contained in:
parent
c10843ad2f
commit
2bc323aa58
4 changed files with 413 additions and 2 deletions
39
examples/brute_force.rs
Normal file
39
examples/brute_force.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
326
src/belt_finding/brute_force.rs
Normal file
326
src/belt_finding/brute_force.rs
Normal 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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,7 +6,9 @@ use colored::{Color, Colorize};
|
||||||
use std::fmt::{write, Display};
|
use std::fmt::{write, Display};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
pub mod brute_force;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum Direction {
|
pub enum Direction {
|
||||||
Up,
|
Up,
|
||||||
Right,
|
Right,
|
||||||
|
|
@ -14,6 +16,48 @@ pub enum Direction {
|
||||||
Left,
|
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)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct Position {
|
pub struct Position {
|
||||||
pub x: usize,
|
pub x: usize,
|
||||||
|
|
@ -156,7 +200,7 @@ impl Display for Problem {
|
||||||
write!(f, "{}", "↑".color(COLORS[i]))?;
|
write!(f, "{}", "↑".color(COLORS[i]))?;
|
||||||
}
|
}
|
||||||
} else if self.map.get(x, y).blocked {
|
} else if self.map.get(x, y).blocked {
|
||||||
write!(f, "{}", "#")?;
|
write!(f, "#")?;
|
||||||
} else if x % 8 == 0 || y % 8 == 0 {
|
} else if x % 8 == 0 || y % 8 == 0 {
|
||||||
write!(f, "∙")?;
|
write!(f, "∙")?;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
#![feature(slice_first_last_chunk)]
|
||||||
|
|
||||||
pub mod belt_finding;
|
pub mod belt_finding;
|
||||||
pub mod blueprint;
|
pub mod blueprint;
|
||||||
pub mod graph;
|
pub mod graph;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue