diff --git a/factorio-blueprint-generator/src/factory.rs b/factorio-blueprint-generator/src/factory.rs index 91ca1e0..a546356 100644 --- a/factorio-blueprint-generator/src/factory.rs +++ b/factorio-blueprint-generator/src/factory.rs @@ -4,7 +4,7 @@ use factorio_blueprint::abstraction::{Blueprint, Entity}; use factorio_core::{beltoptions::Beltspeed, prelude::*, visualize::Visualize}; use factorio_layout::{Connection, Interface, LayoutInput, Layouter, MacroBlock}; use factorio_pathfinding::Pathfinder; -use rand::{Rng, seq::IndexedRandom}; +use rand::{Rng, SeedableRng, seq::IndexedRandom}; use serde::{Deserialize, Serialize}; use crate::assembly::assembly_line_2_input; @@ -38,11 +38,11 @@ pub struct FactoryConnection { pub to: usize, } -pub fn generate_factory( +pub fn generate_factory( layouter: &L, pathfinder: &P, factory_graph: FactoryGraph, - rng: &mut impl Rng, + rng: &mut R, ) -> Blueprint { let mut blocks = Vec::new(); let mut blueprints = Vec::new(); diff --git a/factorio-layout/Cargo.toml b/factorio-layout/Cargo.toml index 2e7f2cf..1808a21 100644 --- a/factorio-layout/Cargo.toml +++ b/factorio-layout/Cargo.toml @@ -14,3 +14,4 @@ clap = { version = "4.4.8", features = ["derive"] } miette = { version = "7.2.0", features = ["fancy"] } serde_yaml = "0.9.34" tracing = "0.1.41" +rayon = "1.10.0" diff --git a/factorio-layout/src/genetic_algorithm_v1.rs b/factorio-layout/src/genetic_algorithm_v1.rs index bde1450..1077546 100644 --- a/factorio-layout/src/genetic_algorithm_v1.rs +++ b/factorio-layout/src/genetic_algorithm_v1.rs @@ -1,3 +1,4 @@ +use rayon::iter::{IntoParallelIterator, ParallelIterator}; use tracing::{Level, field::Empty, info, span, trace, warn}; use crate::{ @@ -16,30 +17,39 @@ pub struct GeneticAlgorithm { } impl Layouter for GeneticAlgorithm { - fn layout( + fn layout( &self, input: &crate::LayoutInput, pathfinder: &P, rng: &mut R, - ) -> Option { + ) -> Option + where + R: rand::Rng + rand::SeedableRng + Send + Sync, + P: factorio_pathfinding::Pathfinder + Sync, + { assert!(self.population_new + self.population_keep <= self.population_size); let _complete_span = span!(Level::TRACE, "genetic_algorithm_v1").entered(); - let mut population = Vec::new(); + let mut population: Vec<(LayoutResult, usize)>; + let mut rngs: Vec<_> = (0..self.population_size) + .map(|_| R::from_rng(rng)) + .collect(); { let _initial_generation_span = span!(Level::TRACE, "initial generation").entered(); - for i in 0..self.population_size { - let _layout_span = span!(Level::TRACE, "layout", i).entered(); - loop { - if let Some(l) = self.valid_layout.layout(input, pathfinder, rng) { - let score = score(input, &l); - population.push((l, score)); - break; + population = (0..self.population_size, &mut rngs) + .into_par_iter() + .map(|(i, rng)| { + let _layout_span = span!(Level::TRACE, "layout", i).entered(); + loop { + if let Some(l) = self.valid_layout.layout(input, pathfinder, rng) { + let score = score(input, &l); + break (l, score); + } + warn!("Unable to generate valid layout"); } - warn!("Unable to generate valid layout"); - } - } + }) + .collect(); } population.sort_by_key(|(_, s)| *s); @@ -49,51 +59,55 @@ impl Layouter for GeneticAlgorithm { let generation_span = span!(Level::TRACE, "generation", generation = g, score = Empty).entered(); - { - let _mutate_span = span!(Level::TRACE, "mutate").entered(); - for i in 0..(self.population_size - self.population_keep - self.population_new) { - let _layout_span = span!(Level::TRACE, "layout", i).entered(); - loop { - let parent = &population[i % self.population_keep].0; - if let Some(blocks) = mutate(input, parent, rng) { - let (connections, map) = - path_input_from_blocks_positions(input, parent.size, &blocks); + let (keep, modify) = population.split_at_mut(self.population_keep); - if let Some(paths) = - pathfinder.find_paths(factorio_pathfinding::PathInput { - connections: &connections, - map, - }) - { - let r = LayoutResult { - positions: blocks, - path_result: paths, - size: parent.size, - }; - let score = score(input, &r); - population.push((r, score)); - break; + ( + (0..self.population_size - self.population_keep), + &mut rngs, + modify, + ) + .into_par_iter() + .for_each(|(i, rng, m)| { + if i < self.population_new { + { + let _layout_span = span!(Level::TRACE, "new layout", i).entered(); + loop { + if let Some(l) = self.valid_layout.layout(input, pathfinder, rng) { + let score = score(input, &l); + let _ = std::mem::replace(m, (l, score)); + break; + } + warn!("Unable to generate valid layout"); } } - trace!("unsuccesfull mutation"); - } - } - } + } else { + let _layout_span = span!(Level::TRACE, "layout", i).entered(); + loop { + let parent = &keep[i % keep.len()].0; + if let Some(blocks) = mutate(input, parent, rng) { + let (connections, map) = + path_input_from_blocks_positions(input, parent.size, &blocks); - { - let _new_span = span!(Level::TRACE, "new").entered(); - for i in 0..self.population_new { - let _layout_span = span!(Level::TRACE, "layout", i).entered(); - loop { - if let Some(l) = self.valid_layout.layout(input, pathfinder, rng) { - let score = score(input, &l); - population[self.population_size - self.population_new + i] = (l, score); - break; + if let Some(paths) = + pathfinder.find_paths(factorio_pathfinding::PathInput { + connections: &connections, + map, + }) + { + let r = LayoutResult { + positions: blocks, + path_result: paths, + size: parent.size, + }; + let score = score(input, &r); + let _ = std::mem::replace(m, (r, score)); + break; + } + } + trace!("unsuccesfull mutation"); } - warn!("Unable to generate valid layout"); } - } - } + }); population.sort_by_key(|(_, s)| *s); if population[0].1 < best_result.1 { diff --git a/factorio-layout/src/lib.rs b/factorio-layout/src/lib.rs index 5ed210d..505ad8b 100644 --- a/factorio-layout/src/lib.rs +++ b/factorio-layout/src/lib.rs @@ -5,7 +5,7 @@ use factorio_core::{ visualize::{self, Visualization, Visualize}, }; use factorio_pathfinding::Pathfinder; -use rand::Rng; +use rand::{Rng, SeedableRng}; use serde::{Deserialize, Serialize}; pub mod genetic_algorithm_v1; @@ -51,7 +51,7 @@ pub struct LayoutResult { } pub trait Layouter { - fn layout( + fn layout( &self, input: &LayoutInput, pathfinder: &P,