From cebdee4ec7f858f7b75948dd4e364e6affd11356 Mon Sep 17 00:00:00 2001 From: hal8174 Date: Wed, 12 Feb 2025 22:19:00 +0100 Subject: [PATCH] Add tracing support for layouting --- Cargo.lock | 166 +++++++++++++++++- factorio-blueprint-generator/Cargo.toml | 2 + .../src/bin/generate_factory.rs | 16 +- factorio-layout/Cargo.toml | 1 + factorio-layout/src/genetic_algorithm_v1.rs | 89 ++++++---- factorio-pathfinding/Cargo.toml | 1 + .../src/belt_finding/conflict_avoidance.rs | 2 + factorio-pathfinding/src/belt_finding/mod.rs | 30 ++-- 8 files changed, 254 insertions(+), 53 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2e7c469..563efef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -556,6 +556,8 @@ dependencies = [ "serde", "serde_json", "serde_yaml", + "tracing", + "tracing-subscriber", ] [[package]] @@ -585,6 +587,7 @@ dependencies = [ "serde", "serde_json", "serde_yaml", + "tracing", ] [[package]] @@ -606,6 +609,7 @@ dependencies = [ "serde_json", "serde_yaml", "termcolor", + "tracing", ] [[package]] @@ -893,6 +897,15 @@ dependencies = [ "imgref", ] +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "maybe-rayon" version = "0.1.1" @@ -978,6 +991,16 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -1049,6 +1072,12 @@ version = "11.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "owo-colors" version = "4.1.0" @@ -1061,6 +1090,12 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + [[package]] name = "pkg-config" version = "0.3.31" @@ -1169,7 +1204,7 @@ dependencies = [ "rand 0.8.5", "rand_chacha 0.3.1", "rand_xorshift", - "regex-syntax", + "regex-syntax 0.8.5", "rusty-fork", "tempfile", "unarray", @@ -1364,8 +1399,17 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", ] [[package]] @@ -1376,9 +1420,15 @@ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.5", ] +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + [[package]] name = "regex-syntax" version = "0.8.5" @@ -1497,6 +1547,15 @@ dependencies = [ "unsafe-libyaml", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shlex" version = "1.3.0" @@ -1644,6 +1703,16 @@ dependencies = [ "syn", ] +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "tiff" version = "0.9.1" @@ -1699,6 +1768,67 @@ dependencies = [ "winnow", ] +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + [[package]] name = "unarray" version = "0.1.4" @@ -1746,6 +1876,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + [[package]] name = "version-compare" version = "0.2.0" @@ -1860,6 +1996,22 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + [[package]] name = "winapi-util" version = "0.1.9" @@ -1869,6 +2021,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-sys" version = "0.59.0" diff --git a/factorio-blueprint-generator/Cargo.toml b/factorio-blueprint-generator/Cargo.toml index de77e98..ec65a98 100644 --- a/factorio-blueprint-generator/Cargo.toml +++ b/factorio-blueprint-generator/Cargo.toml @@ -13,3 +13,5 @@ serde_json = "1.0.135" serde_yaml = "0.9.34" clap = { version = "4.5.26", features = ["derive"] } rand = { version = "0.9.0", features = ["small_rng"] } +tracing = "0.1.41" +tracing-subscriber = { version = "0.3.19", features = ["env-filter"] } diff --git a/factorio-blueprint-generator/src/bin/generate_factory.rs b/factorio-blueprint-generator/src/bin/generate_factory.rs index 502800b..38f0a64 100644 --- a/factorio-blueprint-generator/src/bin/generate_factory.rs +++ b/factorio-blueprint-generator/src/bin/generate_factory.rs @@ -7,6 +7,7 @@ use factorio_core::prelude::*; use factorio_layout::{genetic_algorithm_v1::GeneticAlgorithm, valid_layout::ValidLayout}; use factorio_pathfinding::belt_finding::ConflictAvoidance; use rand::{SeedableRng, rngs::SmallRng}; +use tracing_subscriber::{EnvFilter, fmt::format::FmtSpan}; #[derive(Parser)] struct Args { @@ -21,6 +22,11 @@ struct Args { fn main() { let args = Args::parse(); + tracing_subscriber::fmt::fmt() + .with_env_filter(EnvFilter::from_default_env()) + .with_span_events(FmtSpan::NEW | FmtSpan::CLOSE) + .init(); + let text = std::fs::File::open(&args.path).unwrap(); let factory_graph: FactoryGraph = serde_yaml::from_reader(text).unwrap(); @@ -35,10 +41,10 @@ fn main() { let l = GeneticAlgorithm { mutation_retries: 20, - population_size: 5, - population_keep: 8, - population_new: 2, - generations: 5, + population_size: 20, + population_keep: 4, + population_new: 4, + generations: 40, valid_layout: l, }; let p = ConflictAvoidance { @@ -55,5 +61,5 @@ fn main() { println!("{}", serde_json::to_string_pretty(&b).unwrap()); } - println!("{}", encode(&serde_json::to_string(&b).unwrap())); + let _ = std::fs::write("out.bp", encode(&serde_json::to_string(&b).unwrap())); } diff --git a/factorio-layout/Cargo.toml b/factorio-layout/Cargo.toml index 3b9ad4e..2e7f2cf 100644 --- a/factorio-layout/Cargo.toml +++ b/factorio-layout/Cargo.toml @@ -13,3 +13,4 @@ serde_json = "1.0.108" clap = { version = "4.4.8", features = ["derive"] } miette = { version = "7.2.0", features = ["fancy"] } serde_yaml = "0.9.34" +tracing = "0.1.41" diff --git a/factorio-layout/src/genetic_algorithm_v1.rs b/factorio-layout/src/genetic_algorithm_v1.rs index 6d08c2f..6e13418 100644 --- a/factorio-layout/src/genetic_algorithm_v1.rs +++ b/factorio-layout/src/genetic_algorithm_v1.rs @@ -1,3 +1,5 @@ +use tracing::{Level, info, span, trace, warn}; + use crate::{ LayoutResult, Layouter, misc::{mutate, path_input_from_blocks_positions, score}, @@ -20,14 +22,23 @@ impl Layouter for GeneticAlgorithm { pathfinder: &P, rng: &mut R, ) -> Option { + assert!(self.population_new > 0); + 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(); - for _ in 0..self.population_size { - loop { - if let Some(l) = self.valid_layout.layout(input, pathfinder, rng) { - let score = score(input, &l); - population.push((l, score)); - break; + { + 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; + } + warn!("Unable to generate valid layout"); } } } @@ -36,38 +47,50 @@ impl Layouter for GeneticAlgorithm { let mut best_result = population[0].clone(); for g in 0..self.generations { - for i in 0..(self.population_size - self.population_keep - self.population_new) { - 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 _generation_span = span!(Level::TRACE, "generation", g).entered(); - 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; + { + 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); + + 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; + } } + trace!("unsuccesfull mutation"); } } } - for i in 0..self.population_new { - 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; + { + 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; + } + warn!("Unable to generate valid layout"); } } } @@ -76,7 +99,7 @@ impl Layouter for GeneticAlgorithm { if population[0].1 < best_result.1 { best_result = population[0].clone(); } - eprintln!("completed generation {g}\nscore: {}", population[0].1); + info!("completed generation {g} best score: {}", population[0].1); } Some(best_result.0) diff --git a/factorio-pathfinding/Cargo.toml b/factorio-pathfinding/Cargo.toml index a9566b1..278445c 100644 --- a/factorio-pathfinding/Cargo.toml +++ b/factorio-pathfinding/Cargo.toml @@ -28,3 +28,4 @@ serde = { version = "1.0.192", features = ["derive"] } serde_json = "1.0.108" serde_yaml = "0.9.34" termcolor = "1.4.1" +tracing = "0.1.41" diff --git a/factorio-pathfinding/src/belt_finding/conflict_avoidance.rs b/factorio-pathfinding/src/belt_finding/conflict_avoidance.rs index 4160145..8e3284c 100644 --- a/factorio-pathfinding/src/belt_finding/conflict_avoidance.rs +++ b/factorio-pathfinding/src/belt_finding/conflict_avoidance.rs @@ -7,6 +7,7 @@ use std::{ ops::RangeInclusive, time::{Duration, Instant}, }; +use tracing::{span, Level}; use super::Problem; @@ -456,6 +457,7 @@ impl ConflictAvoidance { } pub fn remove_all_conflicts(&mut self, timeout: Option) -> bool { + let _span = span!(Level::TRACE, "remove_all_conflicts").entered(); let end = timeout.map(|t| std::time::Instant::now() + t); while self.remove_conflict(end) {} diff --git a/factorio-pathfinding/src/belt_finding/mod.rs b/factorio-pathfinding/src/belt_finding/mod.rs index 602cd06..a03cd3d 100644 --- a/factorio-pathfinding/src/belt_finding/mod.rs +++ b/factorio-pathfinding/src/belt_finding/mod.rs @@ -10,6 +10,8 @@ use crate::SinglePathInput; use crate::{graph::wheighted_graph::shortest_path::dijkstra, SinglePathfinder}; use factorio_core::{prelude::*, visualize::Visualize}; use serde::{Deserialize, Serialize}; +use tracing::span; +use tracing::Level; pub mod brute_force; @@ -24,6 +26,8 @@ impl SinglePathfinder for ConflictAvoidance { &self, input: SinglePathInput, ) -> Option>> { + let _span = span!(Level::TRACE, "find_paths").entered(); + let (pos, size) = input.map.area(); let mut p = Problem::new(size.x as usize, size.y as usize); @@ -264,23 +268,27 @@ impl WheightedGraph for MapInternal<'_> { impl Problem { pub fn find_path(&mut self) -> bool { + let _span = span!(Level::TRACE, "find_path").entered(); for i in 0..self.start.len() { self.calculate_wheights(i); let m = MapInternal { map: &self.map, end: self.end[i], }; - // let p = dijkstra::>(&m, self.start[i], self.end[i]); - let p = a_star::, _>( - &m, - self.start[i], - self.end[i], - |&(p, _)| { - 1.5 * (PositionType::abs_diff(p.x, self.end[i].0.x) - + PositionType::abs_diff(p.y, self.end[i].0.y)) - as f64 - }, - ); + let p = { + let _pathfinding_span = span!(Level::TRACE, "graph").entered(); + // dijkstra::>(&m, self.start[i], self.end[i]) + a_star::, _>( + &m, + self.start[i], + self.end[i], + |&(p, _)| { + 1.5 * (PositionType::abs_diff(p.x, self.end[i].0.x) + + PositionType::abs_diff(p.y, self.end[i].0.y)) + as f64 + }, + ) + }; if let Some(p) = p { self.path[i] = p; } else {