Add Layout trait
This commit is contained in:
parent
00eda50872
commit
295490858b
12 changed files with 407 additions and 41 deletions
49
factorio-layout/new_layout.yaml
Normal file
49
factorio-layout/new_layout.yaml
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
|
||||
size:
|
||||
x: 15
|
||||
y: 15
|
||||
blocks:
|
||||
- size:
|
||||
x: 3
|
||||
y: 2
|
||||
input:
|
||||
- offset:
|
||||
x: 1
|
||||
y: 1
|
||||
dir: Up
|
||||
output:
|
||||
- offset:
|
||||
x: 1
|
||||
y: 0
|
||||
dir: Up
|
||||
- size:
|
||||
x: 5
|
||||
y: 2
|
||||
input:
|
||||
output:
|
||||
- offset:
|
||||
x: 1
|
||||
y: 1
|
||||
dir: Down
|
||||
- size:
|
||||
x: 5
|
||||
y: 7
|
||||
input:
|
||||
- offset:
|
||||
x: 0
|
||||
y: 1
|
||||
dir: Right
|
||||
output:
|
||||
connections:
|
||||
- startblock: 1
|
||||
startpoint: 0
|
||||
endblock: 0
|
||||
endpoint: 0
|
||||
lanes: 1
|
||||
beltspeed: Normal
|
||||
- startblock: 0
|
||||
startpoint: 0
|
||||
endblock: 2
|
||||
endpoint: 0
|
||||
lanes: 1
|
||||
beltspeed: Normal
|
||||
42
factorio-layout/src/bin/new_layout.rs
Normal file
42
factorio-layout/src/bin/new_layout.rs
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use clap::Parser;
|
||||
use factorio_core::prelude::Position;
|
||||
use factorio_layout::{Layouter, valid_layout::ValidLayout};
|
||||
use factorio_pathfinding::belt_finding::ConflictAvoidance;
|
||||
use rand::{SeedableRng, rngs::SmallRng};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
struct Args {
|
||||
#[clap(short, long, default_value_t = 0)]
|
||||
seed: u64,
|
||||
|
||||
path: PathBuf,
|
||||
// #[command(subcommand)]
|
||||
// subcommand: Commands,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = Args::parse();
|
||||
|
||||
let file = std::fs::File::open(args.path).unwrap();
|
||||
|
||||
let problem = serde_yaml::from_reader(file).unwrap();
|
||||
|
||||
let l = ValidLayout {
|
||||
max_tries: 100,
|
||||
retries: 10,
|
||||
start_size: Position::new(10, 10),
|
||||
growth: Position::new(2, 2),
|
||||
};
|
||||
|
||||
let p = ConflictAvoidance {
|
||||
timeout: Some(std::time::Duration::from_millis(100)),
|
||||
};
|
||||
|
||||
let mut rng = SmallRng::seed_from_u64(args.seed);
|
||||
|
||||
let r = l.layout(&problem, &p, &mut rng);
|
||||
|
||||
dbg!(r);
|
||||
}
|
||||
0
factorio-layout/src/genetic_algorithm_v1.rs
Normal file
0
factorio-layout/src/genetic_algorithm_v1.rs
Normal file
21
factorio-layout/src/genetic_algorithm_v2.rs
Normal file
21
factorio-layout/src/genetic_algorithm_v2.rs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
use factorio_pathfinding::Pathfinder;
|
||||
use rand::Rng;
|
||||
|
||||
use crate::Layouter;
|
||||
|
||||
pub struct GeneticAlgorithmV2 {
|
||||
pub new_layouts: usize,
|
||||
pub mutation_timeout: usize,
|
||||
pub max_mutations: usize,
|
||||
}
|
||||
|
||||
impl Layouter for GeneticAlgorithmV2 {
|
||||
fn layout<R: Rng, P: Pathfinder>(
|
||||
&self,
|
||||
input: &crate::LayoutInput,
|
||||
pathfinder: &P,
|
||||
rng: &mut R,
|
||||
) -> Option<crate::LayoutResult> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +1,55 @@
|
|||
use factorio_core::{beltoptions::Beltspeed, pathfield::PathField, prelude::*};
|
||||
use factorio_pathfinding::Pathfinder;
|
||||
use rand::Rng;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub mod block;
|
||||
pub mod genetic_algorithm_v1;
|
||||
pub mod genetic_algorithm_v2;
|
||||
pub mod layout;
|
||||
pub mod misc;
|
||||
pub mod valid_layout;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct MacroBlock {
|
||||
pub size: Position,
|
||||
pub input: Vec<Interface>,
|
||||
pub output: Vec<Interface>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Interface {
|
||||
pub offset: Position,
|
||||
pub dir: Direction,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Connection {
|
||||
pub startblock: usize,
|
||||
pub startpoint: usize,
|
||||
pub endblock: usize,
|
||||
pub endpoint: usize,
|
||||
pub lanes: usize,
|
||||
pub beltspeed: Beltspeed,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct LayoutInput {
|
||||
pub blocks: Vec<MacroBlock>,
|
||||
pub connections: Vec<Connection>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LayoutResult {
|
||||
pub positions: Vec<Block>,
|
||||
pub path_result: Vec<Vec<PathField>>,
|
||||
}
|
||||
|
||||
pub trait Layouter {
|
||||
fn layout<R: Rng, P: Pathfinder>(
|
||||
&self,
|
||||
input: &LayoutInput,
|
||||
pathfinder: &P,
|
||||
rng: &mut R,
|
||||
) -> Option<LayoutResult>;
|
||||
}
|
||||
|
|
|
|||
119
factorio-layout/src/misc.rs
Normal file
119
factorio-layout/src/misc.rs
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
use crate::LayoutInput;
|
||||
use factorio_core::prelude::*;
|
||||
use factorio_pathfinding::{Connection, PathInput, examples::HashMapMap};
|
||||
use rand::Rng;
|
||||
|
||||
pub fn initally_set_blocks(
|
||||
input: &LayoutInput,
|
||||
size: Position,
|
||||
retries: usize,
|
||||
rng: &mut impl Rng,
|
||||
) -> Option<Vec<Block>> {
|
||||
let mut blocks = Vec::new();
|
||||
|
||||
let mut aabbs = Vec::new();
|
||||
|
||||
if place_block(input, size, retries, &mut blocks, &mut aabbs, rng) {
|
||||
Some(blocks)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn place_block(
|
||||
input: &LayoutInput,
|
||||
size: Position,
|
||||
retries: usize,
|
||||
blocks: &mut Vec<Block>,
|
||||
aabbs: &mut Vec<AABB>,
|
||||
rng: &mut impl Rng,
|
||||
) -> bool {
|
||||
if input.blocks.len() == blocks.len() {
|
||||
return true;
|
||||
}
|
||||
|
||||
let b = &input.blocks[blocks.len()];
|
||||
for _ in 0..retries {
|
||||
let dir = rng.r#gen::<Direction>();
|
||||
|
||||
let pos = match dir {
|
||||
Direction::Up => Position::new(
|
||||
rng.gen_range(0..=(size.x - b.size.x)),
|
||||
rng.gen_range(0..=(size.y - b.size.y)),
|
||||
),
|
||||
Direction::Right => Position::new(
|
||||
rng.gen_range((b.size.y - 1)..size.x),
|
||||
rng.gen_range(0..=(size.y - b.size.x)),
|
||||
),
|
||||
Direction::Down => Position::new(
|
||||
rng.gen_range((b.size.x - 1)..size.x),
|
||||
rng.gen_range((b.size.y - 1)..size.y),
|
||||
),
|
||||
Direction::Left => Position::new(
|
||||
rng.gen_range(0..=(size.x - b.size.y)),
|
||||
rng.gen_range((b.size.x - 1)..size.y),
|
||||
),
|
||||
};
|
||||
|
||||
let current = Block::new(pos, dir, b.size);
|
||||
let current_aabb = current.get_aabb();
|
||||
|
||||
if aabbs.iter().all(|&b| !AABB::collision(b, current_aabb)) {
|
||||
blocks.push(current);
|
||||
aabbs.push(current_aabb);
|
||||
|
||||
if place_block(input, size, retries, blocks, aabbs, rng) {
|
||||
return true;
|
||||
}
|
||||
|
||||
blocks.pop();
|
||||
aabbs.pop();
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn path_input_from_blocks_positions(
|
||||
input: &LayoutInput,
|
||||
size: Position,
|
||||
block_positions: &[Block],
|
||||
) -> (Vec<Connection>, HashMapMap) {
|
||||
let mut map = HashMapMap::new(size);
|
||||
|
||||
let mut connections = Vec::new();
|
||||
|
||||
for b in block_positions {
|
||||
let aabb = b.get_aabb();
|
||||
|
||||
map.set_blocked_range(aabb.min(), aabb.max(), true);
|
||||
}
|
||||
|
||||
for c in &input.connections {
|
||||
let start_transform = block_positions[c.startblock].block_to_world();
|
||||
let start_pos = input.blocks[c.startblock].output[c.startpoint]
|
||||
.offset
|
||||
.transform(start_transform);
|
||||
let start_dir = input.blocks[c.startblock].output[c.startpoint]
|
||||
.dir
|
||||
.transform(start_transform);
|
||||
let end_transform = block_positions[c.endblock].block_to_world();
|
||||
let end_pos = input.blocks[c.endblock].input[c.endpoint]
|
||||
.offset
|
||||
.transform(end_transform);
|
||||
let end_dir = input.blocks[c.endblock].input[c.endpoint]
|
||||
.dir
|
||||
.transform(end_transform);
|
||||
|
||||
connections.push(Connection {
|
||||
start_pos,
|
||||
start_dir,
|
||||
end_pos,
|
||||
end_dir,
|
||||
beltspeed: c.beltspeed,
|
||||
lanes: c.lanes,
|
||||
});
|
||||
}
|
||||
|
||||
(connections, map)
|
||||
}
|
||||
44
factorio-layout/src/valid_layout.rs
Normal file
44
factorio-layout/src/valid_layout.rs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
use factorio_core::prelude::{Position, PositionType};
|
||||
use factorio_pathfinding::Pathfinder;
|
||||
use rand::Rng;
|
||||
|
||||
use crate::{
|
||||
LayoutResult, Layouter,
|
||||
misc::{initally_set_blocks, path_input_from_blocks_positions},
|
||||
};
|
||||
|
||||
pub struct ValidLayout {
|
||||
pub max_tries: usize,
|
||||
pub retries: usize,
|
||||
pub start_size: Position,
|
||||
pub growth: Position,
|
||||
}
|
||||
|
||||
impl Layouter for ValidLayout {
|
||||
fn layout<R: Rng, P: Pathfinder>(
|
||||
&self,
|
||||
input: &crate::LayoutInput,
|
||||
pathfinder: &P,
|
||||
rng: &mut R,
|
||||
) -> Option<LayoutResult> {
|
||||
for i in 0..self.max_tries {
|
||||
let size = self.start_size + i as PositionType * self.growth;
|
||||
|
||||
if let Some(blocks) = initally_set_blocks(input, size, self.retries, rng) {
|
||||
let (connections, map) = path_input_from_blocks_positions(input, size, &blocks);
|
||||
|
||||
if let Some(paths) = pathfinder.find_paths(factorio_pathfinding::PathInput {
|
||||
connections: &connections,
|
||||
map,
|
||||
}) {
|
||||
return Some(LayoutResult {
|
||||
positions: blocks,
|
||||
path_result: paths,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue