Refactor layouting into separate crate

This commit is contained in:
hal8174 2025-01-27 22:09:59 +01:00
parent c3bb980fcf
commit 5c8010c23b
15 changed files with 124 additions and 55 deletions

16
Cargo.lock generated
View file

@ -584,6 +584,21 @@ dependencies = [
"termcolor", "termcolor",
] ]
[[package]]
name = "factorio-layout"
version = "0.1.0"
dependencies = [
"clap 4.5.26",
"factorio-core",
"factorio-pathfinding",
"image",
"miette",
"rand",
"serde",
"serde_json",
"serde_yaml",
]
[[package]] [[package]]
name = "factorio-pathfinding" name = "factorio-pathfinding"
version = "0.1.0" version = "0.1.0"
@ -599,7 +614,6 @@ dependencies = [
"miette", "miette",
"proptest", "proptest",
"proptest-derive", "proptest-derive",
"rand",
"serde", "serde",
"serde_json", "serde_json",
"serde_yaml", "serde_yaml",

View file

@ -1,5 +1,5 @@
[workspace] [workspace]
members = [ "factorio-blueprint", "factorio-blueprint-generator", "factorio-core", members = [ "factorio-blueprint", "factorio-blueprint-generator", "factorio-core", "factorio-layout",
"factorio-pathfinding" "factorio-pathfinding"
] ]
resolver = "2" resolver = "2"

View file

@ -85,6 +85,7 @@ fn main() {
// ], // ],
8, 8,
) )
.0
.to_blueprint(), .to_blueprint(),
); );

View file

@ -40,7 +40,10 @@ fn calculate_station_height(
station_height station_height
} }
pub fn multistation(stations: &[StationSpec], stacker_size: usize) -> Blueprint { pub fn multistation(
stations: &[StationSpec],
stacker_size: usize,
) -> (Blueprint, PositionType, Vec<PositionType>) {
let longest_train = stations let longest_train = stations
.iter() .iter()
.map(|s| s.locomotives + s.wagons) .map(|s| s.locomotives + s.wagons)
@ -144,6 +147,7 @@ pub fn multistation(stations: &[StationSpec], stacker_size: usize) -> Blueprint
let inrail_x = 10 - 4 * stacker_length as PositionType; let inrail_x = 10 - 4 * stacker_length as PositionType;
let outrail_x = inrail_x + 52 + 4 * (7 * longest_train).div_ceil(2) as PositionType; let outrail_x = inrail_x + 52 + 4 * (7 * longest_train).div_ceil(2) as PositionType;
let mut output_heights = Vec::new();
let mut previous_station_heights = 0; let mut previous_station_heights = 0;
// station // station
for (i, station) in stations.iter().enumerate() { for (i, station) in stations.iter().enumerate() {
@ -269,6 +273,8 @@ pub fn multistation(stations: &[StationSpec], stacker_size: usize) -> Blueprint
let station_height = let station_height =
calculate_station_height(output_height, station.lanes, station.beltspeed); calculate_station_height(output_height, station.lanes, station.beltspeed);
output_heights.push(30 + total_stacker_height + previous_station_heights + output_height);
// rail crossing // rail crossing
let (beltdirection, underground_left, underground_right) = match station.load { let (beltdirection, underground_left, underground_right) = match station.load {
true => ( true => (
@ -629,5 +635,5 @@ pub fn multistation(stations: &[StationSpec], stacker_size: usize) -> Blueprint
blueprint.transform(Transformation::new(Direction::Right, Position::new(0, 0))); blueprint.transform(Transformation::new(Direction::Right, Position::new(0, 0)));
blueprint (blueprint, outrail_x + 5, output_heights)
} }

View file

@ -1,9 +1,5 @@
use factorio_core::{ use factorio_core::{
aabb::AABB, aabb::AABB, beltoptions::Beltspeed, pathfield::PathField, prelude::*,
beltoptions::Beltspeed,
direction,
pathfield::PathField,
prelude::{Direction, Position, PositionType, Transformable, Transformation},
quaterdirection::QuaterDirection, quaterdirection::QuaterDirection,
}; };
use std::{collections::HashMap, sync::atomic::AtomicUsize}; use std::{collections::HashMap, sync::atomic::AtomicUsize};

View file

@ -0,0 +1,15 @@
[package]
name = "factorio-layout"
version = "0.1.0"
edition = "2024"
[dependencies]
factorio-core = { path = "../factorio-core" }
factorio-pathfinding = { path = "../factorio-pathfinding" }
rand = { version = "0.8.5", features = ["small_rng"] }
serde = { version = "1.0.192", features = ["derive"] }
image = "0.25.2"
serde_json = "1.0.108"
clap = { version = "4.4.8", features = ["derive"] }
miette = { version = "7.2.0", features = ["fancy"] }
serde_yaml = "0.9.34"

View file

@ -1,8 +1,8 @@
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use factorio_core::visualize::Visualize; use factorio_core::visualize::Visualize;
use factorio_pathfinding::layout::{genetic_algorithm2, GeneticAlgorithm, PathLayout}; use factorio_layout::layout::{GeneticAlgorithm, PathLayout, genetic_algorithm2};
use miette::{Context, IntoDiagnostic, Result}; use miette::{Context, IntoDiagnostic, Result};
use rand::{rngs::SmallRng, SeedableRng}; use rand::{SeedableRng, rngs::SmallRng};
use std::path::PathBuf; use std::path::PathBuf;
#[derive(Debug, Parser)] #[derive(Debug, Parser)]

View file

@ -1,10 +1,10 @@
use crate::belt_finding::conflict_avoidance::ConflictAvoidance;
use factorio_core::{ use factorio_core::{
pathfield::PathField, pathfield::PathField,
prelude::*, prelude::*,
visualize::{image_grid, Color, Symbol, Visualization, Visualize}, visualize::{Color, Symbol, Visualization, Visualize, image_grid},
}; };
use rand::{seq::SliceRandom, Rng}; use factorio_pathfinding::belt_finding::{self, conflict_avoidance::ConflictAvoidance};
use rand::{Rng, seq::SliceRandom};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{sync::atomic::AtomicU32, time::Instant}; use std::{sync::atomic::AtomicU32, time::Instant};
@ -195,9 +195,48 @@ pub struct PathLayout<'a> {
score: usize, score: usize,
} }
pub fn beltfinding_problem_from_layout(l: &Layout) -> belt_finding::Problem {
let mut p = belt_finding::Problem::new(l.problem.size.x as usize, l.problem.size.y as usize);
for b in &l.blocks {
let aabb = b.get_aabb();
p.set_blocked_range(
aabb.min().x as usize,
aabb.min().y as usize,
aabb.max().x as usize,
aabb.max().y as usize,
true,
);
}
for c in &l.problem.connections {
let start_transform = l.blocks[c.startblock].block_to_world();
let startpos = l.problem.blocks[c.startblock].output[c.startpoint]
.offset
.transform(start_transform);
let startdir = l.problem.blocks[c.startblock].output[c.startpoint]
.dir
.transform(start_transform);
let end_transform = l.blocks[c.endblock].block_to_world();
let endpos = l.problem.blocks[c.endblock].input[c.endpoint]
.offset
.transform(end_transform);
let enddir = l.problem.blocks[c.endblock].input[c.endpoint]
.dir
.transform(end_transform);
p.add_connection(
(startpos, startdir),
(endpos.in_direction(&enddir, -1), enddir),
);
}
p
}
impl<'a> PathLayout<'a> { impl<'a> PathLayout<'a> {
pub fn new(layout: Layout<'a>) -> Option<PathLayout<'a>> { pub fn new(layout: Layout<'a>) -> Option<PathLayout<'a>> {
let mut p = crate::belt_finding::Problem::from_layout(&layout); let mut p = beltfinding_problem_from_layout(&layout);
if !p.find_path() { if !p.find_path() {
return None; return None;
@ -343,7 +382,7 @@ impl Layout<'_> {
let b = &problem.blocks[blocks.len()]; let b = &problem.blocks[blocks.len()];
for _ in 0..1000 { for _ in 0..1000 {
let dir = rng.gen::<Direction>(); let dir = rng.r#gen::<Direction>();
let pos = match dir { let pos = match dir {
Direction::Up => Position::new( Direction::Up => Position::new(
@ -433,7 +472,7 @@ impl Layout<'_> {
fn mutate_replace<R: Rng + ?Sized>(layout: &mut Layout, rng: &mut R) -> bool { fn mutate_replace<R: Rng + ?Sized>(layout: &mut Layout, rng: &mut R) -> bool {
let i = rng.gen_range(0..layout.blocks.len()); let i = rng.gen_range(0..layout.blocks.len());
let dir = rng.gen::<Direction>(); let dir = rng.r#gen::<Direction>();
let b = &layout.problem.blocks[i]; let b = &layout.problem.blocks[i];
@ -492,7 +531,7 @@ impl Layout<'_> {
fn mutate_jiggle<R: Rng + ?Sized>(layout: &mut Layout, rng: &mut R) -> bool { fn mutate_jiggle<R: Rng + ?Sized>(layout: &mut Layout, rng: &mut R) -> bool {
let i = rng.gen_range(0..layout.blocks.len()); let i = rng.gen_range(0..layout.blocks.len());
let dir = rng.gen::<Direction>(); let dir = rng.r#gen::<Direction>();
let step = [(1, 10), (2, 5), (3, 5)] let step = [(1, 10), (2, 5), (3, 5)]
.choose_weighted(rng, |i| i.1) .choose_weighted(rng, |i| i.1)
.unwrap() .unwrap()

View file

@ -0,0 +1 @@
pub mod layout;

View file

@ -24,7 +24,6 @@ image = "0.25.2"
miette = { version = "7.2.0", features = ["fancy"] } miette = { version = "7.2.0", features = ["fancy"] }
proptest = "1.5.0" proptest = "1.5.0"
proptest-derive = "0.5.0" proptest-derive = "0.5.0"
rand = { version = "0.8.5", features = ["small_rng"] }
serde = { version = "1.0.192", features = ["derive"] } 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"

View file

@ -1,6 +1,5 @@
use crate::graph::wheighted_graph::shortest_path::dijkstra; use crate::graph::wheighted_graph::shortest_path::dijkstra;
use crate::graph::wheighted_graph::WheightedGraph; use crate::graph::wheighted_graph::WheightedGraph;
use crate::layout::Layout;
use crate::misc::Map; use crate::misc::Map;
use crate::priority_queue::BinaryHeap; use crate::priority_queue::BinaryHeap;
use factorio_core::{prelude::*, visualize::Visualize}; use factorio_core::{prelude::*, visualize::Visualize};
@ -34,44 +33,44 @@ impl Problem {
} }
} }
pub fn from_layout(l: &Layout) -> Self { // pub fn from_layout(l: &Layout) -> Self {
let mut p = Self::new(l.problem.size.x as usize, l.problem.size.y as usize); // let mut p = Self::new(l.problem.size.x as usize, l.problem.size.y as usize);
for b in &l.blocks { // for b in &l.blocks {
let aabb = b.get_aabb(); // let aabb = b.get_aabb();
p.set_blocked_range( // p.set_blocked_range(
aabb.min().x as usize, // aabb.min().x as usize,
aabb.min().y as usize, // aabb.min().y as usize,
aabb.max().x as usize, // aabb.max().x as usize,
aabb.max().y as usize, // aabb.max().y as usize,
true, // true,
); // );
} // }
for c in &l.problem.connections { // for c in &l.problem.connections {
let start_transform = l.blocks[c.startblock].block_to_world(); // let start_transform = l.blocks[c.startblock].block_to_world();
let startpos = l.problem.blocks[c.startblock].output[c.startpoint] // let startpos = l.problem.blocks[c.startblock].output[c.startpoint]
.offset // .offset
.transform(start_transform); // .transform(start_transform);
let startdir = l.problem.blocks[c.startblock].output[c.startpoint] // let startdir = l.problem.blocks[c.startblock].output[c.startpoint]
.dir // .dir
.transform(start_transform); // .transform(start_transform);
let end_transform = l.blocks[c.endblock].block_to_world(); // let end_transform = l.blocks[c.endblock].block_to_world();
let endpos = l.problem.blocks[c.endblock].input[c.endpoint] // let endpos = l.problem.blocks[c.endblock].input[c.endpoint]
.offset // .offset
.transform(end_transform); // .transform(end_transform);
let enddir = l.problem.blocks[c.endblock].input[c.endpoint] // let enddir = l.problem.blocks[c.endblock].input[c.endpoint]
.dir // .dir
.transform(end_transform); // .transform(end_transform);
p.add_connection( // p.add_connection(
(startpos, startdir), // (startpos, startdir),
(endpos.in_direction(&enddir, -1), enddir), // (endpos.in_direction(&enddir, -1), enddir),
); // );
} // }
p // p
} // }
pub fn add_connection(&mut self, start: (Position, Direction), end: (Position, Direction)) { pub fn add_connection(&mut self, start: (Position, Direction), end: (Position, Direction)) {
self.start.push(start); self.start.push(start);

View file

@ -1,5 +1,4 @@
pub mod belt_finding; pub mod belt_finding;
pub mod graph; pub mod graph;
pub mod layout;
pub mod misc; pub mod misc;
pub mod priority_queue; pub mod priority_queue;