Add initial station generator

This commit is contained in:
hal8174 2025-01-14 23:33:20 +01:00
parent 24b989b9f2
commit c4a17d1792
9 changed files with 398 additions and 37 deletions

View file

@ -0,0 +1 @@
0eNqd0MEOgjAMANB/6bkQYU7dfoUQA9iYJmyQbaiE7N8dePCg8eClWbv1Le0CbT/R6NgG0AtwN1gPulrA89U2/VqzjSHQYOjCk8mopy447rJx6AkiAtsLPUAXsUYgGzgwvYQtmc92Mi259AB/Sgjj4FPzYNc/EyhyiTCDzopdkcsY8UMs/xVV+R0U/4KnwwqmBdzZbdNXBUosUdZYpYgindItBzIJf28c4UbOb6A8lGqvlJTidBRKxPgE1KeFBQ==

View file

@ -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<BlueprintEntity> {
let mut res = Vec::new();
for path in &self.belts {
res.extend(convert_to_blueprint(&path[1..], speed, nextfree));
}
res
}
}
impl Visualize for ConflictAvoidance {

View file

@ -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);
}
}
}

12
src/bin/station.rs Normal file
View file

@ -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())
);
}

View file

@ -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(

View file

@ -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 {

290
src/blueprint/station.rs Normal file
View file

@ -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<BlueprintEntity>,
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)
}

View file

@ -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<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
position_relative_to_grid: Option<BlueprintPosition>,
#[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<u32>,
}
#[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<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
@ -159,7 +162,7 @@ pub struct BlueprintEntity {
tags: Option<serde_json::Value>,
}
#[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<BlueprintInventoryLocation>,
}
#[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<BlueprintItemFilter>,
bar: u16,
}
#[derive(Serialize, Deserialize, Debug)]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct BlueprintConnection {
#[serde(rename = "1")]
first: Option<BlueprintConnectionPoint>,
@ -218,7 +221,7 @@ pub struct BlueprintConnection {
second: Option<BlueprintConnectionPoint>,
}
#[derive(Serialize, Deserialize, Debug)]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct BlueprintConnectionPoint {
#[serde(default)]
red: Vec<BlueprintConnectionData>,
@ -226,33 +229,33 @@ pub struct BlueprintConnectionPoint {
green: Vec<BlueprintConnectionData>,
}
#[derive(Serialize, Deserialize, Debug)]
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
pub struct BlueprintConnectionData {
entity_id: u64,
circuit_id: Option<u64>,
}
#[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,

View file

@ -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;
}
}