diff --git a/examples/layout.rs b/examples/layout.rs index 9fd5c3c..40bc0f0 100644 --- a/examples/layout.rs +++ b/examples/layout.rs @@ -27,15 +27,32 @@ fn main() { // 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); + let m = l.mutate(&mut rng); + m.print_visualization(); - c.remove_all_conflicts(); + let m = m.mutate(&mut rng); + m.print_visualization(); + let m = m.mutate(&mut rng); + m.print_visualization(); + let m = m.mutate(&mut rng); + m.print_visualization(); + let m = m.mutate(&mut rng); + m.print_visualization(); + let m = m.mutate(&mut rng); + m.print_visualization(); + let m = m.mutate(&mut rng); + m.print_visualization(); + let m = m.mutate(&mut rng); + m.print_visualization(); + // let mut p = Problem::from_layout(&l); + // p.print(); + // p.find_path(); + // p.print(); + // let mut c = ConflictAvoidance::new(p); - c.print(); + // c.remove_all_conflicts(); + + // c.print(); // println!("Seed: {i}, Score {}", s); // l.print_visualization(); diff --git a/layout.yml b/layout.yml index 4189f8f..24e98dc 100644 --- a/layout.yml +++ b/layout.yml @@ -1,6 +1,6 @@ size: - x: 15 - y: 15 + x: 10 + y: 10 blocks: - size: x: 3 diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 5bb391f..15b2751 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -1,6 +1,6 @@ use crate::common::visualize::{Color, Symbol, Visualization, Visualize}; use crate::prelude::*; -use rand::Rng; +use rand::{seq::SliceRandom, Rng}; use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize)] @@ -143,11 +143,115 @@ impl Layout<'_> { /// Mutate existing layout, creating a valid layout pub fn mutate(&self, rng: &mut R) -> Self { - let s = self.clone(); + let mut s = self.clone(); + + #[allow(clippy::type_complexity)] + let r: &[(&dyn Fn(&mut Layout, &mut R) -> bool, _)] = &[ + (&Self::mutate_replace::, 30), + (&Self::mutate_flip::, 50), + (&Self::mutate_jiggle::, 80), + ]; + + loop { + let p = r.choose_weighted(rng, |i| i.1).unwrap(); + + if p.0(&mut s, rng) && rng.gen_bool(0.4) { + break; + } + } s } + fn mutate_replace(layout: &mut Layout, rng: &mut R) -> bool { + let i = rng.gen_range(0..layout.blocks.len()); + + let dir = rng.gen::(); + + let b = &layout.problem.blocks[i]; + + let pos = match dir { + Direction::Up => Position::new( + rng.gen_range(0..=(layout.problem.size.x - b.size.x)), + rng.gen_range(0..=(layout.problem.size.y - b.size.y)), + ), + Direction::Right => Position::new( + rng.gen_range((b.size.y - 1)..layout.problem.size.x), + rng.gen_range(0..=(layout.problem.size.y - b.size.x)), + ), + Direction::Down => Position::new( + rng.gen_range((b.size.x - 1)..layout.problem.size.x), + rng.gen_range((b.size.y - 1)..layout.problem.size.y), + ), + Direction::Left => Position::new( + rng.gen_range(0..=(layout.problem.size.x - b.size.y)), + rng.gen_range((b.size.x - 1)..layout.problem.size.y), + ), + }; + + if layout.blocks.iter().enumerate().all(|(j, (p, d))| { + j == i || !Self::collision((&layout.problem.blocks[j], *p, *d), (b, pos, dir)) + }) { + layout.blocks[i] = (pos, dir); + true + } else { + false + } + } + + fn mutate_flip(layout: &mut Layout, rng: &mut R) -> bool { + let i = rng.gen_range(0..layout.blocks.len()); + let b = &mut layout.blocks[i]; + let block = &layout.problem.blocks[i]; + + b.0 = match &b.1 { + Direction::Up => b.0 + block.size - Position::new(1, 1), + Direction::Right => b.0 + Position::new(1 - block.size.y, block.size.x - 1), + Direction::Down => b.0 - block.size + Position::new(1, 1), + Direction::Left => b.0 + Position::new(block.size.y - 1, 1 - block.size.x), + }; + b.1 = b.1.reverse(); + + true + } + + fn mutate_jiggle(layout: &mut Layout, rng: &mut R) -> bool { + let i = rng.gen_range(0..layout.blocks.len()); + let dir = rng.gen::(); + // let step = [(1, 10), (2, 5), (3, 5)] + // .choose_weighted(rng, |i| i.1) + // .unwrap() + // .0; + let step = 1; + + let b = &layout.problem.blocks[i]; + + let new_pos = layout.blocks[i].0.in_direction(&dir, step); + + let (npos, nsize) = Self::normalize_pos((b, new_pos, layout.blocks[i].1)); + + if npos.x < 0 + || npos.y < 0 + || npos.x + nsize.x > layout.problem.size.x + || npos.y + nsize.y > layout.problem.size.y + { + return false; + } + + if layout.blocks.iter().enumerate().all(|(j, (p, d))| { + j == i + || !Self::collision( + (&layout.problem.blocks[j], *p, *d), + (b, new_pos, layout.blocks[i].1), + ) + }) { + layout.blocks[i].0 = new_pos; + true + } else { + false + } + } + fn collision( block1: (&Block, Position, Direction), block2: (&Block, Position, Direction),