From c4a17d1792818d20fc6fda41c308dcb8ed086f5b Mon Sep 17 00:00:00 2001 From: hal8174 Date: Tue, 14 Jan 2025 23:33:20 +0100 Subject: [PATCH] Add initial station generator --- blueprints/electric_pole_connection.bp | 1 + src/belt_finding/conflict_avoidance.rs | 11 + src/bin/beltfinding.rs | 23 ++ src/bin/station.rs | 12 + src/blueprint/belt.rs | 23 +- src/blueprint/mod.rs | 1 + src/blueprint/station.rs | 290 +++++++++++++++++++++++++ src/blueprint/structs.rs | 57 ++--- src/layout/mod.rs | 17 +- 9 files changed, 398 insertions(+), 37 deletions(-) create mode 100644 blueprints/electric_pole_connection.bp create mode 100644 src/bin/station.rs create mode 100644 src/blueprint/station.rs diff --git a/blueprints/electric_pole_connection.bp b/blueprints/electric_pole_connection.bp new file mode 100644 index 0000000..5f31e0e --- /dev/null +++ b/blueprints/electric_pole_connection.bp @@ -0,0 +1 @@ +0eNqd0MEOgjAMANB/6bkQYU7dfoUQA9iYJmyQbaiE7N8dePCg8eClWbv1Le0CbT/R6NgG0AtwN1gPulrA89U2/VqzjSHQYOjCk8mopy447rJx6AkiAtsLPUAXsUYgGzgwvYQtmc92Mi259AB/Sgjj4FPzYNc/EyhyiTCDzopdkcsY8UMs/xVV+R0U/4KnwwqmBdzZbdNXBUosUdZYpYgindItBzIJf28c4UbOb6A8lGqvlJTidBRKxPgE1KeFBQ== diff --git a/src/belt_finding/conflict_avoidance.rs b/src/belt_finding/conflict_avoidance.rs index 24413ed..abcf367 100644 --- a/src/belt_finding/conflict_avoidance.rs +++ b/src/belt_finding/conflict_avoidance.rs @@ -1,4 +1,6 @@ use super::{common::PathField, Problem}; +use crate::blueprint::belt::{convert_to_blueprint, Beltspeed}; +use crate::blueprint::BlueprintEntity; use crate::{belt_finding::brute_force::BruteforceBuilder, misc::Map}; use crate::{common::visualize::Visualize, prelude::*}; use std::{ @@ -496,6 +498,15 @@ impl ConflictAvoidance { } true } + + pub fn belt_blueprint(&self, speed: &Beltspeed, nextfree: &mut u32) -> Vec { + let mut res = Vec::new(); + for path in &self.belts { + res.extend(convert_to_blueprint(&path[1..], speed, nextfree)); + } + + res + } } impl Visualize for ConflictAvoidance { diff --git a/src/bin/beltfinding.rs b/src/bin/beltfinding.rs index b0b26ef..448e1f1 100644 --- a/src/bin/beltfinding.rs +++ b/src/bin/beltfinding.rs @@ -1,6 +1,7 @@ use clap::{Parser, Subcommand, ValueEnum}; use factorio_blueprint::{ belt_finding::{conflict_avoidance::ConflictAvoidance, problems, Problem}, + blueprint::{Blueprint, BlueprintString}, common::visualize::Visualize, }; use std::{io, path::PathBuf}; @@ -46,6 +47,26 @@ struct Args { problem: ProblemCase, } +fn output_blueprint(conflict: &ConflictAvoidance) { + let mut offset = 0; + let belts = conflict.belt_blueprint( + &factorio_blueprint::blueprint::belt::Beltspeed::Normal, + &mut offset, + ); + + let bp = BlueprintString::Blueprint( + Blueprint::builder() + .entities(belts) + .label(format!("test")) + .build(), + ); + + println!( + "{}", + factorio_blueprint::blueprint::encode(&serde_json::to_string(&bp).unwrap()) + ); +} + fn main() { let args = Args::parse(); @@ -67,6 +88,7 @@ fn main() { while c.remove_conflict(None) { c.print_visualization(); } + output_blueprint(&c); } Mode::ConflictStep => { p.print_visualization(); @@ -79,6 +101,7 @@ fn main() { let mut s = String::new(); let _ = io::stdin().read_line(&mut s); } + output_blueprint(&c); } } } diff --git a/src/bin/station.rs b/src/bin/station.rs new file mode 100644 index 0000000..979d1cf --- /dev/null +++ b/src/bin/station.rs @@ -0,0 +1,12 @@ +use factorio_blueprint::blueprint::{station::basic_unload_station, BlueprintString}; + +fn main() { + let b = BlueprintString::Blueprint(basic_unload_station()); + + println!("{}", serde_json::to_string_pretty(&b).unwrap()); + + println!( + "{}", + factorio_blueprint::blueprint::encode(&serde_json::to_string(&b).unwrap()) + ); +} diff --git a/src/blueprint/belt.rs b/src/blueprint/belt.rs index 2e2e66f..dc61f69 100644 --- a/src/blueprint/belt.rs +++ b/src/blueprint/belt.rs @@ -10,7 +10,7 @@ pub enum Beltspeed { } impl Beltspeed { - fn string(&self) -> String { + pub fn string(&self) -> String { match self { Beltspeed::Normal => "transport-belt", Beltspeed::Fast => "fast-transport-belt", @@ -20,7 +20,7 @@ impl Beltspeed { .to_owned() } - fn string_underground(&self) -> String { + pub fn string_underground(&self) -> String { match self { Beltspeed::Normal => "underground-belt", Beltspeed::Fast => "fast-underground-belt", @@ -29,6 +29,25 @@ impl Beltspeed { } .to_owned() } + + pub fn string_splitter(&self) -> String { + match self { + Beltspeed::Normal => "splitter", + Beltspeed::Fast => "fast-splitter", + Beltspeed::Express => "express-splitter", + Beltspeed::Turbo => "turbo-splitter", + } + .to_owned() + } + + pub fn halve(&self) -> Self { + match self { + Beltspeed::Normal => Beltspeed::Normal, + Beltspeed::Fast => Beltspeed::Normal, + Beltspeed::Express => Beltspeed::Fast, + Beltspeed::Turbo => Beltspeed::Fast, + } + } } pub fn convert_belt_to_blueprint( diff --git a/src/blueprint/mod.rs b/src/blueprint/mod.rs index 446efdf..9e3049b 100644 --- a/src/blueprint/mod.rs +++ b/src/blueprint/mod.rs @@ -10,6 +10,7 @@ pub use structs::*; pub mod balancer; pub mod belt; +pub mod station; pub mod train; pub fn decode(s: &str) -> String { diff --git a/src/blueprint/station.rs b/src/blueprint/station.rs new file mode 100644 index 0000000..39c4ef0 --- /dev/null +++ b/src/blueprint/station.rs @@ -0,0 +1,290 @@ +use std::sync::Arc; + +use super::{belt::Beltspeed, Blueprint, BlueprintEntity, BlueprintPosition}; + +pub fn station_unload( + length: usize, + locomotives: usize, + unloader: &Vec, + beltspeed: Beltspeed, + double: bool, + output_x: f64, + output_y: f64, +) -> Blueprint { + assert!(length.is_power_of_two()); + + let mut e = Vec::new(); + + let global_x_offset = locomotives * 7; + + for l in 1..=(length + locomotives) { + e.push( + BlueprintEntity::builder( + "medium-electric-pole".to_owned(), + l as u32 - 1, + BlueprintPosition::new((7 * l) as f64 + 0.5, -1.5), + ) + .build(), + ); + } + + let offset = e.len(); + for l in 0..length { + e.extend(unloader.iter().cloned().map(|mut e| { + e.position.x += (7 * l + global_x_offset) as f64; + e.entity_number += (offset + unloader.len() * l) as u32; + e + })); + } + + let offset = e.len(); + e.push( + BlueprintEntity::builder( + "train-stop".to_owned(), + offset as u32, + BlueprintPosition::new(1.0, -1.0), + ) + .direction(12) + .build(), + ); + + for l in 0..((length * 7 + global_x_offset + 1) / 2) { + let offset = e.len() as u32; + e.push( + BlueprintEntity::builder( + "straight-rail".to_owned(), + offset, + BlueprintPosition::new(2.0 * l as f64 + 1.0, 1.0), + ) + .direction(4) + .build(), + ); + } + + for i in 0..length { + let depth = (i / 2).count_ones(); + + for j in 0..depth { + let offset = e.len() as u32; + e.push( + BlueprintEntity::builder( + beltspeed.string(), + offset, + BlueprintPosition::new( + (7 * i + global_x_offset) as f64 + output_x, + output_y - j as f64, + ), + ) + .build(), + ); + } + + if i % 2 == 0 { + let offset = e.len() as u32; + e.push( + BlueprintEntity::builder( + beltspeed.string(), + offset, + BlueprintPosition::new( + (7 * i + global_x_offset) as f64 + output_x, + output_y - depth as f64, + ), + ) + .direction(12) + .build(), + ); + let offset = e.len() as u32; + e.push( + BlueprintEntity::builder( + beltspeed.string_splitter(), + offset, + BlueprintPosition::new( + (7 * i + global_x_offset) as f64 + output_x - 1.0, + output_y - depth as f64 - 0.5, + ), + ) + .direction(12) + .build(), + ); + } else { + let offset = e.len() as u32; + e.extend((0..=7).map(|j| { + BlueprintEntity::builder( + beltspeed.string(), + offset + j as u32, + BlueprintPosition::new( + (7 * i - j + global_x_offset) as f64 + output_x, + output_y - depth as f64 - 1.0, + ), + ) + .direction(12) + .build() + })); + let offset = e.len() as u32; + e.push( + BlueprintEntity::builder( + beltspeed.string(), + offset, + BlueprintPosition::new( + (7 * i + global_x_offset) as f64 + output_x, + output_y - depth as f64, + ), + ) + .build(), + ); + } + } + + for i in 2..(length.ilog2() as usize + 1) { + let p = 1 << i; + for j in 0..(length / p) { + let depth = j.count_ones(); + let offset = e.len() as u32; + e.push( + BlueprintEntity::builder( + beltspeed.string_splitter(), + offset, + BlueprintPosition::new( + (7 * j * p + global_x_offset) as f64 - i as f64 + output_x, + output_y - i as f64 + 0.5 - depth as f64, + ), + ) + .direction(12) + .build(), + ); + e.extend((0..(7 * p / 2)).map(|l| { + BlueprintEntity::builder( + beltspeed.string(), + offset + l as u32, + BlueprintPosition::new( + (7 * j * p + global_x_offset) as f64 - i as f64 + output_x + l as f64 + 1.0, + output_y - i as f64 - depth as f64, + ), + ) + .direction(12) + .build() + })) + } + } + + Blueprint::builder() + .label("station".to_owned()) + .entities(e) + .wires( + (0..((length + locomotives - 1) as u32)) + .map(|i| [i, 5, i + 1, 5]) + .collect(), + ) + .build() +} + +pub fn basic_unload_station() -> Blueprint { + let top = vec![ + BlueprintEntity::builder( + "steel-chest".to_owned(), + 0, + BlueprintPosition::new(1.5, -1.5), + ) + .build(), + BlueprintEntity::builder( + "bulk-inserter".to_owned(), + 1, + BlueprintPosition::new(1.5, -0.5), + ) + .direction(8) + .build(), + BlueprintEntity::builder( + "fast-inserter".to_owned(), + 2, + BlueprintPosition::new(2.5, -1.5), + ) + .direction(12) + .build(), + BlueprintEntity::builder( + "fast-transport-belt".to_owned(), + 3, + BlueprintPosition::new(3.5, -1.5), + ) + .build(), + ]; + + let bottom = vec![ + BlueprintEntity::builder( + "steel-chest".to_owned(), + 0, + BlueprintPosition::new(1.5, -1.5), + ) + .build(), + BlueprintEntity::builder( + "bulk-inserter".to_owned(), + 1, + BlueprintPosition::new(1.5, -0.5), + ) + .direction(8) + .build(), + BlueprintEntity::builder( + "fast-inserter".to_owned(), + 2, + BlueprintPosition::new(2.5, -1.5), + ) + .direction(12) + .build(), + BlueprintEntity::builder( + "fast-transport-belt".to_owned(), + 3, + BlueprintPosition::new(3.5, -1.5), + ) + .build(), + ]; + + let full = vec![ + BlueprintEntity::builder( + "steel-chest".to_owned(), + 0, + BlueprintPosition::new(1.5, -1.5), + ) + .build(), + BlueprintEntity::builder( + "bulk-inserter".to_owned(), + 1, + BlueprintPosition::new(1.5, -0.5), + ) + .direction(8) + .build(), + BlueprintEntity::builder( + "fast-inserter".to_owned(), + 2, + BlueprintPosition::new(2.5, -1.5), + ) + .direction(12) + .build(), + BlueprintEntity::builder( + "steel-chest".to_owned(), + 0, + BlueprintPosition::new(5.5, -1.5), + ) + .build(), + BlueprintEntity::builder( + "bulk-inserter".to_owned(), + 1, + BlueprintPosition::new(5.5, -0.5), + ) + .direction(8) + .build(), + BlueprintEntity::builder( + "fast-inserter".to_owned(), + 2, + BlueprintPosition::new(4.5, -1.5), + ) + .direction(4) + .build(), + BlueprintEntity::builder( + "fast-transport-belt".to_owned(), + 3, + BlueprintPosition::new(3.5, -1.5), + ) + .build(), + ]; + + station_unload(64, 3, &full, Beltspeed::Normal, false, 3.5, -2.5) +} diff --git a/src/blueprint/structs.rs b/src/blueprint/structs.rs index 2a80225..e517702 100644 --- a/src/blueprint/structs.rs +++ b/src/blueprint/structs.rs @@ -10,7 +10,7 @@ const fn calculate_version(major: u16, minor: u16, patch: u16, dev: u16) -> u64 ((major as u64) << 48) | ((minor as u64) << 32) | ((patch as u64) << 16) | (dev as u64) } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub enum BlueprintString { #[serde(rename = "blueprint_book")] BlueprintBook(BlueprintBook), @@ -18,7 +18,7 @@ pub enum BlueprintString { Blueprint(Blueprint), } -#[derive(Serialize, Deserialize, Debug, Builder)] +#[derive(Serialize, Deserialize, Debug, Clone, Builder)] pub struct BlueprintBook { #[builder(skip = "blueprint-book".to_owned())] item: String, @@ -32,7 +32,7 @@ pub struct BlueprintBook { version: u64, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct BlueprintBookEntry { #[serde(flatten)] entry: BlueprintString, @@ -45,7 +45,7 @@ impl BlueprintBookEntry { } } -#[derive(Serialize, Deserialize, Debug, Builder)] +#[derive(Serialize, Deserialize, Debug, Clone, Builder)] pub struct Blueprint { #[builder(skip = "blueprint".to_owned())] item: String, @@ -75,11 +75,14 @@ pub struct Blueprint { absolute_snapping: Option, #[serde(skip_serializing_if = "Option::is_none")] position_relative_to_grid: Option, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + #[builder(default)] + wires: Vec<[u32; 4]>, #[builder(skip = VERSION)] version: u64, } -#[derive(Serialize, Deserialize, Debug, Builder)] +#[derive(Serialize, Deserialize, Debug, Clone, Builder)] pub struct BlueprintStockConnection { #[builder(start_fn)] stock: u32, @@ -89,14 +92,14 @@ pub struct BlueprintStockConnection { back: Option, } -#[derive(Serialize, Deserialize, Debug, Builder)] +#[derive(Serialize, Deserialize, Debug, Clone, Builder)] pub struct BlueprintEntity { #[builder(start_fn)] name: String, #[builder(start_fn)] - entity_number: u32, + pub entity_number: u32, #[builder(start_fn)] - position: BlueprintPosition, + pub position: BlueprintPosition, #[serde(skip_serializing_if = "Option::is_none")] direction: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -159,7 +162,7 @@ pub struct BlueprintEntity { tags: Option, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct BlueprintItemRequest { id: BlueprintItemID, items: BlueprintItem, @@ -176,18 +179,18 @@ impl BlueprintItemRequest { } } -#[derive(Serialize, Deserialize, Debug, Builder)] +#[derive(Serialize, Deserialize, Debug, Clone, Builder)] pub struct BlueprintItemID { #[builder(start_fn)] name: String, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct BlueprintItem { in_inventory: Vec, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct BlueprintInventoryLocation { count: u32, inventory: u32, @@ -204,13 +207,13 @@ impl BlueprintInventoryLocation { } } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct BlueprintInventory { filters: Vec, bar: u16, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct BlueprintConnection { #[serde(rename = "1")] first: Option, @@ -218,7 +221,7 @@ pub struct BlueprintConnection { second: Option, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct BlueprintConnectionPoint { #[serde(default)] red: Vec, @@ -226,33 +229,33 @@ pub struct BlueprintConnectionPoint { green: Vec, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone, Copy)] pub struct BlueprintConnectionData { entity_id: u64, circuit_id: Option, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct BlueprintItemFilter { name: String, index: u32, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct BlueprintLogisticFilter { name: String, index: u32, count: u32, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct BlueprintSpeakerParameter { playback_volume: f64, playback_globally: bool, allow_polyphony: bool, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct BlueprintSpeakerAlertParameter { show_alert: bool, show_on_map: bool, @@ -260,17 +263,17 @@ pub struct BlueprintSpeakerAlertParameter { alert_message: String, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct BlueprintSignalID { name: String, #[serde(rename = "type")] signal_type: String, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone, Copy)] pub struct BlueprintPosition { - x: f64, - y: f64, + pub x: f64, + pub y: f64, } impl BlueprintPosition { @@ -279,19 +282,19 @@ impl BlueprintPosition { } } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct BlueprintIcon { index: u32, signal: BlueprintSignalID, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct BlueprintTile { name: String, position: BlueprintPosition, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct BlueprintColor { r: f32, g: f32, diff --git a/src/layout/mod.rs b/src/layout/mod.rs index fc34bde..58694fc 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -112,6 +112,7 @@ pub fn genetic_algorithm2<'a, R: Rng + ?Sized>( problem: &'a Problem, new_layouts: usize, mutation_timeout: usize, + max_mutations: usize, rng: &'_ mut R, ) -> PathLayout<'a> { let mut m = (0..new_layouts) @@ -119,19 +120,19 @@ pub fn genetic_algorithm2<'a, R: Rng + ?Sized>( .min_by_key(|p| p.score()) .unwrap(); - m.print_visualization(); + // m.print_visualization(); let mut last_improvement = 0; let mut count = 0; - while last_improvement < mutation_timeout { + while last_improvement < mutation_timeout && count < max_mutations { last_improvement += 1; count += 1; if let Some(p) = PathLayout::new(m.layout.mutate(rng)) { if p.score() < m.score() { m = p; - println!("Step: {count}"); - m.print_visualization(); + // println!("Step: {count}"); + // m.print_visualization(); last_improvement = 0; } } @@ -207,15 +208,15 @@ impl<'a> PathLayout<'a> { if !c.remove_all_conflicts(Some(std::time::Duration::from_secs(2))) { if start.elapsed().as_secs_f32() > 0.5 { - println!("Conflict avoidance: {:.2}", start.elapsed().as_secs_f32()); - c.print_visualization(); + // println!("Conflict avoidance: {:.2}", start.elapsed().as_secs_f32()); + // c.print_visualization(); let file = std::fs::File::create(format!( "out/{}.json", OUTFILEINDEX.fetch_add(1, std::sync::atomic::Ordering::Relaxed) )) .unwrap(); serde_json::to_writer(file, &p).unwrap(); - println!("Saved slow solve."); + // println!("Saved slow solve."); } return None; } @@ -419,7 +420,7 @@ impl Layout<'_> { loop { let p = r.choose_weighted(rng, |i| i.1).unwrap(); - if p.0(&mut s, rng) && rng.gen_bool(0.2) { + if p.0(&mut s, rng) && rng.gen_bool(0.5) { break; } }