diff --git a/Cargo.lock b/Cargo.lock index 698637e..325cbcd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -569,7 +569,9 @@ dependencies = [ "factorio-layout", "factorio-pathfinding", "rand", + "serde", "serde_json", + "serde_yaml", ] [[package]] diff --git a/factorio-blueprint-generator/Cargo.toml b/factorio-blueprint-generator/Cargo.toml index ecd7d55..6bac0b7 100644 --- a/factorio-blueprint-generator/Cargo.toml +++ b/factorio-blueprint-generator/Cargo.toml @@ -8,6 +8,8 @@ factorio-pathfinding = { path = "../factorio-pathfinding" } factorio-layout = { path = "../factorio-layout" } factorio-core = { path = "../factorio-core" } factorio-blueprint = { path = "../factorio-blueprint" } +serde = { version = "1.0.192", features = ["derive"] } serde_json = "1.0.135" +serde_yaml = "0.9.34" clap = { version = "4.5.26", features = ["derive"] } rand = { version = "0.8.5", features = ["small_rng"] } diff --git a/factorio-blueprint-generator/factory_graph_fast_inserter.yml b/factorio-blueprint-generator/factory_graph_fast_inserter.yml new file mode 100644 index 0000000..748f0ef --- /dev/null +++ b/factorio-blueprint-generator/factory_graph_fast_inserter.yml @@ -0,0 +1,43 @@ +subfactories: + - !SubFactory + recipe: iron-gear-wheel + machines: 2 + machine: assembly-machine-3 + - !SubFactory + recipe: copper-cable + machines: 9 + machine: assembly-machine-3 + - !SubFactory + recipe: electronic-circuit + machines: 6 + machine: assembly-machine-3 + - !BeltPool + - !SubFactory + recipe: inserter + machines: 2 + machine: assembly-machine-3 + - !SubFactory + recipe: fast-inserter + machines: 2 + machine: assembly-machine-3 +factory_connections: + - item: iron-gear-wheel + amount: 5 + from: 0 + to: 4 + - item: copper-cable + amount: 45 + from: 1 + to: 2 + - item: electronic-circuit + amount: 15 + from: 2 + to: 3 + - item: electronic-circuit + amount: 5 + from: 3 + to: 4 + - item: electronic-circuit + amount: 10 + from: 3 + to: 5 diff --git a/factorio-blueprint-generator/factory_graph_inserter.yml b/factorio-blueprint-generator/factory_graph_inserter.yml new file mode 100644 index 0000000..7e5e2cb --- /dev/null +++ b/factorio-blueprint-generator/factory_graph_inserter.yml @@ -0,0 +1,49 @@ +subfactories: + - !SubFactory + recipe: iron-gear-wheel + machines: 2 + machine: assembly-machine-3 + - !SubFactory + recipe: copper-cable + machines: 3 + machine: assembly-machine-3 + - !SubFactory + recipe: electronic-circuit + machines: 2 + machine: assembly-machine-3 + - !SubFactory + recipe: inserter + machines: 2 + machine: assembly-machine-3 + - !ExternalConnection + inputs: 1 + outputs: 5 +factory_connections: + - item: iron-gear-wheel + amount: 5 + from: 0 + to: 3 + - item: copper-cable + amount: 15 + from: 1 + to: 2 + - item: electronic-circuit + amount: 5 + from: 2 + to: 3 + - item: iron-plate + amount: 10 + from: 4 + to: 0 + - item: iron-plate + amount: 5 + from: 4 + to: 2 + - item: copper-plate + amount: 7.5 + from: 4 + to: 1 + - item: inserter + amount: 5 + from: 3 + to: 4 diff --git a/factorio-blueprint-generator/src/assembly.rs b/factorio-blueprint-generator/src/assembly.rs index 8210161..ae7968e 100644 --- a/factorio-blueprint-generator/src/assembly.rs +++ b/factorio-blueprint-generator/src/assembly.rs @@ -214,3 +214,385 @@ pub fn assembly_line(assembly_machines: usize, recipe: impl AsRef) -> Bluep blueprint } + +pub fn assembly_line_2_input( + assembly_machines: usize, + recipe: impl AsRef, + input_belt1: Beltspeed, + input_belt2: Option, + output_belt: Beltspeed, +) -> (Blueprint, Position, PositionType, Vec) { + let halve_machines = assembly_machines / 2; + let mut blueprint = Blueprint::new(); + + let mut last = None; + for i in (0..halve_machines.div_ceil(3)).rev() { + let y_pos = if i == halve_machines.div_ceil(3) - 1 && halve_machines % 3 == 1 { + 1 + 18 * i as PositionType + } else { + 9 + 18 * i as PositionType + }; + let top = blueprint.add_entity(Entity::new_electric_pole( + ElectricPoleType::Medium, + Position::new(y_pos, 5), + )); + + let middle = blueprint.add_entity(Entity::new_electric_pole( + ElectricPoleType::Medium, + Position::new(y_pos, 17), + )); + + let bottom = blueprint.add_entity(Entity::new_electric_pole( + ElectricPoleType::Medium, + Position::new(y_pos, 25), + )); + + blueprint.add_wire(top, 5, middle, 5); + blueprint.add_wire(middle, 5, bottom, 5); + if let Some((last_top, last_middle, last_bottom)) = last { + blueprint.add_wire(top, 5, last_top, 5); + blueprint.add_wire(middle, 5, last_middle, 5); + blueprint.add_wire(bottom, 5, last_bottom, 5); + } + + last = Some((top, middle, bottom)); + } + + for i in 0..halve_machines { + blueprint.add_entity(Entity::new_production( + "assembling-machine-3", + recipe.as_ref(), + Position::new(3 + 6 * i as PositionType, 9), + Direction::Up, + Position::new(6, 6), + )); + blueprint.add_entity(Entity::new_production( + "assembling-machine-3", + recipe.as_ref(), + Position::new(3 + 6 * i as PositionType, 21), + Direction::Up, + Position::new(6, 6), + )); + match i % 3 { + 0 => { + if input_belt2.is_some() { + blueprint.add_entity(Entity::new_inserter( + InserterType::Long, + None, + Position::new(3 + 6 * i as PositionType, 5), + Direction::Up, + )); + blueprint.add_entity(Entity::new_inserter( + InserterType::Long, + None, + Position::new(3 + 6 * i as PositionType, 25), + Direction::Down, + )); + } + blueprint.add_entity(Entity::new_inserter( + InserterType::Fast, + None, + Position::new(5 + 6 * i as PositionType, 5), + Direction::Up, + )); + blueprint.add_entity(Entity::new_inserter( + InserterType::Fast, + None, + Position::new(3 + 6 * i as PositionType, 13), + Direction::Up, + )); + blueprint.add_entity(Entity::new_inserter( + InserterType::Fast, + None, + Position::new(3 + 6 * i as PositionType, 17), + Direction::Down, + )); + blueprint.add_entity(Entity::new_inserter( + InserterType::Fast, + None, + Position::new(5 + 6 * i as PositionType, 25), + Direction::Down, + )); + } + 1 => { + if input_belt2.is_some() { + blueprint.add_entity(Entity::new_inserter( + InserterType::Long, + None, + Position::new(1 + 6 * i as PositionType, 5), + Direction::Up, + )); + blueprint.add_entity(Entity::new_inserter( + InserterType::Long, + None, + Position::new(1 + 6 * i as PositionType, 25), + Direction::Down, + )); + } + blueprint.add_entity(Entity::new_inserter( + InserterType::Fast, + None, + Position::new(5 + 6 * i as PositionType, 5), + Direction::Up, + )); + blueprint.add_entity(Entity::new_inserter( + InserterType::Fast, + None, + Position::new(1 + 6 * i as PositionType, 13), + Direction::Up, + )); + blueprint.add_entity(Entity::new_inserter( + InserterType::Fast, + None, + Position::new(1 + 6 * i as PositionType, 17), + Direction::Down, + )); + blueprint.add_entity(Entity::new_inserter( + InserterType::Fast, + None, + Position::new(5 + 6 * i as PositionType, 25), + Direction::Down, + )); + } + 2 => { + if input_belt2.is_some() { + blueprint.add_entity(Entity::new_inserter( + InserterType::Long, + None, + Position::new(1 + 6 * i as PositionType, 5), + Direction::Up, + )); + blueprint.add_entity(Entity::new_inserter( + InserterType::Long, + None, + Position::new(1 + 6 * i as PositionType, 25), + Direction::Down, + )); + } + blueprint.add_entity(Entity::new_inserter( + InserterType::Fast, + None, + Position::new(3 + 6 * i as PositionType, 5), + Direction::Up, + )); + blueprint.add_entity(Entity::new_inserter( + InserterType::Fast, + None, + Position::new(1 + 6 * i as PositionType, 13), + Direction::Up, + )); + blueprint.add_entity(Entity::new_inserter( + InserterType::Fast, + None, + Position::new(1 + 6 * i as PositionType, 17), + Direction::Down, + )); + blueprint.add_entity(Entity::new_inserter( + InserterType::Fast, + None, + Position::new(3 + 6 * i as PositionType, 25), + Direction::Down, + )); + } + _ => unreachable!(), + } + } + + for i in 0..(3 * halve_machines + 2) { + if let Some(beltspeed) = input_belt2 { + if i > 0 { + blueprint.add_entity(Entity::new_belt( + beltspeed, + Position::new(1 + 2 * i as PositionType, 1), + Direction::Left, + )); + } + } + if i < (3 * halve_machines) { + blueprint.add_entity(Entity::new_belt( + input_belt1, + Position::new(1 + 2 * i as PositionType, 3), + Direction::Right, + )); + } + if i < (3 * halve_machines - 1) { + blueprint.add_entity(Entity::new_belt( + output_belt, + Position::new(1 + 2 * i as PositionType, 15), + Direction::Left, + )); + } + if i > 1 && i < (3 * halve_machines + 1) { + blueprint.add_entity(Entity::new_belt( + input_belt1, + Position::new(1 + 2 * i as PositionType, 27), + Direction::Left, + )); + } + if let Some(beltspeed) = input_belt2 { + if i < (3 * halve_machines + 1) { + blueprint.add_entity(Entity::new_belt( + beltspeed, + Position::new(1 + 2 * i as PositionType, 29), + Direction::Right, + )); + } + } + } + + for i in 0..15 { + if i > 0 && i < 13 { + blueprint.add_entity(Entity::new_belt( + input_belt1, + Position::new( + 1 + 6 * halve_machines as PositionType, + 1 + 2 * i as PositionType, + ), + Direction::Down, + )); + } + if let Some(beltspeed) = input_belt2 { + if i > 0 { + blueprint.add_entity(Entity::new_belt( + beltspeed, + Position::new( + 3 + 6 * halve_machines as PositionType, + 1 + 2 * i as PositionType, + ), + Direction::Up, + )); + } + } + } + + if assembly_machines % 2 == 1 { + let middle = blueprint.add_entity(Entity::new_electric_pole( + ElectricPoleType::Medium, + Position::new(-3, 17), + )); + + let bottom = blueprint.add_entity(Entity::new_electric_pole( + ElectricPoleType::Medium, + Position::new(-3, 25), + )); + + blueprint.add_wire(middle, 5, bottom, 5); + + if let Some((_, last_middle, last_bottom)) = last { + blueprint.add_wire(middle, 5, last_middle, 5); + blueprint.add_wire(bottom, 5, last_bottom, 5); + } + + blueprint.add_entity(Entity::new_production( + "assembling-machine-3", + recipe.as_ref(), + Position::new(-3, 21), + Direction::Up, + Position::new(6, 6), + )); + + blueprint.add_entity(Entity::new_inserter( + InserterType::Fast, + None, + Position::new(-5, 17), + Direction::Down, + )); + blueprint.add_entity(Entity::new_inserter( + InserterType::Long, + None, + Position::new(-1, 17), + Direction::Down, + )); + blueprint.add_entity(Entity::new_inserter( + InserterType::Fast, + None, + Position::new(-1, 25), + Direction::Down, + )); + if input_belt2.is_some() { + blueprint.add_entity(Entity::new_inserter( + InserterType::Long, + None, + Position::new(-5, 25), + Direction::Down, + )); + } + + blueprint.add_entity(Entity::new_belt( + output_belt, + Position::new(-1, 13), + Direction::Down, + )); + blueprint.add_entity(Entity::new_belt( + output_belt, + Position::new(-1, 15), + Direction::Left, + )); + blueprint.add_entity(Entity::new_belt( + output_belt, + Position::new(-3, 13), + Direction::Left, + )); + blueprint.add_entity(Entity::new_belt( + output_belt, + Position::new(-3, 15), + Direction::Up, + )); + blueprint.add_entity(Entity::new_belt( + output_belt, + Position::new(-5, 13), + Direction::Left, + )); + blueprint.add_entity(Entity::new_belt( + output_belt, + Position::new(-5, 15), + Direction::Up, + )); + + for i in 0..3 { + blueprint.add_entity(Entity::new_belt( + input_belt1, + Position::new(-5 + 2 * i as PositionType, 3), + Direction::Right, + )); + blueprint.add_entity(Entity::new_belt( + input_belt1, + Position::new(-1 + 2 * i as PositionType, 27), + Direction::Left, + )); + if let Some(beltspeed) = input_belt2 { + blueprint.add_entity(Entity::new_belt( + beltspeed, + Position::new(-5 + 2 * i as PositionType, 29), + Direction::Right, + )); + } + } + + blueprint.transform(Transformation::new(Direction::Up, Position::new(6, 0))); + } + + if input_belt2.is_none() { + blueprint.transform(Transformation::new(Direction::Up, Position::new(0, -2))); + } + + ( + blueprint, + Position::new( + 3 * assembly_machines.div_ceil(2) as PositionType + + if input_belt2.is_some() { 2 } else { 1 }, + if input_belt2.is_some() { 15 } else { 13 }, + ), + match (input_belt2.is_some(), assembly_machines % 2 == 0) { + (true, true) => 7, + (true, false) => 6, + (false, true) => 6, + (false, false) => 5, + }, + if input_belt2.is_some() { + vec![1, 14] + } else { + vec![0] + }, + ) +} diff --git a/factorio-blueprint-generator/src/bin/assembly.rs b/factorio-blueprint-generator/src/bin/assembly.rs index e36629a..6cc4553 100644 --- a/factorio-blueprint-generator/src/bin/assembly.rs +++ b/factorio-blueprint-generator/src/bin/assembly.rs @@ -1,6 +1,7 @@ use clap::Parser; use factorio_blueprint::{BlueprintString, encode}; -use factorio_blueprint_generator::assembly::assembly_line; +use factorio_blueprint_generator::assembly::{assembly_line, assembly_line_2_input}; +use factorio_core::beltoptions::Beltspeed; #[derive(Parser)] struct Args { @@ -8,12 +9,22 @@ struct Args { json: bool, assembly_machines: usize, recipe: String, + output_belt: Beltspeed, + input_belt: Vec, } fn main() { let args = Args::parse(); let b = BlueprintString::Blueprint( - assembly_line(args.assembly_machines, args.recipe).to_blueprint(), + assembly_line_2_input( + args.assembly_machines, + args.recipe, + args.input_belt[0], + args.input_belt.get(1).copied(), + args.output_belt, + ) + .0 + .to_blueprint(), ); if args.json { diff --git a/factorio-blueprint-generator/src/bin/generate_factory.rs b/factorio-blueprint-generator/src/bin/generate_factory.rs index 417abb4..334fbd8 100644 --- a/factorio-blueprint-generator/src/bin/generate_factory.rs +++ b/factorio-blueprint-generator/src/bin/generate_factory.rs @@ -1,6 +1,8 @@ +use std::path::PathBuf; + use clap::Parser; use factorio_blueprint::{BlueprintString, encode}; -use factorio_blueprint_generator::factory::generate_factory; +use factorio_blueprint_generator::factory::{FactoryGraph, generate_factory}; use factorio_core::prelude::*; use factorio_layout::{genetic_algorithm_v1::GeneticAlgorithm, valid_layout::ValidLayout}; use factorio_pathfinding::belt_finding::ConflictAvoidance; @@ -12,11 +14,18 @@ struct Args { seed: u64, #[arg(short, long)] json: bool, + + path: PathBuf, } fn main() { let args = Args::parse(); + let text = std::fs::File::open(&args.path).unwrap(); + let factory_graph: FactoryGraph = serde_yaml::from_reader(text).unwrap(); + + dbg!(&factory_graph); + let l = ValidLayout { max_tries: 10, retries: 20, @@ -38,7 +47,9 @@ fn main() { let mut rng = SmallRng::seed_from_u64(args.seed); - let b = BlueprintString::Blueprint(generate_factory(&l, &p, &mut rng).to_blueprint()); + let b = BlueprintString::Blueprint( + generate_factory(&l, &p, factory_graph, &mut rng).to_blueprint(), + ); if args.json { println!("{}", serde_json::to_string_pretty(&b).unwrap()); diff --git a/factorio-blueprint-generator/src/factory.rs b/factorio-blueprint-generator/src/factory.rs index df50d7f..1e7c764 100644 --- a/factorio-blueprint-generator/src/factory.rs +++ b/factorio-blueprint-generator/src/factory.rs @@ -3,85 +3,124 @@ use factorio_core::{beltoptions::Beltspeed, prelude::*}; use factorio_layout::{Connection, Interface, LayoutInput, Layouter, MacroBlock}; use factorio_pathfinding::Pathfinder; use rand::Rng; +use serde::{Deserialize, Serialize}; -use crate::assembly::assembly_line; +use crate::assembly::{assembly_line, assembly_line_2_input}; + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct FactoryGraph { + pub subfactories: Vec, + pub factory_connections: Vec, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub enum Building { + SubFactory { + recipe: String, + machines: usize, + machine: String, + }, + ExternalConnection { + inputs: usize, + outputs: usize, + }, + BeltPool, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct FactoryConnection { + pub item: String, + pub amount: f64, + pub from: usize, + pub to: usize, +} pub fn generate_factory( layouter: &L, pathfinder: &P, + factory_graph: FactoryGraph, rng: &mut impl Rng, ) -> Blueprint { - // 2 * gears - // 3 * copper wires - // 2 * green circuits - // 2 * inserter - let input = vec![ - Interface { - offset: Position::new(0, 1), - dir: Direction::Right, - }, - Interface { - offset: Position::new(0, 7), - dir: Direction::Right, - }, - Interface { - offset: Position::new(0, 8), - dir: Direction::Right, - }, - ]; - let output = vec![Interface { - offset: Position::new(0, 0), - dir: Direction::Left, - }]; - let blocks = vec![ - MacroBlock { - size: Position::new(6, 9), - input: input.clone(), - output: output.clone(), - }, - MacroBlock { - size: Position::new(9, 9), - input: input.clone(), - output: output.clone(), - }, - MacroBlock { - size: Position::new(6, 9), - input: input.clone(), - output: output.clone(), - }, - MacroBlock { - size: Position::new(6, 9), - input: input.clone(), - output: output.clone(), - }, - ]; + let mut blocks = Vec::new(); + let mut blueprints = Vec::new(); + let mut connections = Vec::new(); + let mut used_connections = Vec::new(); - let connections = vec![ - Connection { - startblock: 0, - startpoint: 0, - endblock: 3, - endpoint: 0, + for b in &factory_graph.subfactories { + match b { + Building::SubFactory { + recipe, + machines, + machine, + } => { + assert!(machine == "assembly-machine-3"); + + let (b, size, y_output, y_inputs) = assembly_line_2_input( + *machines, + recipe, + Beltspeed::Normal, + Some(Beltspeed::Normal), + Beltspeed::Normal, + ); + + blueprints.push(b); + blocks.push(MacroBlock { + size, + input: y_inputs + .iter() + .map(|&y| Interface { + offset: Position::new(0, y), + dir: Direction::Right, + }) + .collect(), + output: vec![Interface { + offset: Position::new(0, y_output), + dir: Direction::Left, + }], + }); + + used_connections.push((0, 0)); + } + Building::ExternalConnection { inputs, outputs } => { + blocks.push(MacroBlock { + size: Position::new(1, (inputs + outputs) as PositionType), + input: (0..*inputs) + .map(|y| Interface { + offset: Position::new(0, y as PositionType), + dir: Direction::Left, + }) + .collect(), + output: (0..*outputs) + .map(|y| Interface { + offset: Position::new(0, (inputs + y) as PositionType), + dir: Direction::Right, + }) + .collect(), + }); + + blueprints.push(Blueprint::new()); + used_connections.push((0, 0)); + } + Building::BeltPool => todo!(), + } + } + + for c in &factory_graph.factory_connections { + connections.push(Connection { + startblock: c.from, + startpoint: used_connections[c.from].0, + endblock: c.to, + endpoint: used_connections[c.to].1, lanes: 1, - beltspeed: Beltspeed::Fast, - }, - Connection { - startblock: 1, - startpoint: 0, - endblock: 2, - endpoint: 0, - lanes: 1, - beltspeed: Beltspeed::Fast, - }, - Connection { - startblock: 2, - startpoint: 0, - endblock: 3, - endpoint: 1, - lanes: 1, - beltspeed: Beltspeed::Fast, - }, - ]; + beltspeed: Beltspeed::Normal, + }); + + used_connections[c.from].0 += 1; + used_connections[c.to].1 += 1; + } + + // dbg!(&blocks); + // dbg!(&connections); let l = layouter .layout( @@ -94,13 +133,6 @@ pub fn generate_factory( ) .unwrap(); - let blueprints = vec![ - assembly_line(2, "iron-gear-wheel"), - assembly_line(3, "copper-cable"), - assembly_line(2, "electronic-circuit"), - assembly_line(2, "inserter"), - ]; - let mut b = Blueprint::new(); for (block, mut assembly_blueprint) in l.positions.iter().zip(blueprints) { let offset = match block.dir() {