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::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 {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
#![feature(slice_first_last_chunk)]
|
||||
|
||||
pub mod belt_finding;
|
||||
pub mod blueprint;
|
||||
pub mod graph;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue