Add tracing support for layouting

This commit is contained in:
hal8174 2025-02-12 22:19:00 +01:00
parent b53d1e87bc
commit cebdee4ec7
8 changed files with 254 additions and 53 deletions

166
Cargo.lock generated
View file

@ -556,6 +556,8 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"serde_yaml", "serde_yaml",
"tracing",
"tracing-subscriber",
] ]
[[package]] [[package]]
@ -585,6 +587,7 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"serde_yaml", "serde_yaml",
"tracing",
] ]
[[package]] [[package]]
@ -606,6 +609,7 @@ dependencies = [
"serde_json", "serde_json",
"serde_yaml", "serde_yaml",
"termcolor", "termcolor",
"tracing",
] ]
[[package]] [[package]]
@ -893,6 +897,15 @@ dependencies = [
"imgref", "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]] [[package]]
name = "maybe-rayon" name = "maybe-rayon"
version = "0.1.1" version = "0.1.1"
@ -978,6 +991,16 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" 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]] [[package]]
name = "num-bigint" name = "num-bigint"
version = "0.4.6" version = "0.4.6"
@ -1049,6 +1072,12 @@ version = "11.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9"
[[package]]
name = "overload"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]] [[package]]
name = "owo-colors" name = "owo-colors"
version = "4.1.0" version = "4.1.0"
@ -1061,6 +1090,12 @@ version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "pin-project-lite"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]] [[package]]
name = "pkg-config" name = "pkg-config"
version = "0.3.31" version = "0.3.31"
@ -1169,7 +1204,7 @@ dependencies = [
"rand 0.8.5", "rand 0.8.5",
"rand_chacha 0.3.1", "rand_chacha 0.3.1",
"rand_xorshift", "rand_xorshift",
"regex-syntax", "regex-syntax 0.8.5",
"rusty-fork", "rusty-fork",
"tempfile", "tempfile",
"unarray", "unarray",
@ -1364,8 +1399,17 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
"regex-automata", "regex-automata 0.4.9",
"regex-syntax", "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]] [[package]]
@ -1376,9 +1420,15 @@ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "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]] [[package]]
name = "regex-syntax" name = "regex-syntax"
version = "0.8.5" version = "0.8.5"
@ -1497,6 +1547,15 @@ dependencies = [
"unsafe-libyaml", "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]] [[package]]
name = "shlex" name = "shlex"
version = "1.3.0" version = "1.3.0"
@ -1644,6 +1703,16 @@ dependencies = [
"syn", "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]] [[package]]
name = "tiff" name = "tiff"
version = "0.9.1" version = "0.9.1"
@ -1699,6 +1768,67 @@ dependencies = [
"winnow", "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]] [[package]]
name = "unarray" name = "unarray"
version = "0.1.4" version = "0.1.4"
@ -1746,6 +1876,12 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "valuable"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
[[package]] [[package]]
name = "version-compare" name = "version-compare"
version = "0.2.0" version = "0.2.0"
@ -1860,6 +1996,22 @@ version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" 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]] [[package]]
name = "winapi-util" name = "winapi-util"
version = "0.1.9" version = "0.1.9"
@ -1869,6 +2021,12 @@ dependencies = [
"windows-sys", "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]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.59.0" version = "0.59.0"

View file

@ -13,3 +13,5 @@ serde_json = "1.0.135"
serde_yaml = "0.9.34" serde_yaml = "0.9.34"
clap = { version = "4.5.26", features = ["derive"] } clap = { version = "4.5.26", features = ["derive"] }
rand = { version = "0.9.0", features = ["small_rng"] } rand = { version = "0.9.0", features = ["small_rng"] }
tracing = "0.1.41"
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }

View file

@ -7,6 +7,7 @@ use factorio_core::prelude::*;
use factorio_layout::{genetic_algorithm_v1::GeneticAlgorithm, valid_layout::ValidLayout}; use factorio_layout::{genetic_algorithm_v1::GeneticAlgorithm, valid_layout::ValidLayout};
use factorio_pathfinding::belt_finding::ConflictAvoidance; use factorio_pathfinding::belt_finding::ConflictAvoidance;
use rand::{SeedableRng, rngs::SmallRng}; use rand::{SeedableRng, rngs::SmallRng};
use tracing_subscriber::{EnvFilter, fmt::format::FmtSpan};
#[derive(Parser)] #[derive(Parser)]
struct Args { struct Args {
@ -21,6 +22,11 @@ struct Args {
fn main() { fn main() {
let args = Args::parse(); 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 text = std::fs::File::open(&args.path).unwrap();
let factory_graph: FactoryGraph = serde_yaml::from_reader(text).unwrap(); let factory_graph: FactoryGraph = serde_yaml::from_reader(text).unwrap();
@ -35,10 +41,10 @@ fn main() {
let l = GeneticAlgorithm { let l = GeneticAlgorithm {
mutation_retries: 20, mutation_retries: 20,
population_size: 5, population_size: 20,
population_keep: 8, population_keep: 4,
population_new: 2, population_new: 4,
generations: 5, generations: 40,
valid_layout: l, valid_layout: l,
}; };
let p = ConflictAvoidance { let p = ConflictAvoidance {
@ -55,5 +61,5 @@ fn main() {
println!("{}", serde_json::to_string_pretty(&b).unwrap()); 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()));
} }

View file

@ -13,3 +13,4 @@ serde_json = "1.0.108"
clap = { version = "4.4.8", features = ["derive"] } clap = { version = "4.4.8", features = ["derive"] }
miette = { version = "7.2.0", features = ["fancy"] } miette = { version = "7.2.0", features = ["fancy"] }
serde_yaml = "0.9.34" serde_yaml = "0.9.34"
tracing = "0.1.41"

View file

@ -1,3 +1,5 @@
use tracing::{Level, info, span, trace, warn};
use crate::{ use crate::{
LayoutResult, Layouter, LayoutResult, Layouter,
misc::{mutate, path_input_from_blocks_positions, score}, misc::{mutate, path_input_from_blocks_positions, score},
@ -20,14 +22,23 @@ impl Layouter for GeneticAlgorithm {
pathfinder: &P, pathfinder: &P,
rng: &mut R, rng: &mut R,
) -> Option<crate::LayoutResult> { ) -> Option<crate::LayoutResult> {
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(); let mut population = Vec::new();
for _ in 0..self.population_size { {
loop { let _initial_generation_span = span!(Level::TRACE, "initial generation").entered();
if let Some(l) = self.valid_layout.layout(input, pathfinder, rng) { for i in 0..self.population_size {
let score = score(input, &l); let _layout_span = span!(Level::TRACE, "layout", i).entered();
population.push((l, score)); loop {
break; 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(); let mut best_result = population[0].clone();
for g in 0..self.generations { for g in 0..self.generations {
for i in 0..(self.population_size - self.population_keep - self.population_new) { let _generation_span = span!(Level::TRACE, "generation", g).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 { let _mutate_span = span!(Level::TRACE, "mutate").entered();
connections: &connections, for i in 0..(self.population_size - self.population_keep - self.population_new) {
map, let _layout_span = span!(Level::TRACE, "layout", i).entered();
}) loop {
{ let parent = &population[i % self.population_keep].0;
let r = LayoutResult { if let Some(blocks) = mutate(input, parent, rng) {
positions: blocks, let (connections, map) =
path_result: paths, path_input_from_blocks_positions(input, parent.size, &blocks);
size: parent.size,
}; if let Some(paths) =
let score = score(input, &r); pathfinder.find_paths(factorio_pathfinding::PathInput {
population.push((r, score)); connections: &connections,
break; 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 { let _new_span = span!(Level::TRACE, "new").entered();
if let Some(l) = self.valid_layout.layout(input, pathfinder, rng) { for i in 0..self.population_new {
let score = score(input, &l); let _layout_span = span!(Level::TRACE, "layout", i).entered();
population[self.population_size - self.population_new + i] = (l, score); loop {
break; 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 { if population[0].1 < best_result.1 {
best_result = population[0].clone(); 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) Some(best_result.0)

View file

@ -28,3 +28,4 @@ serde = { version = "1.0.192", features = ["derive"] }
serde_json = "1.0.108" serde_json = "1.0.108"
serde_yaml = "0.9.34" serde_yaml = "0.9.34"
termcolor = "1.4.1" termcolor = "1.4.1"
tracing = "0.1.41"

View file

@ -7,6 +7,7 @@ use std::{
ops::RangeInclusive, ops::RangeInclusive,
time::{Duration, Instant}, time::{Duration, Instant},
}; };
use tracing::{span, Level};
use super::Problem; use super::Problem;
@ -456,6 +457,7 @@ impl ConflictAvoidance {
} }
pub fn remove_all_conflicts(&mut self, timeout: Option<Duration>) -> bool { pub fn remove_all_conflicts(&mut self, timeout: Option<Duration>) -> bool {
let _span = span!(Level::TRACE, "remove_all_conflicts").entered();
let end = timeout.map(|t| std::time::Instant::now() + t); let end = timeout.map(|t| std::time::Instant::now() + t);
while self.remove_conflict(end) {} while self.remove_conflict(end) {}

View file

@ -10,6 +10,8 @@ use crate::SinglePathInput;
use crate::{graph::wheighted_graph::shortest_path::dijkstra, SinglePathfinder}; use crate::{graph::wheighted_graph::shortest_path::dijkstra, SinglePathfinder};
use factorio_core::{prelude::*, visualize::Visualize}; use factorio_core::{prelude::*, visualize::Visualize};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tracing::span;
use tracing::Level;
pub mod brute_force; pub mod brute_force;
@ -24,6 +26,8 @@ impl SinglePathfinder for ConflictAvoidance {
&self, &self,
input: SinglePathInput<M>, input: SinglePathInput<M>,
) -> Option<Vec<Vec<factorio_core::pathfield::PathField>>> { ) -> Option<Vec<Vec<factorio_core::pathfield::PathField>>> {
let _span = span!(Level::TRACE, "find_paths").entered();
let (pos, size) = input.map.area(); let (pos, size) = input.map.area();
let mut p = Problem::new(size.x as usize, size.y as usize); let mut p = Problem::new(size.x as usize, size.y as usize);
@ -264,23 +268,27 @@ impl WheightedGraph for MapInternal<'_> {
impl Problem { impl Problem {
pub fn find_path(&mut self) -> bool { pub fn find_path(&mut self) -> bool {
let _span = span!(Level::TRACE, "find_path").entered();
for i in 0..self.start.len() { for i in 0..self.start.len() {
self.calculate_wheights(i); self.calculate_wheights(i);
let m = MapInternal { let m = MapInternal {
map: &self.map, map: &self.map,
end: self.end[i], end: self.end[i],
}; };
// let p = dijkstra::<MapInternal, FastBinaryHeap<_>>(&m, self.start[i], self.end[i]); let p = {
let p = a_star::<MapInternal, FastBinaryHeap<_>, _>( let _pathfinding_span = span!(Level::TRACE, "graph").entered();
&m, // dijkstra::<MapInternal, FastBinaryHeap<_>>(&m, self.start[i], self.end[i])
self.start[i], a_star::<MapInternal, FastBinaryHeap<_>, _>(
self.end[i], &m,
|&(p, _)| { self.start[i],
1.5 * (PositionType::abs_diff(p.x, self.end[i].0.x) self.end[i],
+ PositionType::abs_diff(p.y, self.end[i].0.y)) |&(p, _)| {
as f64 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 { if let Some(p) = p {
self.path[i] = p; self.path[i] = p;
} else { } else {