From c572fcb0e2926df4620aae6a4250599fbf7a5a82 Mon Sep 17 00:00:00 2001 From: hal8174 Date: Sat, 24 Aug 2024 13:59:57 +0200 Subject: [PATCH 1/2] Add visualization interface --- examples/layout.rs | 9 +- src/common/mod.rs | 1 + src/common/visualize.rs | 181 ++++++++++++++++++++++++++++++++++++++++ src/layout/mod.rs | 34 ++++---- 4 files changed, 202 insertions(+), 23 deletions(-) create mode 100644 src/common/visualize.rs diff --git a/examples/layout.rs b/examples/layout.rs index d16e88e..d43ab14 100644 --- a/examples/layout.rs +++ b/examples/layout.rs @@ -1,4 +1,4 @@ -use factorio_blueprint::layout::Layout; +use factorio_blueprint::{common::visualize::Visualize, layout::Layout}; use rand::SeedableRng; fn main() { @@ -15,14 +15,13 @@ fn main() { let p = serde_yaml::from_reader(file).unwrap(); - dbg!(&p); - - for i in 0..1 { + for i in 0..100 { let mut rng = rand::rngs::SmallRng::seed_from_u64(i); let l = Layout::new(&p, &mut rng); println!("Seed: {i}, Score {}", l.score()); - l.print(); + + l.print_visualization(); } } diff --git a/src/common/mod.rs b/src/common/mod.rs index 30537fe..a4a8298 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -1,3 +1,4 @@ pub mod color; pub mod direction; pub mod position; +pub mod visualize; diff --git a/src/common/visualize.rs b/src/common/visualize.rs new file mode 100644 index 0000000..2ff7ba1 --- /dev/null +++ b/src/common/visualize.rs @@ -0,0 +1,181 @@ +use std::collections::HashMap; + +use std::io::Write; +use termcolor::{ColorSpec, StandardStream, WriteColor}; + +use crate::prelude::*; + +pub trait Visualize { + fn visualize(&self) -> Visualization; + + fn print_visualization(&self) { + let v = self.visualize(); + + let stdout = &mut StandardStream::stdout(termcolor::ColorChoice::Always); + + let width_digits = (v.size.x - 1).ilog10() + 1; + let height_digits = (v.size.y - 1).ilog10() + 1; + + // print header + for i in 0..width_digits { + let d = width_digits - i - 1; + + //padding + for _ in 0..height_digits { + write!(stdout, " ").unwrap(); + } + + for x in 0..v.size.x { + let digits = x / (i32::pow(10, d)); + if digits == 0 && d > 0 { + write!(stdout, " ").unwrap(); + } else { + write!( + stdout, + "{}", + char::from_u32((digits % 10) as u32 + 48).unwrap() + ) + .unwrap(); + } + } + writeln!(stdout).unwrap(); + } + + for y in 0..v.size.y { + write!(stdout, "{:1$}", y, height_digits as usize).unwrap(); + + for x in 0..v.size.x { + if let Some((s, fg, bg)) = v.symbols.get(&Position::new(x, y)) { + let mut c = ColorSpec::new(); + c.set_fg(fg.as_ref().map(|c| termcolor::Color::Rgb(c.r, c.g, c.b))); + c.set_bg(bg.as_ref().map(|c| termcolor::Color::Rgb(c.r, c.g, c.b))); + stdout.set_color(&c).unwrap(); + write!(stdout, "{:1}", s.get_str()).unwrap(); + stdout.reset().unwrap(); + } else { + write!(stdout, " ").unwrap(); + } + } + + writeln!(stdout, "{:1$}", y, height_digits as usize).unwrap(); + } + + for i in 0..width_digits { + let d = width_digits - i - 1; + + //padding + for _ in 0..height_digits { + write!(stdout, " ").unwrap(); + } + + for x in 0..v.size.x { + let digits = x / (i32::pow(10, d)); + if digits == 0 && d > 0 { + write!(stdout, " ").unwrap(); + } else { + write!( + stdout, + "{}", + char::from_u32((digits % 10) as u32 + 48).unwrap() + ) + .unwrap(); + } + } + writeln!(stdout).unwrap(); + } + } +} + +pub enum Symbol { + Arrow(Direction), + ArrowEnter(Direction), + ArrowExit(Direction), + Char(&'static str), + Block, + Space, +} + +impl Symbol { + fn get_str(&self) -> &'static str { + match self { + Symbol::Arrow(dir) => match dir { + Direction::Up => "↑", + Direction::Right => "→", + Direction::Down => "↓", + Direction::Left => "←", + }, + Symbol::ArrowEnter(dir) => match dir { + Direction::Up => "↟", + Direction::Right => "↠", + Direction::Down => "↡", + Direction::Left => "↞", + }, + Symbol::ArrowExit(dir) => match dir { + Direction::Up => "↥", + Direction::Right => "↦", + Direction::Down => "↧", + Direction::Left => "↤", + }, + Symbol::Char(c) => c.split_at(1).0, + Symbol::Block => "#", + Symbol::Space => " ", + } + } + + fn get_char(&self) -> char { + self.get_str().chars().next().unwrap() + } +} + +pub struct Visualization { + size: Position, + symbols: HashMap, Option)>, +} + +#[derive(Debug, Clone, Copy)] +pub struct Color { + r: u8, + g: u8, + b: u8, +} + +impl Color { + pub fn new(r: u8, g: u8, b: u8) -> Self { + Self { r, g, b } + } + + pub fn index(i: usize) -> Self { + let c = [ + Color::new(0xe6, 0x00, 0x49), + Color::new(0x0b, 0xb4, 0xff), + Color::new(0x50, 0xe9, 0x91), + Color::new(0xe6, 0xd8, 0x00), + Color::new(0x9b, 0x19, 0xf5), + Color::new(0xff, 0xa3, 0x00), + Color::new(0xdc, 0x0a, 0xb4), + Color::new(0xb3, 0xd4, 0xff), + Color::new(0x00, 0xbf, 0xa0), + ]; + + c[i % c.len()] + } +} + +impl Visualization { + pub fn new(size: Position) -> Self { + Self { + size, + symbols: HashMap::new(), + } + } + + pub fn add_symbol( + &mut self, + pos: Position, + symbol: Symbol, + fg: Option, + bg: Option, + ) { + self.symbols.insert(pos, (symbol, fg, bg)); + } +} diff --git a/src/layout/mod.rs b/src/layout/mod.rs index b3f1ae4..e1c0eae 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -1,3 +1,4 @@ +use crate::common::visualize::{Color, Symbol, Visualization, Visualize}; use crate::prelude::*; use crate::{belt_finding::common::print_map, misc::Map}; @@ -36,7 +37,7 @@ pub struct Problem { #[derive(Debug, Clone, Copy)] pub struct BlockHandle(usize); -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Layout<'a> { problem: &'a Problem, blocks: Vec<(Position, Direction)>, @@ -145,7 +146,9 @@ impl Layout<'_> { /// Mutate existing layout, creating a valid layout pub fn mutate(&self, rng: &mut R) -> Self { - todo!() + let s = self.clone(); + + s } fn collision( @@ -208,10 +211,11 @@ impl Layout<'_> { Direction::Left => pos + Position::new(offset.y, -offset.x), } } +} - pub fn print(&self) { - let mut m: Map> = - Map::new(self.problem.size.x as usize, self.problem.size.y as usize); +impl<'a> Visualize for Layout<'a> { + fn visualize(&self) -> Visualization { + let mut v = Visualization::new(self.problem.size); for (i, ((p, d), b)) in self .blocks @@ -219,39 +223,33 @@ impl Layout<'_> { .zip(self.problem.blocks.iter()) .enumerate() { + let c = Color::index(i); + let (npos, nsize) = Self::normalize_pos((b, *p, *d)); for x in npos.x..(npos.x + nsize.x) { for y in npos.y..(npos.y + nsize.y) { - m.set(x as usize, y as usize, Some((i, "#"))); + v.add_symbol(Position::new(x, y), Symbol::Block, Some(c), None); } } let pos = Self::transform(*p, *d, Position::new(0, 0)); - m.set(pos.x as usize, pos.y as usize, Some((i, "X"))); + v.add_symbol(pos, Symbol::Char("X"), Some(c), None); for input in &b.input { let pos = Self::transform(*p, *d, input.offset); - m.set(pos.x as usize, pos.y as usize, Some((i, "i"))); + v.add_symbol(pos, Symbol::Char("i"), Some(c), None); } for output in &b.output { let pos = Self::transform(*p, *d, output.offset); - m.set(pos.x as usize, pos.y as usize, Some((i, "o"))); + v.add_symbol(pos, Symbol::Char("o"), Some(c), None); } } - let _ = print_map(self.problem.size.x, self.problem.size.y, |x, y| { - if let Some(i) = m.get(x as usize, y as usize) { - let mut color = ColorSpec::new(); - color.set_fg(Some(crate::common::color::COLORS[i.0])); - (color, i.1) - } else { - (ColorSpec::new(), " ") - } - }); + v } } From 5a6be3194e53911cdc990e586d0fa539610417e2 Mon Sep 17 00:00:00 2001 From: hal8174 Date: Sun, 25 Aug 2024 00:08:02 +0200 Subject: [PATCH 2/2] Add belt finding to layouting --- examples/layout.rs | 30 +++++++++++++++++---- layout.yml | 12 ++++++--- src/belt_finding/mod.rs | 44 ++++++++++++++++++++++++++++++ src/layout/mod.rs | 59 ++++++++++++++++++++++++++--------------- 4 files changed, 114 insertions(+), 31 deletions(-) diff --git a/examples/layout.rs b/examples/layout.rs index d43ab14..9fd5c3c 100644 --- a/examples/layout.rs +++ b/examples/layout.rs @@ -1,4 +1,8 @@ -use factorio_blueprint::{common::visualize::Visualize, layout::Layout}; +use factorio_blueprint::{ + belt_finding::{conflict_avoidance::ConflictAvoidance, Problem}, + common::visualize::Visualize, + layout::Layout, +}; use rand::SeedableRng; fn main() { @@ -15,13 +19,29 @@ fn main() { let p = serde_yaml::from_reader(file).unwrap(); - for i in 0..100 { - let mut rng = rand::rngs::SmallRng::seed_from_u64(i); + for i in 0..1 { + let mut rng = rand::rngs::SmallRng::seed_from_u64(5); let l = Layout::new(&p, &mut rng); - println!("Seed: {i}, Score {}", l.score()); - + // let s = l.score(); l.print_visualization(); + + let mut p = Problem::from_layout(&l); + p.print(); + p.find_path(); + p.print(); + let mut c = ConflictAvoidance::new(p); + + c.remove_all_conflicts(); + + c.print(); + // println!("Seed: {i}, Score {}", s); + + // l.print_visualization(); + // if s < min { + // min = s; + // min_l = Some(l); + // } } } diff --git a/layout.yml b/layout.yml index 8b10714..4189f8f 100644 --- a/layout.yml +++ b/layout.yml @@ -1,6 +1,6 @@ size: - x: 10 - y: 10 + x: 15 + y: 15 blocks: - size: x: 3 @@ -9,7 +9,7 @@ blocks: - offset: x: 1 y: 1 - dir: Down + dir: Up output: - offset: x: 1 @@ -31,10 +31,14 @@ blocks: - offset: x: 0 y: 1 - dir: Left + dir: Right output: connections: - startblock: 1 startpoint: 0 endblock: 0 endpoint: 0 + - startblock: 0 + startpoint: 0 + endblock: 2 + endpoint: 0 diff --git a/src/belt_finding/mod.rs b/src/belt_finding/mod.rs index e8e6269..0648f41 100644 --- a/src/belt_finding/mod.rs +++ b/src/belt_finding/mod.rs @@ -1,5 +1,6 @@ use crate::common::color::COLORS; use crate::graph::wheighted_graph::WheightedGraph; +use crate::layout::Layout; use crate::misc::Map; use crate::{ graph::wheighted_graph::shortest_path::dijkstra, priority_queue::fibonacci_heap::FibonacciHeap, @@ -36,6 +37,49 @@ impl Problem { } } + pub fn from_layout(l: &Layout) -> Self { + let mut p = Self::new(l.problem.size.x as usize, l.problem.size.y as usize); + + for ((pos, dir), b) in l.blocks.iter().zip(l.problem.blocks.iter()) { + let (npos, nsize) = Layout::normalize_pos((b, *pos, *dir)); + + let nend = npos + nsize - Position::new(1, 1); + + p.set_blocked_range( + npos.x as usize, + npos.y as usize, + nend.x as usize, + nend.y as usize, + true, + ); + } + + for c in &l.problem.connections { + let startpos = Layout::transform( + l.blocks[c.startblock].0, + l.blocks[c.startblock].1, + l.problem.blocks[c.startblock].output[c.startpoint].offset, + ); + let startdir = Layout::rotate( + l.problem.blocks[c.startblock].output[c.startpoint].dir, + l.blocks[c.startblock].1, + ); + let enddir = Layout::rotate( + l.problem.blocks[c.endblock].input[c.endpoint].dir, + l.blocks[c.endblock].1, + ); + let endpos = Layout::transform( + l.blocks[c.endblock].0, + l.blocks[c.endblock].1, + l.problem.blocks[c.endblock].input[c.endpoint].offset, + ) + .in_direction(&enddir, -1); + p.add_connection((startpos, startdir), (endpos, enddir)); + } + + p + } + pub fn add_connection(&mut self, start: (Position, Direction), end: (Position, Direction)) { self.start.push(start); self.end.push(end); diff --git a/src/layout/mod.rs b/src/layout/mod.rs index e1c0eae..5bb391f 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -1,37 +1,34 @@ use crate::common::visualize::{Color, Symbol, Visualization, Visualize}; use crate::prelude::*; -use crate::{belt_finding::common::print_map, misc::Map}; - use rand::Rng; use serde::{Deserialize, Serialize}; -use termcolor::ColorSpec; #[derive(Debug, Serialize, Deserialize)] -struct Block { - size: Position, - input: Vec, - output: Vec, +pub(crate) struct Block { + pub(crate) size: Position, + pub(crate) input: Vec, + pub(crate) output: Vec, } #[derive(Debug, Serialize, Deserialize)] -struct Interface { - offset: Position, - dir: Direction, +pub(crate) struct Interface { + pub(crate) offset: Position, + pub(crate) dir: Direction, } #[derive(Debug, Serialize, Deserialize)] -struct Connection { - startblock: usize, - startpoint: usize, - endblock: usize, - endpoint: usize, +pub(crate) struct Connection { + pub(crate) startblock: usize, + pub(crate) startpoint: usize, + pub(crate) endblock: usize, + pub(crate) endpoint: usize, } #[derive(Debug, Serialize, Deserialize)] pub struct Problem { - size: Position, - blocks: Vec, - connections: Vec, + pub(crate) size: Position, + pub(crate) blocks: Vec, + pub(crate) connections: Vec, } #[derive(Debug, Clone, Copy)] @@ -39,8 +36,8 @@ pub struct BlockHandle(usize); #[derive(Debug, Clone)] pub struct Layout<'a> { - problem: &'a Problem, - blocks: Vec<(Position, Direction)>, + pub(crate) problem: &'a Problem, + pub(crate) blocks: Vec<(Position, Direction)>, } impl Problem { @@ -164,7 +161,7 @@ impl Layout<'_> { && npos1.y + nsize1.y > npos2.y } - fn normalize_pos(block: (&Block, Position, Direction)) -> (Position, Position) { + pub(crate) fn normalize_pos(block: (&Block, Position, Direction)) -> (Position, Position) { let npos = match block.2 { Direction::Up => block.1, Direction::Right => block.1.in_direction(&Direction::Left, block.0.size.y - 1), @@ -203,7 +200,7 @@ impl Layout<'_> { sum } - fn transform(pos: Position, dir: Direction, offset: Position) -> Position { + pub(crate) fn transform(pos: Position, dir: Direction, offset: Position) -> Position { match dir { Direction::Up => pos + offset, Direction::Right => pos + Position::new(-offset.y, offset.x), @@ -211,6 +208,24 @@ impl Layout<'_> { Direction::Left => pos + Position::new(offset.y, -offset.x), } } + + pub(crate) fn rotate(dir: Direction, rot: Direction) -> Direction { + match (rot, dir) { + (Direction::Up, _) => dir, + (Direction::Right, Direction::Up) => Direction::Right, + (Direction::Right, Direction::Right) => Direction::Down, + (Direction::Right, Direction::Down) => Direction::Left, + (Direction::Right, Direction::Left) => Direction::Up, + (Direction::Down, Direction::Up) => Direction::Down, + (Direction::Down, Direction::Right) => Direction::Left, + (Direction::Down, Direction::Down) => Direction::Up, + (Direction::Down, Direction::Left) => Direction::Right, + (Direction::Left, Direction::Up) => Direction::Left, + (Direction::Left, Direction::Right) => Direction::Up, + (Direction::Left, Direction::Down) => Direction::Right, + (Direction::Left, Direction::Left) => Direction::Down, + } + } } impl<'a> Visualize for Layout<'a> {