Add beltfinding timeout and debugging.
This commit is contained in:
parent
f20a1841c9
commit
79c8b0c710
13 changed files with 278 additions and 53 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,3 +1,4 @@
|
|||
/target
|
||||
flamegraph.svg
|
||||
perf.data*
|
||||
out
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ macro_rules! bench_bruteforce {
|
|||
b.iter(|| {
|
||||
let mut b = p.clone();
|
||||
|
||||
while b.next_finish_state() {}
|
||||
while b.next_finish_state(None) {}
|
||||
|
||||
b.solution_count()
|
||||
});
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ fn main() {
|
|||
|
||||
match args.mode {
|
||||
Mode::Solutions => {
|
||||
while b.next_finish_state() {
|
||||
while b.next_finish_state(None) {
|
||||
println!("{}\n{}\n{}", b.count(), b.solution_count(), b.cost());
|
||||
b.print();
|
||||
}
|
||||
|
|
@ -64,7 +64,7 @@ fn main() {
|
|||
println!("Solutions: {}\nStates: {}", b.solution_count(), b.count());
|
||||
}
|
||||
Mode::Statistics => {
|
||||
while b.next_finish_state() {}
|
||||
while b.next_finish_state(None) {}
|
||||
|
||||
println!("Solutions: {}\nStates: {}", b.solution_count(), b.count());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,5 @@
|
|||
use clap::Parser;
|
||||
use factorio_blueprint::{
|
||||
belt_finding::{conflict_avoidance::ConflictAvoidance, Problem},
|
||||
common::visualize::Visualize,
|
||||
layout::{GeneticAlgorithm, Layout, PathLayout},
|
||||
};
|
||||
use factorio_blueprint::layout::GeneticAlgorithm;
|
||||
use rand::{rngs::SmallRng, SeedableRng};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
|
|
@ -15,7 +11,7 @@ struct Args {
|
|||
fn main() {
|
||||
let args = Args::parse();
|
||||
|
||||
let file = std::fs::File::open("layout2.yml").unwrap();
|
||||
let file = std::fs::File::open("layout3.yml").unwrap();
|
||||
|
||||
let p = serde_yaml::from_reader(file).unwrap();
|
||||
|
||||
|
|
@ -23,7 +19,7 @@ fn main() {
|
|||
|
||||
dbg!(&p);
|
||||
|
||||
let mut g = GeneticAlgorithm::new(&p, 20, 4, 2, &mut rng);
|
||||
let mut g = GeneticAlgorithm::new(&p, 40, 5, 5, &mut rng);
|
||||
|
||||
for i in 0..100 {
|
||||
println!("Generatrion {i}");
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use clap::{Parser, ValueEnum};
|
||||
use factorio_blueprint::belt_finding::{conflict_avoidance::ConflictAvoidance, problems, Problem};
|
||||
use std::io;
|
||||
use std::{io, path::PathBuf};
|
||||
|
||||
#[derive(ValueEnum, Clone)]
|
||||
enum Mode {
|
||||
|
|
@ -59,9 +59,9 @@ fn main() {
|
|||
p.print();
|
||||
p.find_path();
|
||||
p.print();
|
||||
let mut c = ConflictAvoidance::new(p);
|
||||
let mut c = ConflictAvoidance::new(&p);
|
||||
c.print();
|
||||
while c.remove_conflict() {
|
||||
while c.remove_conflict(None) {
|
||||
c.print();
|
||||
}
|
||||
}
|
||||
|
|
@ -75,9 +75,9 @@ fn main() {
|
|||
p.print();
|
||||
p.find_path();
|
||||
p.print();
|
||||
let mut c = ConflictAvoidance::new(p);
|
||||
let mut c = ConflictAvoidance::new(&p);
|
||||
c.print();
|
||||
while c.remove_conflict() {
|
||||
while c.remove_conflict(None) {
|
||||
c.print();
|
||||
let mut s = String::new();
|
||||
let _ = io::stdin().read_line(&mut s);
|
||||
|
|
|
|||
115
layout3.yml
Normal file
115
layout3.yml
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
size:
|
||||
x: 50
|
||||
y: 50
|
||||
blocks:
|
||||
- size:
|
||||
x: 3
|
||||
y: 2
|
||||
input:
|
||||
- offset:
|
||||
x: 1
|
||||
y: 1
|
||||
dir: Up
|
||||
output:
|
||||
- offset:
|
||||
x: 1
|
||||
y: 0
|
||||
dir: Up
|
||||
- size:
|
||||
x: 5
|
||||
y: 2
|
||||
input:
|
||||
output:
|
||||
- offset:
|
||||
x: 1
|
||||
y: 1
|
||||
dir: Down
|
||||
- size:
|
||||
x: 5
|
||||
y: 7
|
||||
input:
|
||||
- offset:
|
||||
x: 0
|
||||
y: 1
|
||||
dir: Right
|
||||
output:
|
||||
- size:
|
||||
x: 5
|
||||
y: 5
|
||||
input:
|
||||
- offset:
|
||||
x: 0
|
||||
y: 1
|
||||
dir: Right
|
||||
output:
|
||||
- offset:
|
||||
x: 0
|
||||
y: 3
|
||||
dir: Left
|
||||
- size:
|
||||
x: 10
|
||||
y: 10
|
||||
input:
|
||||
- offset:
|
||||
x: 0
|
||||
y: 1
|
||||
dir: Right
|
||||
- offset:
|
||||
x: 0
|
||||
y: 3
|
||||
dir: Right
|
||||
output:
|
||||
- offset:
|
||||
x: 9
|
||||
y: 1
|
||||
dir: Right
|
||||
- offset:
|
||||
x: 9
|
||||
y: 3
|
||||
dir: Right
|
||||
- size:
|
||||
x: 10
|
||||
y: 5
|
||||
input:
|
||||
- offset:
|
||||
x: 0
|
||||
y: 1
|
||||
dir: Right
|
||||
- offset:
|
||||
x: 0
|
||||
y: 3
|
||||
dir: Right
|
||||
output:
|
||||
- offset:
|
||||
x: 9
|
||||
y: 1
|
||||
dir: Right
|
||||
- offset:
|
||||
x: 9
|
||||
y: 3
|
||||
dir: Right
|
||||
connections:
|
||||
- startblock: 1
|
||||
startpoint: 0
|
||||
endblock: 0
|
||||
endpoint: 0
|
||||
- startblock: 0
|
||||
startpoint: 0
|
||||
endblock: 3
|
||||
endpoint: 0
|
||||
- startblock: 3
|
||||
startpoint: 0
|
||||
endblock: 4
|
||||
endpoint: 0
|
||||
- startblock: 4
|
||||
startpoint: 0
|
||||
endblock: 5
|
||||
endpoint: 0
|
||||
- startblock: 4
|
||||
startpoint: 1
|
||||
endblock: 5
|
||||
endpoint: 1
|
||||
- startblock: 5
|
||||
startpoint: 0
|
||||
endblock: 2
|
||||
endpoint: 0
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
use std::time::Instant;
|
||||
|
||||
use super::{
|
||||
common::{print_map, PathField},
|
||||
Position,
|
||||
|
|
@ -548,11 +550,12 @@ impl Bruteforce {
|
|||
self.modify_remove()
|
||||
}
|
||||
|
||||
pub fn next_finish_state(&mut self) -> bool {
|
||||
pub fn next_finish_state(&mut self, timeout: Option<Instant>) -> bool {
|
||||
while self.next_state() {
|
||||
// if self.count % 1000000 == 0 {
|
||||
// println!("{}\n{}", self.count, self);
|
||||
// }
|
||||
if self.count % 10000 == 0 && timeout.is_some_and(|t| t < std::time::Instant::now()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if self.problems.iter().all(|p| p.finished) {
|
||||
self.solution_count += 1;
|
||||
return true;
|
||||
|
|
@ -679,7 +682,7 @@ mod test {
|
|||
fn $i() {
|
||||
let mut b = problems::$i();
|
||||
|
||||
while b.next_finish_state() {}
|
||||
while b.next_finish_state(None) {}
|
||||
|
||||
assert_eq!(b.solution_count(), $x);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,10 @@ use super::{
|
|||
};
|
||||
use crate::prelude::*;
|
||||
use crate::{belt_finding::brute_force::BruteforceBuilder, misc::Map};
|
||||
use std::ops::RangeInclusive;
|
||||
use std::{
|
||||
ops::RangeInclusive,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use termcolor::ColorSpec;
|
||||
|
||||
#[derive(Default)]
|
||||
|
|
@ -68,7 +71,7 @@ impl Candidate {
|
|||
}
|
||||
|
||||
impl ConflictAvoidance {
|
||||
pub fn new(problem: Problem) -> Self {
|
||||
pub fn new(problem: &Problem) -> Self {
|
||||
let mut map: Map<Field> = Map::new(problem.map.width, problem.map.height);
|
||||
for x in 0..problem.map.width {
|
||||
for y in 0..problem.map.height {
|
||||
|
|
@ -130,7 +133,11 @@ impl ConflictAvoidance {
|
|||
&self.belts
|
||||
}
|
||||
|
||||
fn try_bruteforce(&self, candidate: &Candidate) -> Option<Vec<BruteForceEntry>> {
|
||||
fn try_bruteforce(
|
||||
&self,
|
||||
candidate: &Candidate,
|
||||
timeout: Option<Instant>,
|
||||
) -> Option<Vec<BruteForceEntry>> {
|
||||
let xrange = candidate.min.x as usize..=candidate.max.x as usize;
|
||||
let yrange = candidate.min.y as usize..=candidate.max.y as usize;
|
||||
|
||||
|
|
@ -218,9 +225,9 @@ impl ConflictAvoidance {
|
|||
{
|
||||
let p = start_pos - offset;
|
||||
// println!("Blocked {:?}", p);
|
||||
if b.get_blocked(p.x as usize, p.y as usize) {
|
||||
return None;
|
||||
}
|
||||
// if b.get_blocked(p.x as usize, p.y as usize) {
|
||||
// return None;
|
||||
// }
|
||||
b.set_blocked(p.x as usize, p.y as usize, true);
|
||||
}
|
||||
|
||||
|
|
@ -258,7 +265,7 @@ impl ConflictAvoidance {
|
|||
let mut min_cost = f64::INFINITY;
|
||||
let mut solutions = Vec::new();
|
||||
|
||||
while b.next_finish_state() {
|
||||
while b.next_finish_state(timeout) {
|
||||
// println!("{}", b);
|
||||
// b.print();
|
||||
let c = b.cost();
|
||||
|
|
@ -286,7 +293,7 @@ impl ConflictAvoidance {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn remove_conflict(&mut self) -> bool {
|
||||
pub fn remove_conflict(&mut self, timeout: Option<Instant>) -> bool {
|
||||
let mut conflicts: Map<usize> = Map::new(self.map.width, self.map.height);
|
||||
|
||||
for x in 0..self.map.width {
|
||||
|
|
@ -347,7 +354,7 @@ impl ConflictAvoidance {
|
|||
}
|
||||
// dbg!(&candidates);
|
||||
|
||||
loop {
|
||||
while timeout.is_some_and(|t| t < Instant::now()) {
|
||||
candidates.sort_by_key(|c| -c.area());
|
||||
// dbg!(&candidates);
|
||||
let c = match candidates.pop() {
|
||||
|
|
@ -359,7 +366,7 @@ impl ConflictAvoidance {
|
|||
|
||||
self.range = Some((c.min.x..=c.max.x, c.min.y..=c.max.y));
|
||||
|
||||
let result = self.try_bruteforce(&c);
|
||||
let result = self.try_bruteforce(&c, timeout);
|
||||
|
||||
// dbg!(&solutions);
|
||||
|
||||
|
|
@ -442,10 +449,13 @@ impl ConflictAvoidance {
|
|||
candidates.push(candidate);
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn remove_all_conflicts(&mut self) -> bool {
|
||||
while self.remove_conflict() {}
|
||||
pub fn remove_all_conflicts(&mut self, timeout: Option<Duration>) -> bool {
|
||||
let end = timeout.map(|t| std::time::Instant::now() + t);
|
||||
while self.remove_conflict(end) {}
|
||||
|
||||
let mut conflicts: Map<bool> = Map::new(self.map.width, self.map.height);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
use crate::common::color::COLORS;
|
||||
use crate::graph::wheighted_graph::shortest_path::dijkstra;
|
||||
use crate::graph::wheighted_graph::WheightedGraph;
|
||||
use crate::layout::Layout;
|
||||
use crate::misc::Map;
|
||||
use crate::priority_queue::{BinaryHeap, Trace};
|
||||
use crate::{
|
||||
graph::wheighted_graph::shortest_path::dijkstra, priority_queue::fibonacci_heap::FibonacciHeap,
|
||||
};
|
||||
use crate::priority_queue::BinaryHeap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use termcolor::ColorSpec;
|
||||
|
||||
use self::common::print_map;
|
||||
|
|
@ -15,12 +14,13 @@ pub mod brute_force;
|
|||
pub mod common;
|
||||
pub mod conflict_avoidance;
|
||||
|
||||
#[derive(Default, Clone, Copy)]
|
||||
#[derive(Debug, Default, Serialize, Deserialize, Clone, Copy)]
|
||||
pub struct Field {
|
||||
pub blocked: bool,
|
||||
weight: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Problem {
|
||||
map: Map<Field>,
|
||||
start: Vec<(Position, Direction)>,
|
||||
|
|
|
|||
80
src/bin/beltfinding.rs
Normal file
80
src/bin/beltfinding.rs
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
use clap::{Parser, Subcommand, ValueEnum};
|
||||
use factorio_blueprint::belt_finding::{conflict_avoidance::ConflictAvoidance, problems, Problem};
|
||||
use std::{io, path::PathBuf};
|
||||
|
||||
#[derive(ValueEnum, Clone)]
|
||||
enum Mode {
|
||||
Solve,
|
||||
ConflictAvoidance,
|
||||
ConflictStep,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum ProblemCase {
|
||||
Simple,
|
||||
Level1,
|
||||
Level2,
|
||||
Level3,
|
||||
Level5,
|
||||
File { filename: PathBuf },
|
||||
}
|
||||
|
||||
impl ProblemCase {
|
||||
fn get_problem(&self) -> Problem {
|
||||
match self {
|
||||
ProblemCase::Simple => problems::simple(),
|
||||
ProblemCase::Level1 => problems::belt_madness_level_1(),
|
||||
ProblemCase::Level2 => problems::belt_madness_level_2(),
|
||||
ProblemCase::Level3 => problems::belt_madness_level_3(),
|
||||
ProblemCase::Level5 => problems::belt_madness_level_5(),
|
||||
ProblemCase::File { filename } => {
|
||||
let file = std::fs::File::open(filename).unwrap();
|
||||
serde_json::from_reader(file).unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
struct Args {
|
||||
#[arg(value_enum, default_value = "conflict-avoidance")]
|
||||
mode: Mode,
|
||||
#[command(subcommand)]
|
||||
problem: ProblemCase,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = Args::parse();
|
||||
|
||||
let mut p = args.problem.get_problem();
|
||||
|
||||
match args.mode {
|
||||
Mode::Solve => {
|
||||
p.print();
|
||||
p.find_path();
|
||||
p.print();
|
||||
}
|
||||
Mode::ConflictAvoidance => {
|
||||
p.print();
|
||||
p.find_path();
|
||||
p.print();
|
||||
let mut c = ConflictAvoidance::new(&p);
|
||||
c.print();
|
||||
while c.remove_conflict(None) {
|
||||
c.print();
|
||||
}
|
||||
}
|
||||
Mode::ConflictStep => {
|
||||
p.print();
|
||||
p.find_path();
|
||||
p.print();
|
||||
let mut c = ConflictAvoidance::new(&p);
|
||||
c.print();
|
||||
while c.remove_conflict(None) {
|
||||
c.print();
|
||||
let mut s = String::new();
|
||||
let _ = io::stdin().read_line(&mut s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -61,6 +61,10 @@ where
|
|||
G::Node: Eq + Hash + Clone + Debug,
|
||||
G: WheightedGraph,
|
||||
{
|
||||
if start == end {
|
||||
return Some(vec![start]);
|
||||
}
|
||||
|
||||
let mut map: HashMap<G::Node, MapObject<G::Node, P::Handle>> = HashMap::new();
|
||||
|
||||
let mut q = P::new();
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
use std::sync::atomic::AtomicU32;
|
||||
use std::time::Instant;
|
||||
|
||||
use crate::belt_finding::common::PathField;
|
||||
use crate::belt_finding::conflict_avoidance::ConflictAvoidance;
|
||||
use crate::common::visualize::{Color, Symbol, Visualization, Visualize};
|
||||
|
|
@ -5,6 +8,8 @@ use crate::prelude::*;
|
|||
use rand::{seq::SliceRandom, Rng};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
static OUTFILEINDEX: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
pub struct GeneticAlgorithm<'a> {
|
||||
problem: &'a Problem,
|
||||
population: Vec<PathLayout<'a>>,
|
||||
|
|
@ -107,8 +112,8 @@ pub struct Problem {
|
|||
pub(crate) connections: Vec<Connection>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct BlockHandle(usize);
|
||||
// #[derive(Debug, Clone, Copy)]
|
||||
// pub struct BlockHandle(usize);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Layout<'a> {
|
||||
|
|
@ -134,16 +139,25 @@ impl<'a> PathLayout<'a> {
|
|||
|
||||
// println!("Find Path: {:.2}", start.elapsed().as_secs_f32());
|
||||
|
||||
let mut c = ConflictAvoidance::new(p);
|
||||
let mut c = ConflictAvoidance::new(&p);
|
||||
|
||||
// let start = std::time::Instant::now();
|
||||
let start = std::time::Instant::now();
|
||||
|
||||
if !c.remove_all_conflicts() {
|
||||
if !c.remove_all_conflicts(Some(std::time::Duration::from_secs(2))) {
|
||||
if start.elapsed().as_secs_f32() > 0.5 {
|
||||
println!("Conflict avoidance: {:.2}", start.elapsed().as_secs_f32());
|
||||
c.print();
|
||||
let file = std::fs::File::create(format!(
|
||||
"out/{}.json",
|
||||
OUTFILEINDEX.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
|
||||
))
|
||||
.unwrap();
|
||||
serde_json::to_writer(file, &p).unwrap();
|
||||
println!("Saved slow solve.");
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
// println!("Conflict avoidance: {:.2}", start.elapsed().as_secs_f32());
|
||||
|
||||
let paths = c.get_paths().to_vec();
|
||||
|
||||
let score = paths
|
||||
|
|
@ -210,15 +224,15 @@ impl Problem {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn add_block(&mut self, size: Position) -> BlockHandle {
|
||||
self.blocks.push(Block {
|
||||
size,
|
||||
input: Vec::new(),
|
||||
output: Vec::new(),
|
||||
});
|
||||
// pub fn add_block(&mut self, size: Position) -> BlockHandle {
|
||||
// self.blocks.push(Block {
|
||||
// size,
|
||||
// input: Vec::new(),
|
||||
// output: Vec::new(),
|
||||
// });
|
||||
|
||||
BlockHandle(self.blocks.len() - 1)
|
||||
}
|
||||
// BlockHandle(self.blocks.len() - 1)
|
||||
// }
|
||||
|
||||
// pub fn add_connection(
|
||||
// &mut self,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
#[derive(Clone, Debug)]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Map<T> {
|
||||
pub width: usize,
|
||||
pub height: usize,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue