Initial layout implementation.
This commit is contained in:
parent
1596bf180d
commit
f284b692cc
7 changed files with 374 additions and 6 deletions
88
Cargo.lock
generated
88
Cargo.lock
generated
|
|
@ -100,6 +100,12 @@ version = "3.15.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "cast"
|
||||
version = "0.3.0"
|
||||
|
|
@ -274,6 +280,7 @@ dependencies = [
|
|||
"clap 4.5.3",
|
||||
"criterion",
|
||||
"flate2",
|
||||
"rand",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"termcolor",
|
||||
|
|
@ -289,6 +296,17 @@ dependencies = [
|
|||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "1.8.3"
|
||||
|
|
@ -342,9 +360,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.153"
|
||||
version = "0.2.158"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
||||
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
|
|
@ -416,6 +434,15 @@ dependencies = [
|
|||
"plotters-backend",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.79"
|
||||
|
|
@ -434,6 +461,36 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.9.0"
|
||||
|
|
@ -612,6 +669,12 @@ dependencies = [
|
|||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.92"
|
||||
|
|
@ -772,3 +835,24 @@ name = "windows_x86_64_msvc"
|
|||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ harness = false
|
|||
base64 = "0.21.5"
|
||||
clap = { version = "4.4.8", features = ["derive"] }
|
||||
flate2 = "1.0.28"
|
||||
rand = { version = "0.8.5", features = ["small_rng"] }
|
||||
serde = { version = "1.0.192", features = ["derive"] }
|
||||
serde_json = "1.0.108"
|
||||
termcolor = "1.4.1"
|
||||
|
|
|
|||
25
examples/layout.rs
Normal file
25
examples/layout.rs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
use factorio_blueprint::{
|
||||
belt_finding::common::Position,
|
||||
layout::{Layout, Problem},
|
||||
};
|
||||
use rand::SeedableRng;
|
||||
|
||||
fn main() {
|
||||
let mut p = Problem::new(Position::new(10, 10));
|
||||
|
||||
let b1 = p.add_block(Position::new(3, 2));
|
||||
let b2 = p.add_block(Position::new(5, 2));
|
||||
let b3 = p.add_block(Position::new(5, 7));
|
||||
|
||||
p.add_connection(b1, Position::new(0, 0), b2, Position::new(0, 0));
|
||||
p.add_connection(b2, Position::new(3, 1), b3, Position::new(4, 6));
|
||||
|
||||
for i in 0..10 {
|
||||
let mut rng = rand::rngs::SmallRng::seed_from_u64(i);
|
||||
|
||||
let l = Layout::new(&p, &mut rng);
|
||||
|
||||
println!("Seed: {i}, Score {}", l.score());
|
||||
l.print();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
use core::panic;
|
||||
use std::io::{self, Write};
|
||||
|
||||
use rand::prelude::Distribution;
|
||||
use termcolor::{ColorSpec, StandardStream, WriteColor};
|
||||
|
||||
pub type PositionType = i32;
|
||||
|
|
@ -89,6 +90,19 @@ impl Direction {
|
|||
}
|
||||
}
|
||||
|
||||
impl Distribution<Direction> for rand::distributions::Standard {
|
||||
fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> Direction {
|
||||
let a = [
|
||||
Direction::Up,
|
||||
Direction::Right,
|
||||
Direction::Down,
|
||||
Direction::Left,
|
||||
];
|
||||
let r = rng.gen_range(0..4);
|
||||
a[r]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Position {
|
||||
pub x: PositionType,
|
||||
|
|
@ -132,6 +146,16 @@ impl Position {
|
|||
}
|
||||
}
|
||||
|
||||
impl std::ops::Add for Position {
|
||||
type Output = Position;
|
||||
|
||||
fn add(mut self, rhs: Self) -> Self::Output {
|
||||
self.x += rhs.x;
|
||||
self.y += rhs.y;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Sub for Position {
|
||||
type Output = Position;
|
||||
|
||||
|
|
@ -208,8 +232,8 @@ where
|
|||
{
|
||||
let stdout = &mut StandardStream::stdout(termcolor::ColorChoice::Always);
|
||||
|
||||
let width_digits = width.ilog10() + 1;
|
||||
let height_digits = height.ilog10() + 1;
|
||||
let width_digits = (width - 1).ilog10() + 1;
|
||||
let height_digits = (height - 1).ilog10() + 1;
|
||||
|
||||
// print header
|
||||
for i in 0..width_digits {
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ impl Problem {
|
|||
}
|
||||
}
|
||||
|
||||
static COLORS: Cyclic<Color, 6> = Cyclic([
|
||||
pub static COLORS: Cyclic<Color, 6> = Cyclic([
|
||||
Color::Red,
|
||||
Color::Green,
|
||||
Color::Yellow,
|
||||
|
|
@ -137,7 +137,7 @@ static COLORS: Cyclic<Color, 6> = Cyclic([
|
|||
Color::Cyan,
|
||||
]);
|
||||
|
||||
struct Cyclic<T, const N: usize>([T; N]);
|
||||
pub struct Cyclic<T, const N: usize>([T; N]);
|
||||
|
||||
impl<T, const N: usize> Index<usize> for Cyclic<T, N> {
|
||||
type Output = T;
|
||||
|
|
|
|||
233
src/layout/mod.rs
Normal file
233
src/layout/mod.rs
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
use crate::{
|
||||
belt_finding::{
|
||||
common::{print_map, Direction, Position},
|
||||
COLORS,
|
||||
},
|
||||
misc::Map,
|
||||
};
|
||||
|
||||
use rand::Rng;
|
||||
use termcolor::ColorSpec;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Block {
|
||||
size: Position,
|
||||
input: Vec<Interface>,
|
||||
output: Vec<Interface>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Interface {
|
||||
offset: Position,
|
||||
// dir: Direction,
|
||||
target: (usize, usize),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Problem {
|
||||
size: Position,
|
||||
blocks: Vec<Block>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct BlockHandle(usize);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Layout<'a> {
|
||||
problem: &'a Problem,
|
||||
blocks: Vec<(Position, Direction)>,
|
||||
}
|
||||
|
||||
impl Problem {
|
||||
pub fn new(size: Position) -> Self {
|
||||
Self {
|
||||
size,
|
||||
blocks: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_block(&mut self, size: Position) -> BlockHandle {
|
||||
self.blocks.push(Block {
|
||||
size,
|
||||
input: Vec::new(),
|
||||
output: Vec::new(),
|
||||
});
|
||||
|
||||
BlockHandle(self.blocks.len() - 1)
|
||||
}
|
||||
|
||||
pub fn add_connection(
|
||||
&mut self,
|
||||
starthandle: BlockHandle,
|
||||
startoffset: Position,
|
||||
endhandle: BlockHandle,
|
||||
endoffset: Position,
|
||||
) {
|
||||
let startinterface = self.blocks[starthandle.0].output.len();
|
||||
let endinterface = self.blocks[endhandle.0].input.len();
|
||||
|
||||
self.blocks[starthandle.0].output.push(Interface {
|
||||
offset: startoffset,
|
||||
target: (endhandle.0, endinterface),
|
||||
});
|
||||
|
||||
self.blocks[endhandle.0].input.push(Interface {
|
||||
offset: endoffset,
|
||||
target: (starthandle.0, startinterface),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Layout<'_> {
|
||||
/// Create a new valid layout
|
||||
pub fn new<'a, R: Rng + ?Sized>(problem: &'a Problem, rng: &'_ mut R) -> Layout<'a> {
|
||||
let mut blocks = Vec::new();
|
||||
|
||||
assert!(Self::place_block(problem, &mut blocks, rng));
|
||||
|
||||
Layout { problem, blocks }
|
||||
}
|
||||
|
||||
fn place_block<R: Rng + ?Sized>(
|
||||
problem: &'_ Problem,
|
||||
blocks: &mut Vec<(Position, Direction)>,
|
||||
rng: &'_ mut R,
|
||||
) -> bool {
|
||||
if problem.blocks.len() == blocks.len() {
|
||||
return true;
|
||||
}
|
||||
|
||||
let b = &problem.blocks[blocks.len()];
|
||||
for _ in 0..1000 {
|
||||
let dir = rng.gen::<Direction>();
|
||||
|
||||
let pos = match dir {
|
||||
Direction::Up => Position::new(
|
||||
rng.gen_range(0..=(problem.size.x - b.size.x)),
|
||||
rng.gen_range(0..=(problem.size.y - b.size.y)),
|
||||
),
|
||||
Direction::Right => Position::new(
|
||||
rng.gen_range((b.size.y - 1)..problem.size.x),
|
||||
rng.gen_range(0..=(problem.size.y - b.size.x)),
|
||||
),
|
||||
Direction::Down => Position::new(
|
||||
rng.gen_range((b.size.x - 1)..problem.size.x),
|
||||
rng.gen_range((b.size.y - 1)..problem.size.y),
|
||||
),
|
||||
Direction::Left => Position::new(
|
||||
rng.gen_range(0..=(problem.size.x - b.size.y)),
|
||||
rng.gen_range((b.size.x - 1)..problem.size.y),
|
||||
),
|
||||
};
|
||||
|
||||
if blocks
|
||||
.iter()
|
||||
.enumerate()
|
||||
.all(|(i, (p, d))| !Self::collision((&problem.blocks[i], *p, *d), (b, pos, dir)))
|
||||
{
|
||||
blocks.push((pos, dir));
|
||||
|
||||
if Self::place_block(problem, blocks, rng) {
|
||||
return true;
|
||||
}
|
||||
|
||||
blocks.pop();
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Mutate existing layout, creating a valid layout
|
||||
pub fn mutate<R: Rng + ?Sized>(&self, rng: &mut R) -> Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn collision(
|
||||
block1: (&Block, Position, Direction),
|
||||
block2: (&Block, Position, Direction),
|
||||
) -> bool {
|
||||
let (npos1, nsize1) = Self::normalize_pos(block1);
|
||||
let (npos2, nsize2) = Self::normalize_pos(block2);
|
||||
|
||||
npos1.x < npos2.x + nsize2.x
|
||||
&& npos1.x + nsize1.x > npos2.x
|
||||
&& npos1.y < npos2.y + nsize2.y
|
||||
&& npos1.y + nsize1.y > npos2.y
|
||||
}
|
||||
|
||||
fn normalize_pos(block: (&Block, Position, Direction)) -> (Position, Position) {
|
||||
let npos = match block.2 {
|
||||
Direction::Up => block.1,
|
||||
Direction::Right => block.1.in_direction(&Direction::Left, block.0.size.y - 1),
|
||||
Direction::Down => block.1 - (block.0.size - Position::new(1, 1)),
|
||||
Direction::Left => block.1.in_direction(&Direction::Up, block.0.size.x - 1),
|
||||
};
|
||||
let nsize = match block.2 {
|
||||
Direction::Up | Direction::Down => block.0.size,
|
||||
Direction::Right | Direction::Left => Position {
|
||||
x: block.0.size.y,
|
||||
y: block.0.size.x,
|
||||
},
|
||||
};
|
||||
|
||||
(npos, nsize)
|
||||
}
|
||||
|
||||
pub fn score(&self) -> i32 {
|
||||
let mut sum = 0;
|
||||
for ((pos, dir), b) in self.blocks.iter().zip(self.problem.blocks.iter()) {
|
||||
for i in &b.output {
|
||||
let startpos = Self::transform(*pos, *dir, i.offset);
|
||||
let endpos = Self::transform(
|
||||
self.blocks[i.target.0].0,
|
||||
self.blocks[i.target.0].1,
|
||||
self.problem.blocks[i.target.0].input[i.target.1].offset,
|
||||
);
|
||||
|
||||
sum += (startpos.x - endpos.x).abs() + (startpos.y - endpos.y).abs();
|
||||
}
|
||||
}
|
||||
|
||||
sum
|
||||
}
|
||||
|
||||
fn transform(pos: Position, dir: Direction, offset: Position) -> Position {
|
||||
match dir {
|
||||
Direction::Up => pos + offset,
|
||||
Direction::Right => pos + Position::new(-offset.y, offset.x),
|
||||
Direction::Down => pos - offset,
|
||||
Direction::Left => pos + Position::new(offset.y, -offset.x),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print(&self) {
|
||||
let mut m: Map<Option<usize>> =
|
||||
Map::new(self.problem.size.x as usize, self.problem.size.y as usize);
|
||||
|
||||
for (i, ((p, d), b)) in self
|
||||
.blocks
|
||||
.iter()
|
||||
.zip(self.problem.blocks.iter())
|
||||
.enumerate()
|
||||
{
|
||||
let (npos, nsize) = Self::normalize_pos((b, *p, *d));
|
||||
|
||||
for x in npos.x..(npos.x + nsize.x) {
|
||||
for y in npos.y..(npos.y + nsize.y) {
|
||||
m.set(x as usize, y as usize, Some(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let _ = print_map(self.problem.size.x, self.problem.size.y, |x, y| {
|
||||
if let Some(i) = m.get(x as usize, y as usize) {
|
||||
let mut color = ColorSpec::new();
|
||||
color.set_fg(Some(COLORS[*i]));
|
||||
(color, "#")
|
||||
} else {
|
||||
(ColorSpec::new(), " ")
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
pub mod belt_finding;
|
||||
pub mod blueprint;
|
||||
pub mod graph;
|
||||
pub mod layout;
|
||||
pub mod misc;
|
||||
pub mod priority_queue;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue