Progress with factory generation from factory graph

This commit is contained in:
hal8174 2025-02-02 23:10:40 +01:00
parent 0ab97485bb
commit 0f8bf5000c
8 changed files with 613 additions and 81 deletions

2
Cargo.lock generated
View file

@ -569,7 +569,9 @@ dependencies = [
"factorio-layout", "factorio-layout",
"factorio-pathfinding", "factorio-pathfinding",
"rand", "rand",
"serde",
"serde_json", "serde_json",
"serde_yaml",
] ]
[[package]] [[package]]

View file

@ -8,6 +8,8 @@ factorio-pathfinding = { path = "../factorio-pathfinding" }
factorio-layout = { path = "../factorio-layout" } factorio-layout = { path = "../factorio-layout" }
factorio-core = { path = "../factorio-core" } factorio-core = { path = "../factorio-core" }
factorio-blueprint = { path = "../factorio-blueprint" } factorio-blueprint = { path = "../factorio-blueprint" }
serde = { version = "1.0.192", features = ["derive"] }
serde_json = "1.0.135" serde_json = "1.0.135"
serde_yaml = "0.9.34"
clap = { version = "4.5.26", features = ["derive"] } clap = { version = "4.5.26", features = ["derive"] }
rand = { version = "0.8.5", features = ["small_rng"] } rand = { version = "0.8.5", features = ["small_rng"] }

View file

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

View file

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

View file

@ -214,3 +214,385 @@ pub fn assembly_line(assembly_machines: usize, recipe: impl AsRef<str>) -> Bluep
blueprint blueprint
} }
pub fn assembly_line_2_input(
assembly_machines: usize,
recipe: impl AsRef<str>,
input_belt1: Beltspeed,
input_belt2: Option<Beltspeed>,
output_belt: Beltspeed,
) -> (Blueprint, Position, PositionType, Vec<PositionType>) {
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]
},
)
}

View file

@ -1,6 +1,7 @@
use clap::Parser; use clap::Parser;
use factorio_blueprint::{BlueprintString, encode}; 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)] #[derive(Parser)]
struct Args { struct Args {
@ -8,12 +9,22 @@ struct Args {
json: bool, json: bool,
assembly_machines: usize, assembly_machines: usize,
recipe: String, recipe: String,
output_belt: Beltspeed,
input_belt: Vec<Beltspeed>,
} }
fn main() { fn main() {
let args = Args::parse(); let args = Args::parse();
let b = BlueprintString::Blueprint( 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 { if args.json {

View file

@ -1,6 +1,8 @@
use std::path::PathBuf;
use clap::Parser; use clap::Parser;
use factorio_blueprint::{BlueprintString, encode}; 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_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;
@ -12,11 +14,18 @@ struct Args {
seed: u64, seed: u64,
#[arg(short, long)] #[arg(short, long)]
json: bool, json: bool,
path: PathBuf,
} }
fn main() { fn main() {
let args = Args::parse(); 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 { let l = ValidLayout {
max_tries: 10, max_tries: 10,
retries: 20, retries: 20,
@ -38,7 +47,9 @@ fn main() {
let mut rng = SmallRng::seed_from_u64(args.seed); 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 { if args.json {
println!("{}", serde_json::to_string_pretty(&b).unwrap()); println!("{}", serde_json::to_string_pretty(&b).unwrap());

View file

@ -3,85 +3,124 @@ use factorio_core::{beltoptions::Beltspeed, prelude::*};
use factorio_layout::{Connection, Interface, LayoutInput, Layouter, MacroBlock}; use factorio_layout::{Connection, Interface, LayoutInput, Layouter, MacroBlock};
use factorio_pathfinding::Pathfinder; use factorio_pathfinding::Pathfinder;
use rand::Rng; 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<Building>,
pub factory_connections: Vec<FactoryConnection>,
}
#[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<L: Layouter, P: Pathfinder>( pub fn generate_factory<L: Layouter, P: Pathfinder>(
layouter: &L, layouter: &L,
pathfinder: &P, pathfinder: &P,
factory_graph: FactoryGraph,
rng: &mut impl Rng, rng: &mut impl Rng,
) -> Blueprint { ) -> Blueprint {
// 2 * gears let mut blocks = Vec::new();
// 3 * copper wires let mut blueprints = Vec::new();
// 2 * green circuits let mut connections = Vec::new();
// 2 * inserter let mut used_connections = Vec::new();
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 connections = vec![ for b in &factory_graph.subfactories {
Connection { match b {
startblock: 0, Building::SubFactory {
startpoint: 0, recipe,
endblock: 3, machines,
endpoint: 0, 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, lanes: 1,
beltspeed: Beltspeed::Fast, beltspeed: Beltspeed::Normal,
}, });
Connection {
startblock: 1, used_connections[c.from].0 += 1;
startpoint: 0, used_connections[c.to].1 += 1;
endblock: 2, }
endpoint: 0,
lanes: 1, // dbg!(&blocks);
beltspeed: Beltspeed::Fast, // dbg!(&connections);
},
Connection {
startblock: 2,
startpoint: 0,
endblock: 3,
endpoint: 1,
lanes: 1,
beltspeed: Beltspeed::Fast,
},
];
let l = layouter let l = layouter
.layout( .layout(
@ -94,13 +133,6 @@ pub fn generate_factory<L: Layouter, P: Pathfinder>(
) )
.unwrap(); .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(); let mut b = Blueprint::new();
for (block, mut assembly_blueprint) in l.positions.iter().zip(blueprints) { for (block, mut assembly_blueprint) in l.positions.iter().zip(blueprints) {
let offset = match block.dir() { let offset = match block.dir() {