diff --git a/examples/brute_force.rs b/examples/brute_force.rs index b86fc35..0c7bcfa 100644 --- a/examples/brute_force.rs +++ b/examples/brute_force.rs @@ -1,7 +1,10 @@ use std::io; use clap::{Parser, ValueEnum}; -use factorio_blueprint::belt_finding::brute_force::{problems, Bruteforce}; +use factorio_blueprint::{ + belt_finding::brute_force::{problems, Bruteforce}, + common::visualize::Visualize, +}; #[derive(ValueEnum, Clone)] enum Mode { @@ -44,20 +47,20 @@ fn main() { let mut b = args.problem.get_problem(); - b.print(); + b.print_visualization(); match args.mode { Mode::Solutions => { while b.next_finish_state(None) { println!("{}\n{}\n{}", b.count(), b.solution_count(), b.cost()); - b.print(); + b.print_visualization(); } println!("Solutions: {}\nStates: {}", b.solution_count(), b.count()); } Mode::Step => { while b.next_state() { - b.print(); + b.print_visualization(); let mut s = String::new(); let _ = io::stdin().read_line(&mut s); } diff --git a/examples/solve_belt.rs b/examples/solve_belt.rs deleted file mode 100644 index 4ddb73a..0000000 --- a/examples/solve_belt.rs +++ /dev/null @@ -1,87 +0,0 @@ -use clap::{Parser, ValueEnum}; -use factorio_blueprint::belt_finding::{conflict_avoidance::ConflictAvoidance, problems, Problem}; -use std::io; - -#[derive(ValueEnum, Clone)] -enum Mode { - Solve, - ConflictAvoidance, - ConflictStep, -} - -#[derive(ValueEnum, Clone)] -enum ProblemCase { - Simple, - Level1, - Level2, - Level3, - Level5, -} - -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(), - } - } -} - -#[derive(Parser)] -struct Args { - #[arg(value_enum, default_value = "level1")] - problem: ProblemCase, - #[arg(value_enum, default_value = "conflict-avoidance")] - mode: Mode, -} - -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(); - p.find_path(); - p.print(); - p.find_path(); - 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(); - p.find_path(); - p.print(); - p.find_path(); - 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); - } - } - } -} diff --git a/src/belt_finding/brute_force.rs b/src/belt_finding/brute_force.rs index e178a12..833ecf3 100644 --- a/src/belt_finding/brute_force.rs +++ b/src/belt_finding/brute_force.rs @@ -1,12 +1,7 @@ -use std::time::Instant; - -use super::{ - common::{print_map, PathField}, - Position, -}; -use crate::misc::Map; +use super::{common::PathField, Position}; use crate::prelude::*; -use termcolor::ColorSpec; +use crate::{common::visualize::Visualize, misc::Map}; +use std::time::Instant; #[derive(Default, Debug, Clone)] pub struct BruteforceField { @@ -592,83 +587,67 @@ impl Bruteforce { pub fn solution_count(&self) -> u128 { self.solution_count } - pub fn print(&self) { - let mut m: Map> = Map::new(self.map.width, self.map.height); +} - for (i, problem) in self.problems.iter().enumerate() { - if problem.finished { - m.set( - problem.end_pos.x as usize, - problem.end_pos.y as usize, - Some((i, "T")), - ); - } else { - m.set( - problem.end_pos.x as usize, - problem.end_pos.y as usize, - Some((i, "t")), - ); - } - for p in &problem.path { - match p { - PathField::Belt { pos, dir } => match dir { - Direction::Up => m.set(pos.x as usize, pos.y as usize, Some((i, "↑"))), - Direction::Right => m.set(pos.x as usize, pos.y as usize, Some((i, "→"))), - Direction::Down => m.set(pos.x as usize, pos.y as usize, Some((i, "↓"))), - Direction::Left => m.set(pos.x as usize, pos.y as usize, Some((i, "←"))), - }, - PathField::Underground { pos, dir, len } => { - match dir { - Direction::Up => m.set(pos.x as usize, pos.y as usize, Some((i, "↟"))), - Direction::Right => { - m.set(pos.x as usize, pos.y as usize, Some((i, "↠"))) - } - Direction::Down => { - m.set(pos.x as usize, pos.y as usize, Some((i, "↡"))) - } - Direction::Left => { - m.set(pos.x as usize, pos.y as usize, Some((i, "↞"))) - } - }; - let end_pos = pos.in_direction(dir, *len as PositionType); - match dir { - Direction::Up => { - m.set(end_pos.x as usize, end_pos.y as usize, Some((i, "↥"))) - } - Direction::Right => { - m.set(end_pos.x as usize, end_pos.y as usize, Some((i, "↦"))) - } - Direction::Down => { - m.set(end_pos.x as usize, end_pos.y as usize, Some((i, "↧"))) - } - Direction::Left => { - m.set(end_pos.x as usize, end_pos.y as usize, Some((i, "↤"))) - } - }; - } +impl Visualize for Bruteforce { + fn visualize(&self) -> crate::common::visualize::Visualization { + let mut v = crate::common::visualize::Visualization::new(Position::new( + self.map.width as i32, + self.map.height as i32, + )); + + for x in 0..self.map.width { + for y in 0..self.map.height { + if self.map.get(x, y).blocked { + v.add_symbol( + Position::new(x as i32, y as i32), + crate::common::visualize::Symbol::Block, + Some(crate::common::visualize::Color::white()), + None, + ); } } } - // Print body - - let _ = print_map(self.map.width as i32, self.map.height as i32, |x, y| { - if let Some((i, c)) = m.get(x as usize, y as usize) { - let mut color = ColorSpec::new(); - color.set_fg(Some(crate::common::color::COLORS[*i])); - (color, *c) - } else if self.map.get(x as usize, y as usize).blocked { - (ColorSpec::new(), "#") - } else if self.map.get(x as usize, y as usize).underground_horizontal { - (ColorSpec::new(), "_") - } else if self.map.get(x as usize, y as usize).underground_vertical { - (ColorSpec::new(), "|") - } else if x % 8 == 0 || y % 8 == 0 { - (ColorSpec::new(), "∙") - } else { - (ColorSpec::new(), " ") + for (i, problem) in self.problems.iter().enumerate() { + for p in &problem.path { + match p { + PathField::Belt { pos, dir } => { + v.add_symbol( + *pos, + crate::common::visualize::Symbol::Arrow(*dir), + Some(crate::common::visualize::Color::index(i)), + None, + ); + } + PathField::Underground { pos, dir, len } => { + v.add_symbol( + *pos, + crate::common::visualize::Symbol::ArrowEnter(*dir), + Some(crate::common::visualize::Color::index(i)), + None, + ); + v.add_symbol( + pos.in_direction(dir, *len as i32), + crate::common::visualize::Symbol::ArrowExit(*dir), + Some(crate::common::visualize::Color::index(i)), + None, + ); + } + } } - }); + v.add_symbol( + problem.end_pos, + crate::common::visualize::Symbol::Char(match problem.finished { + true => 'T', + false => 't', + }), + Some(crate::common::visualize::Color::index(i)), + None, + ); + } + + v } } diff --git a/src/belt_finding/conflict_avoidance.rs b/src/belt_finding/conflict_avoidance.rs index d1368da..29315d4 100644 --- a/src/belt_finding/conflict_avoidance.rs +++ b/src/belt_finding/conflict_avoidance.rs @@ -1,14 +1,10 @@ -use super::{ - common::{print_map, PathField}, - Problem, -}; -use crate::prelude::*; +use super::{common::PathField, Problem}; use crate::{belt_finding::brute_force::BruteforceBuilder, misc::Map}; +use crate::{common::visualize::Visualize, prelude::*}; use std::{ ops::RangeInclusive, time::{Duration, Instant}, }; -use termcolor::ColorSpec; #[derive(Default)] struct Field { @@ -354,7 +350,7 @@ impl ConflictAvoidance { } // dbg!(&candidates); - while timeout.is_some_and(|t| t < Instant::now()) { + while timeout.is_none_or(|t| t > Instant::now()) { candidates.sort_by_key(|c| -c.area()); // dbg!(&candidates); let c = match candidates.pop() { @@ -499,53 +495,14 @@ impl ConflictAvoidance { } true } +} - pub fn print(&self) { - let mut m: Map> = Map::new(self.map.width, self.map.height); - - for (i, problem) in self.belts.iter().enumerate() { - for p in problem { - match p { - PathField::Belt { pos, dir } => match dir { - Direction::Up => m.set(pos.x as usize, pos.y as usize, Some((i, "↑"))), - Direction::Right => m.set(pos.x as usize, pos.y as usize, Some((i, "→"))), - Direction::Down => m.set(pos.x as usize, pos.y as usize, Some((i, "↓"))), - Direction::Left => m.set(pos.x as usize, pos.y as usize, Some((i, "←"))), - }, - PathField::Underground { pos, dir, len } => { - match dir { - Direction::Up => m.set(pos.x as usize, pos.y as usize, Some((i, "↟"))), - Direction::Right => { - m.set(pos.x as usize, pos.y as usize, Some((i, "↠"))) - } - Direction::Down => { - m.set(pos.x as usize, pos.y as usize, Some((i, "↡"))) - } - Direction::Left => { - m.set(pos.x as usize, pos.y as usize, Some((i, "↞"))) - } - }; - let end_pos = pos.in_direction(dir, *len as PositionType); - match dir { - Direction::Up => { - m.set(end_pos.x as usize, end_pos.y as usize, Some((i, "↥"))) - } - Direction::Right => { - m.set(end_pos.x as usize, end_pos.y as usize, Some((i, "↦"))) - } - Direction::Down => { - m.set(end_pos.x as usize, end_pos.y as usize, Some((i, "↧"))) - } - Direction::Left => { - m.set(end_pos.x as usize, end_pos.y as usize, Some((i, "↤"))) - } - }; - } - } - } - let last_pos = problem.last().unwrap().pos(); - m.set(last_pos.x as usize, last_pos.y as usize, Some((i, "T"))); - } +impl Visualize for ConflictAvoidance { + fn visualize(&self) -> crate::common::visualize::Visualization { + let mut v = crate::common::visualize::Visualization::new(Position::new( + self.map.width as i32, + self.map.height as i32, + )); // create conflict map let mut conflicts: Map = Map::new(self.map.width, self.map.height); @@ -554,6 +511,13 @@ impl ConflictAvoidance { for y in 0..self.map.height { if self.map.get(x, y).blocked { *conflicts.get_mut(x, y) += 1; + + v.add_symbol( + Position::new(x as i32, y as i32), + crate::common::visualize::Symbol::Block, + Some(crate::common::visualize::Color::white()), + None, + ); } } } @@ -572,31 +536,62 @@ impl ConflictAvoidance { } } } - // Print body - let _ = print_map(self.map.width as i32, self.map.height as i32, |x, y| { - let mut color = ColorSpec::new(); - - if let Some((xrange, yrange)) = &self.range { - if xrange.contains(&x) && yrange.contains(&y) { - color.set_bg(Some(termcolor::Color::Rgb(96, 96, 0))); + for (i, path) in self.belts.iter().enumerate() { + for p in path { + match p { + PathField::Belt { pos, dir } => { + v.add_symbol( + *pos, + crate::common::visualize::Symbol::Arrow(*dir), + Some(crate::common::visualize::Color::index(i)), + None, + ); + } + PathField::Underground { pos, dir, len } => { + v.add_symbol( + *pos, + crate::common::visualize::Symbol::ArrowEnter(*dir), + Some(crate::common::visualize::Color::index(i)), + None, + ); + v.add_symbol( + pos.in_direction(dir, *len as i32), + crate::common::visualize::Symbol::ArrowExit(*dir), + Some(crate::common::visualize::Color::index(i)), + None, + ); + } } } + } - if conflicts.get(x as usize, y as usize) > &1 { - color.set_bg(Some(termcolor::Color::Black)); + for x in 0..self.map.width { + for y in 0..self.map.height { + if conflicts.get(x, y) > &1 { + v.overwrite_background( + Position::new(x as i32, y as i32), + Some(crate::common::visualize::Color::new(100, 80, 80)), + ); + } } + } - if let Some((i, c)) = m.get(x as usize, y as usize) { - color.set_fg(Some(crate::common::color::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, " ") + for path in &self.belts { + for p in &path[1..] { + 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; + } + } } - }); + } + + v } } diff --git a/src/belt_finding/mod.rs b/src/belt_finding/mod.rs index b224220..7d3a8be 100644 --- a/src/belt_finding/mod.rs +++ b/src/belt_finding/mod.rs @@ -1,14 +1,11 @@ -use crate::common::color::COLORS; +use crate::common::visualize::Visualize; use crate::graph::wheighted_graph::shortest_path::dijkstra; use crate::graph::wheighted_graph::WheightedGraph; use crate::layout::Layout; use crate::misc::Map; +use crate::prelude::*; use crate::priority_queue::BinaryHeap; use serde::{Deserialize, Serialize}; -use termcolor::ColorSpec; - -use self::common::print_map; -use crate::prelude::*; pub mod brute_force; pub mod common; @@ -132,45 +129,58 @@ impl Problem { // self.map.get_mut(p.0.x as usize, p.0.y as usize).weight = f64::INFINITY; // } } +} - pub fn print(&self) { - let _ = print_map(self.map.width as i32, self.map.height as i32, |x, y| { - let mut color = ColorSpec::new(); - if let Some(i) = self - .start - .iter() - .position(|p| p.0 == Position::new(x as PositionType, y as PositionType)) - { - color.set_fg(Some(COLORS[i])); - (color, "s") - } else if let Some(i) = self - .end - .iter() - .position(|p| p.0 == Position::new(x as PositionType, y as PositionType)) - { - color.set_fg(Some(COLORS[i])); - (color, "t") - } else if let Some((i, p)) = self.path.iter().enumerate().find_map(|(i, v)| { - v.iter() - .position(|p| p.0 == Position::new(x as PositionType, y as PositionType)) - .map(|j| (i, j)) - }) { - color.set_fg(Some(COLORS[i])); - let c = &self.path[i][p]; - match c.1 { - Direction::Up => (color, "↑"), - Direction::Right => (color, "→"), - Direction::Down => (color, "↓"), - Direction::Left => (color, "←"), +impl Visualize for Problem { + fn visualize(&self) -> crate::common::visualize::Visualization { + let mut v = crate::common::visualize::Visualization::new(Position::new( + self.map.width as i32, + self.map.height as i32, + )); + + for x in 0..self.map.width { + for y in 0..self.map.height { + if self.map.get(x, y).blocked { + v.add_symbol( + Position::new(x as i32, y as i32), + crate::common::visualize::Symbol::Block, + Some(crate::common::visualize::Color::white()), + None, + ) } - } else if self.map.get(x as usize, y as usize).blocked { - (color, "#") - } else if x % 8 == 0 || y % 8 == 0 { - (color, "∙") - } else { - (color, " ") } - }); + } + + for (i, (p, _d)) in self.start.iter().enumerate() { + v.add_symbol( + *p, + crate::common::visualize::Symbol::Char('S'), + Some(crate::common::visualize::Color::index(i)), + None, + ) + } + + for (i, (p, _d)) in self.end.iter().enumerate() { + v.add_symbol( + *p, + crate::common::visualize::Symbol::Char('T'), + Some(crate::common::visualize::Color::index(i)), + None, + ) + } + + for (i, p) in self.path.iter().enumerate() { + for (pos, dir) in p { + v.add_symbol( + *pos, + crate::common::visualize::Symbol::Arrow(*dir), + Some(crate::common::visualize::Color::index(i)), + None, + ) + } + } + + v } } diff --git a/src/bin/beltfinding.rs b/src/bin/beltfinding.rs index 7bf9fdb..de00fd4 100644 --- a/src/bin/beltfinding.rs +++ b/src/bin/beltfinding.rs @@ -1,5 +1,8 @@ use clap::{Parser, Subcommand, ValueEnum}; -use factorio_blueprint::belt_finding::{conflict_avoidance::ConflictAvoidance, problems, Problem}; +use factorio_blueprint::{ + belt_finding::{conflict_avoidance::ConflictAvoidance, problems, Problem}, + common::visualize::Visualize, +}; use std::{io, path::PathBuf}; #[derive(ValueEnum, Clone)] @@ -50,28 +53,28 @@ fn main() { match args.mode { Mode::Solve => { - p.print(); + p.print_visualization(); p.find_path(); - p.print(); + p.print_visualization(); } Mode::ConflictAvoidance => { - p.print(); + p.print_visualization(); p.find_path(); - p.print(); + p.print_visualization(); let mut c = ConflictAvoidance::new(&p); - c.print(); + c.print_visualization(); while c.remove_conflict(None) { - c.print(); + c.print_visualization(); } } Mode::ConflictStep => { - p.print(); + p.print_visualization(); p.find_path(); - p.print(); + p.print_visualization(); let mut c = ConflictAvoidance::new(&p); - c.print(); + c.print_visualization(); while c.remove_conflict(None) { - c.print(); + c.print_visualization(); let mut s = String::new(); let _ = io::stdin().read_line(&mut s); } diff --git a/src/common/position.rs b/src/common/position.rs index 68fc71c..56014b4 100644 --- a/src/common/position.rs +++ b/src/common/position.rs @@ -1,8 +1,6 @@ use crate::prelude::*; use serde::{Deserialize, Serialize}; -use super::transformation; - pub type PositionType = i32; #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] diff --git a/src/common/visualize/image.rs b/src/common/visualize/image.rs index 2efee1b..11f5fc0 100644 --- a/src/common/visualize/image.rs +++ b/src/common/visualize/image.rs @@ -1,6 +1,6 @@ use super::Visualization; use crate::{common::visualize::Symbol, prelude::Direction}; -use image::{GenericImage, Pixel, Rgba, RgbaImage}; +use image::{GenericImage, Rgba, RgbaImage}; pub(super) fn draw>>(v: &Visualization, image: &mut I) { assert_eq!( diff --git a/src/common/visualize/mod.rs b/src/common/visualize/mod.rs index 33d1f3b..d0a77cb 100644 --- a/src/common/visualize/mod.rs +++ b/src/common/visualize/mod.rs @@ -80,6 +80,10 @@ impl Color { Self { r, g, b } } + pub fn white() -> Self { + Self::new(255, 255, 255) + } + pub fn index(i: usize) -> Self { let c = [ Color::new(0xe6, 0x00, 0x49), @@ -112,7 +116,19 @@ impl Visualization { fg: Option, bg: Option, ) { - self.symbols.insert(pos, (symbol, fg, bg)); + if let Some(s) = self.symbols.get_mut(&pos) { + *s = (symbol, fg, bg); + } else { + self.symbols.insert(pos, (symbol, fg, bg)); + } + } + + pub fn overwrite_background(&mut self, pos: Position, bg: Option) { + if let Some(s) = self.symbols.get_mut(&pos) { + s.2 = bg; + } else { + self.symbols.insert(pos, (Symbol::Space, None, bg)); + } } pub fn size(&self) -> Position { diff --git a/src/layout/mod.rs b/src/layout/mod.rs index da48966..98c22e1 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -209,7 +209,7 @@ impl<'a> PathLayout<'a> { 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(); + c.print_visualization(); let file = std::fs::File::create(format!( "out/{}.json", OUTFILEINDEX.fetch_add(1, std::sync::atomic::Ordering::Relaxed)