Cleanup
This commit is contained in:
parent
0df1c9f32c
commit
ebab3fe944
@ -1,6 +1,6 @@
|
|||||||
use svg::node::element::path::Data;
|
use svg::node::element::path::Data;
|
||||||
use svg::node::element::Path;
|
|
||||||
use svg::node::element::Circle;
|
use svg::node::element::Circle;
|
||||||
|
use svg::node::element::Path;
|
||||||
use svg::Document;
|
use svg::Document;
|
||||||
use svg::Node;
|
use svg::Node;
|
||||||
|
|
||||||
@ -13,29 +13,42 @@ fn idx_to_char(idx: usize) -> char {
|
|||||||
0 => 'A',
|
0 => 'A',
|
||||||
1 => 'B',
|
1 => 'B',
|
||||||
2 => 'C',
|
2 => 'C',
|
||||||
_ => unreachable!()
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn save_info(filename: &str, world: &World, tris: &[Triangle], original_tris: &[Triangle]) {
|
pub(crate) fn save_info(
|
||||||
|
filename: &str,
|
||||||
|
world: &World,
|
||||||
|
tris: &[Triangle],
|
||||||
|
original_tris: &[Triangle],
|
||||||
|
) {
|
||||||
let mut text = format!("Gesamtabstand: {:.0}\n", world.width);
|
let mut text = format!("Gesamtabstand: {:.0}\n", world.width);
|
||||||
for (idx, (_, tri)) in world.tris.iter().enumerate() {
|
for (idx, (_, tri)) in world.tris.iter().enumerate() {
|
||||||
let unrotated_tri = tris[idx];
|
let unrotated_tri = tris[idx];
|
||||||
let original_idx = original_tris.iter().position(|x| x.0 == unrotated_tri.0 && x.1 == unrotated_tri.1 && x.2 == unrotated_tri.2).unwrap();
|
let original_idx = original_tris
|
||||||
|
.iter()
|
||||||
|
.position(|x| {
|
||||||
|
x.0 == unrotated_tri.0 && x.1 == unrotated_tri.1 && x.2 == unrotated_tri.2
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
text += &format!("D{}:", original_idx);
|
text += &format!("D{}:", original_idx);
|
||||||
let mut coordinates = vec![tri.0, tri.1, tri.2];
|
let mut coordinates = vec![tri.0, tri.1, tri.2];
|
||||||
coordinates.sort_by_key(|x| (x.x * 100.0) as u32);
|
coordinates.sort_by_key(|x| (x.x * 100.0) as u32);
|
||||||
for (idx, c) in coordinates.into_iter().enumerate() {
|
for (idx, c) in coordinates.into_iter().enumerate() {
|
||||||
text += &format!(" D{}{}[{:.0} {:.0}]", original_idx, idx_to_char(idx), c.x, c.y);
|
text += &format!(
|
||||||
|
" D{}{}[{:.0} {:.0}]",
|
||||||
|
original_idx,
|
||||||
|
idx_to_char(idx),
|
||||||
|
c.x,
|
||||||
|
c.y
|
||||||
|
);
|
||||||
}
|
}
|
||||||
text += "\n";
|
text += "\n";
|
||||||
}
|
}
|
||||||
fs::write(filename, text).unwrap();
|
fs::write(filename, text).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn save_tri(filename: &str, tri: Triangle) {
|
|
||||||
fs::write(filename, generate_svg(&[tri])).unwrap();
|
|
||||||
}
|
|
||||||
pub(crate) fn save_world(filename: &str, world: &World) {
|
pub(crate) fn save_world(filename: &str, world: &World) {
|
||||||
fs::write(
|
fs::write(
|
||||||
filename,
|
filename,
|
||||||
|
186
src/main.rs
186
src/main.rs
@ -2,7 +2,6 @@ use geo::prelude::*;
|
|||||||
use rand::distributions::{Bernoulli, Distribution};
|
use rand::distributions::{Bernoulli, Distribution};
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
|
|
||||||
use std::cmp;
|
|
||||||
use std::f32;
|
use std::f32;
|
||||||
use std::f32::consts::PI;
|
use std::f32::consts::PI;
|
||||||
|
|
||||||
@ -11,6 +10,7 @@ mod input;
|
|||||||
|
|
||||||
type Triangle = geo::Triangle<f32>;
|
type Triangle = geo::Triangle<f32>;
|
||||||
|
|
||||||
|
/// Datenstruktur für eine (evtl. unvollständige) Anordnung
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct World {
|
struct World {
|
||||||
/// Dreiecksgrundstücke
|
/// Dreiecksgrundstücke
|
||||||
@ -18,82 +18,35 @@ struct World {
|
|||||||
/// Gesamtabstand
|
/// Gesamtabstand
|
||||||
width: f32,
|
width: f32,
|
||||||
}
|
}
|
||||||
impl cmp::PartialEq for World {
|
|
||||||
fn eq(&self, _other: &Self) -> bool {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl cmp::Eq for World {}
|
|
||||||
impl cmp::Ord for World {
|
|
||||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
|
||||||
self.width.partial_cmp(&other.width).unwrap().reverse()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl cmp::PartialOrd for World {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
|
||||||
Some(self.cmp(other))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn random_world(rng: &mut SmallRng, tris: &mut Vec<Triangle>, angles: &mut [f32], flips: &mut Vec<bool>) -> World {
|
/// Erzeugt eine zufällig konfigurierte Welt. Die Reihenfolge der Grundstücke als auch Winkel und Spiegelungen werden beliebig festgelegt.
|
||||||
|
fn random_world(
|
||||||
|
rng: &mut SmallRng,
|
||||||
|
tris: &mut Vec<Triangle>,
|
||||||
|
angles: &mut [f32],
|
||||||
|
flips: &mut Vec<bool>,
|
||||||
|
) -> World {
|
||||||
tris.shuffle(rng);
|
tris.shuffle(rng);
|
||||||
let mut world = World { tris: Vec::with_capacity(tris.len()), width: 0.0 };
|
// je ein Dreieck hinzufügen
|
||||||
for (idx, tri) in tris.iter().enumerate() {
|
for idx in 0..tris.len() {
|
||||||
let mut tri = *tri;
|
// zufällige Werte berechnen und anwenden
|
||||||
flips[idx] = rng.gen();
|
|
||||||
if flips[idx] {
|
|
||||||
tri.0.x *= -1.0;
|
|
||||||
tri.1.x *= -1.0;
|
|
||||||
tri.2.x *= -1.0;
|
|
||||||
}
|
|
||||||
angles[idx] = rng.gen_range(0.0, 2.0 * PI);
|
angles[idx] = rng.gen_range(0.0, 2.0 * PI);
|
||||||
tri = rotate(tri, angles[idx]);
|
flips[idx] = rng.gen();
|
||||||
let minx = min(min(tri.0.x, tri.1.x), tri.2.x);
|
|
||||||
tri.0.x -= minx;
|
|
||||||
tri.1.x -= minx;
|
|
||||||
tri.2.x -= minx;
|
|
||||||
let miny = min(min(tri.0.y, tri.1.y), tri.2.y);
|
|
||||||
tri.0.y -= miny;
|
|
||||||
tri.1.y -= miny;
|
|
||||||
tri.2.y -= miny;
|
|
||||||
if world.tris.is_empty() {
|
|
||||||
world.tris.push((0, tri));
|
|
||||||
world.normalize();
|
|
||||||
} else {
|
|
||||||
//tri.0.x += 1.0;
|
|
||||||
//tri.1.x += 1.0;
|
|
||||||
//tri.2.x += 1.0;
|
|
||||||
for &delta in &[
|
|
||||||
/*10.0, 5.0,*/ 1.0, 0.6, 0.32, 0.18, 0.1, 0.06, 0.03, 0.01
|
|
||||||
] {
|
|
||||||
//eprintln!("moving {:?}", tri);
|
|
||||||
//eprintln!("world {:?}", world);
|
|
||||||
let initial_state = world.tris[0..idx].iter().any(|(_, x)| x.intersects(&tri));
|
|
||||||
let dx = if initial_state { delta } else { -delta };
|
|
||||||
while world.tris[0..idx].iter().any(|(_, x)| x.intersects(&tri)) == initial_state {
|
|
||||||
tri.0.x += dx;
|
|
||||||
tri.1.x += dx;
|
|
||||||
tri.2.x += dx;
|
|
||||||
if tri.0.x < -1000.0 {
|
|
||||||
tri.0.x += 1000.0;
|
|
||||||
tri.1.x += 1000.0;
|
|
||||||
tri.2.x += 1000.0;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
construct_world(&tris, angles, flips)
|
||||||
}
|
|
||||||
world.tris.push((0, tri));
|
|
||||||
world.normalize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
world.calc_width();
|
|
||||||
world
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Welt mit vorgegebener Reihenfolge und Konfiguration (Winkel, Spiegelungen) erstellen.
|
||||||
fn construct_world(tris: &[Triangle], angles: &[f32], flips: &[bool]) -> World {
|
fn construct_world(tris: &[Triangle], angles: &[f32], flips: &[bool]) -> World {
|
||||||
let mut world = World { tris: Vec::with_capacity(tris.len()), width: 0.0 };
|
let mut world = World {
|
||||||
|
tris: Vec::with_capacity(tris.len()),
|
||||||
|
width: 0.0,
|
||||||
|
};
|
||||||
|
// jedes Dreieck einzeln hinzufügen
|
||||||
for (idx, tri) in tris.iter().enumerate() {
|
for (idx, tri) in tris.iter().enumerate() {
|
||||||
|
// Kopie anlegen
|
||||||
let mut tri = *tri;
|
let mut tri = *tri;
|
||||||
|
// Werte anwenden
|
||||||
if flips[idx] {
|
if flips[idx] {
|
||||||
tri.0.x *= -1.0;
|
tri.0.x *= -1.0;
|
||||||
tri.1.x *= -1.0;
|
tri.1.x *= -1.0;
|
||||||
@ -112,20 +65,15 @@ fn construct_world(tris: &[Triangle], angles: &[f32], flips: &[bool]) -> World {
|
|||||||
world.tris.push((0, tri));
|
world.tris.push((0, tri));
|
||||||
world.normalize();
|
world.normalize();
|
||||||
} else {
|
} else {
|
||||||
//tri.0.x += 1.0;
|
// Dreieck neben den anderen platzieren
|
||||||
//tri.1.x += 1.0;
|
for &delta in &[1.0, 0.6, 0.32, 0.18, 0.1, 0.06, 0.03, 0.01] {
|
||||||
//tri.2.x += 1.0;
|
|
||||||
for &delta in &[
|
|
||||||
/*10.0, 5.0,*/ 1.0, 0.6, 0.32, 0.18, 0.1, 0.06, 0.03, 0.01
|
|
||||||
] {
|
|
||||||
//eprintln!("moving {:?}", tri);
|
|
||||||
//eprintln!("world {:?}", world);
|
|
||||||
let initial_state = world.tris[0..idx].iter().any(|(_, x)| x.intersects(&tri));
|
let initial_state = world.tris[0..idx].iter().any(|(_, x)| x.intersects(&tri));
|
||||||
let dx = if initial_state { delta } else { -delta };
|
let dx = if initial_state { delta } else { -delta };
|
||||||
while world.tris[0..idx].iter().any(|(_, x)| x.intersects(&tri)) == initial_state {
|
while world.tris[0..idx].iter().any(|(_, x)| x.intersects(&tri)) == initial_state {
|
||||||
tri.0.x += dx;
|
tri.0.x += dx;
|
||||||
tri.1.x += dx;
|
tri.1.x += dx;
|
||||||
tri.2.x += dx;
|
tri.2.x += dx;
|
||||||
|
// "entlaufene" Dreiecke einfangen
|
||||||
if tri.0.x < -1000.0 {
|
if tri.0.x < -1000.0 {
|
||||||
tri.0.x += 1000.0;
|
tri.0.x += 1000.0;
|
||||||
tri.1.x += 1000.0;
|
tri.1.x += 1000.0;
|
||||||
@ -144,6 +92,7 @@ fn construct_world(tris: &[Triangle], angles: &[f32], flips: &[bool]) -> World {
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut tris = input::read_input();
|
let mut tris = input::read_input();
|
||||||
|
// Dreiecke zum Ursprung verschieben
|
||||||
for tri in &mut tris {
|
for tri in &mut tris {
|
||||||
tri.1.x -= tri.0.x;
|
tri.1.x -= tri.0.x;
|
||||||
tri.2.x -= tri.0.x;
|
tri.2.x -= tri.0.x;
|
||||||
@ -155,48 +104,74 @@ fn main() {
|
|||||||
let original_tris = tris.clone();
|
let original_tris = tris.clone();
|
||||||
let mut best = f32::MAX;
|
let mut best = f32::MAX;
|
||||||
let mut i = 0u64;
|
let mut i = 0u64;
|
||||||
let save_prefix = format!("rand_sel_{}_", random::<u64>());
|
let save_prefix = format!("rand_{}_", random::<u64>());
|
||||||
println!("save prefix: {}", save_prefix);
|
println!("Dateipräfix: {}", save_prefix);
|
||||||
loop {
|
loop {
|
||||||
i += 1;
|
i += 1;
|
||||||
let new_best = one_main(&mut tris.clone());
|
let new_best = find_layout(&mut tris.clone());
|
||||||
if new_best.width < best {
|
if new_best.width < best {
|
||||||
best = new_best.width;
|
best = new_best.width;
|
||||||
eprint!("\r");
|
eprint!("\r");
|
||||||
println!("[{}] BEST {:.3} ", i, best);
|
println!("[{}] BEST {:.3} ", i, best);
|
||||||
display::save_world(&format!("{}{}.svg", save_prefix, i), &new_best);
|
display::save_world(&format!("{}{}.svg", save_prefix, i), &new_best);
|
||||||
display::save_info(&format!("{}{}.txt", save_prefix, i), &new_best, &tris, &original_tris);
|
display::save_info(
|
||||||
|
&format!("{}{}.txt", save_prefix, i),
|
||||||
|
&new_best,
|
||||||
|
&tris,
|
||||||
|
&original_tris,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
eprint!("\r");
|
eprint!("\r");
|
||||||
println!("[{}] best {:.3} / prev. {:.3} ", i, new_best.width, best);
|
println!(
|
||||||
|
"[{}] best {:.3} ({:.3}) ",
|
||||||
|
i, new_best.width, best
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn one_main(tris: &mut Vec<Triangle>) -> World {
|
fn find_layout(tris: &mut Vec<Triangle>) -> World {
|
||||||
let mut angles = vec![0.0; tris.len()];
|
let mut angles = vec![0.0; tris.len()];
|
||||||
let mut flips = vec![false; tris.len()];
|
let mut flips = vec![false; tris.len()];
|
||||||
let mut rng = SmallRng::from_entropy();
|
let mut rng = SmallRng::from_entropy();
|
||||||
let mut best_width = f32::MAX;
|
let mut best_width = f32::MAX;
|
||||||
let mut best_all = f32::MAX;
|
|
||||||
let _random = random_world(&mut rng, tris, &mut angles, &mut flips);
|
let _random = random_world(&mut rng, tris, &mut angles, &mut flips);
|
||||||
optimize(69999, &mut best_all, &mut best_width, &tris, &mut angles, &mut flips, &mut rng, "rand_", 0)
|
optimize(
|
||||||
|
99999,
|
||||||
|
&mut best_width,
|
||||||
|
&tris,
|
||||||
|
&mut angles,
|
||||||
|
&mut flips,
|
||||||
|
&mut rng,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
fn optimize(
|
||||||
fn optimize(iters: usize, best_all: &mut f32, best_width: &mut f32, tris: &[Triangle], best_angles: &mut Vec<f32>, best_flips: &mut Vec<bool>, rng: &mut SmallRng, save_prefix: &'static str, save_counter: usize) -> World {
|
iters: usize,
|
||||||
let mut best = World { tris: vec![], width: f32::MAX };
|
best_width: &mut f32,
|
||||||
|
tris: &[Triangle],
|
||||||
|
best_angles: &mut Vec<f32>,
|
||||||
|
best_flips: &mut Vec<bool>,
|
||||||
|
rng: &mut SmallRng,
|
||||||
|
) -> World {
|
||||||
|
let mut best = World {
|
||||||
|
tris: vec![],
|
||||||
|
width: f32::MAX,
|
||||||
|
};
|
||||||
let mut found_better = true;
|
let mut found_better = true;
|
||||||
let mut iteration = 0;
|
let mut iteration = 0;
|
||||||
let flip_choice = Bernoulli::from_ratio(1, 11);
|
let flip_choice = Bernoulli::from_ratio(1, 11);
|
||||||
while found_better {
|
while found_better {
|
||||||
found_better = false;
|
found_better = false;
|
||||||
// slightly modify angles
|
// Winkel und Spiegelungen leicht verändern
|
||||||
let start_angles = best_angles.clone();
|
let start_angles = best_angles.clone();
|
||||||
let start_flips = best_flips.clone();
|
let start_flips = best_flips.clone();
|
||||||
let mut angles = start_angles.clone();
|
let mut angles = start_angles.clone();
|
||||||
let mut flips = start_flips.clone();
|
let mut flips = start_flips.clone();
|
||||||
for i in 0..iters {
|
for i in 0..iters {
|
||||||
|
if i % 10000 == 0 {
|
||||||
|
eprint!(".");
|
||||||
|
}
|
||||||
for a in &mut angles {
|
for a in &mut angles {
|
||||||
*a += rng.gen_range(-0.2, 0.2);
|
*a += rng.gen_range(-0.2, 0.2);
|
||||||
}
|
}
|
||||||
@ -205,19 +180,11 @@ fn optimize(iters: usize, best_all: &mut f32, best_width: &mut f32, tris: &[Tria
|
|||||||
*f = !*f;
|
*f = !*f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// construct new world
|
// Breite der neuen Anordnung berechnen
|
||||||
let new = construct_world(tris, &angles, &flips);
|
let new = construct_world(tris, &angles, &flips);
|
||||||
if new.width < *best_width {
|
if new.width < *best_width {
|
||||||
if new.width < *best_all {
|
|
||||||
*best_all = new.width;
|
|
||||||
eprint!("\r[{}O{}o{}] at {:.3}", save_counter, iteration, i, new.width);
|
|
||||||
//display::save_world(&format!("{}{}_{}_{}.svg", save_prefix, save_counter, iteration, i), &new);
|
|
||||||
//iters = 99999;
|
|
||||||
best = new;
|
best = new;
|
||||||
} else {
|
eprint!("\r[O{}o{}] {:.3}", iteration, i, best.width);
|
||||||
//eprint!("[{}O{}o{}] best: {} (min: {})", save_counter, iteration, i, new.width, best_all);
|
|
||||||
//eprint!(".");
|
|
||||||
}
|
|
||||||
*best_width = best.width;
|
*best_width = best.width;
|
||||||
found_better = true;
|
found_better = true;
|
||||||
*best_angles = angles;
|
*best_angles = angles;
|
||||||
@ -226,6 +193,7 @@ fn optimize(iters: usize, best_all: &mut f32, best_width: &mut f32, tris: &[Tria
|
|||||||
angles = start_angles.clone();
|
angles = start_angles.clone();
|
||||||
flips = start_flips.clone();
|
flips = start_flips.clone();
|
||||||
}
|
}
|
||||||
|
// Winkel einzeln leicht verändern
|
||||||
angles = best_angles.clone();
|
angles = best_angles.clone();
|
||||||
for idx in 0..tris.len() {
|
for idx in 0..tris.len() {
|
||||||
for i in 0..999 {
|
for i in 0..999 {
|
||||||
@ -233,10 +201,21 @@ fn optimize(iters: usize, best_all: &mut f32, best_width: &mut f32, tris: &[Tria
|
|||||||
angles[idx] += angle;
|
angles[idx] += angle;
|
||||||
let new = construct_world(tris, &angles, &best_flips);
|
let new = construct_world(tris, &angles, &best_flips);
|
||||||
if new.width < *best_width {
|
if new.width < *best_width {
|
||||||
*best_all = new.width;
|
eprint!("\r[O{}I{}E{}] {:.3}", iteration, idx, i, new.width);
|
||||||
eprint!("\r[{}O{}I{}E{}] at {:.3}", save_counter, iteration, idx, i, new.width);
|
best = new;
|
||||||
//display::save_world(&format!("{}{}_{}_{}.svg", save_prefix, save_counter, iteration, i), &new);
|
*best_width = best.width;
|
||||||
//iters = 99999;
|
found_better = true;
|
||||||
|
*best_angles = angles.clone();
|
||||||
|
} else {
|
||||||
|
angles[idx] -= angle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i in 1000..2499 {
|
||||||
|
let angle = rng.gen_range(-PI, PI);
|
||||||
|
angles[idx] += angle;
|
||||||
|
let new = construct_world(tris, &angles, &best_flips);
|
||||||
|
if new.width < *best_width {
|
||||||
|
eprint!("\r[O{}I{}EE{}] {:.3}", iteration, idx, i, new.width);
|
||||||
best = new;
|
best = new;
|
||||||
*best_width = best.width;
|
*best_width = best.width;
|
||||||
found_better = true;
|
found_better = true;
|
||||||
@ -252,6 +231,7 @@ fn optimize(iters: usize, best_all: &mut f32, best_width: &mut f32, tris: &[Tria
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl World {
|
impl World {
|
||||||
|
/// rechtes Welt zum Koordinatenursprung verschieben
|
||||||
fn normalize(&mut self) {
|
fn normalize(&mut self) {
|
||||||
let mut maxx = 0.0;
|
let mut maxx = 0.0;
|
||||||
for (_, tri) in &self.tris {
|
for (_, tri) in &self.tris {
|
||||||
@ -276,6 +256,7 @@ impl World {
|
|||||||
tri.2.x -= amount;
|
tri.2.x -= amount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Gesamtabstand berechnen
|
||||||
fn calc_width(&mut self) {
|
fn calc_width(&mut self) {
|
||||||
let mut minx = f32::MIN;
|
let mut minx = f32::MIN;
|
||||||
if (self.tris[0].1).0.y > -0.001 && (self.tris[0].1).0.y < 0.001 {
|
if (self.tris[0].1).0.y > -0.001 && (self.tris[0].1).0.y < 0.001 {
|
||||||
@ -307,6 +288,7 @@ impl World {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Dreieck drehen (Winkel im Bogenmaß)
|
||||||
fn rotate(mut tri: Triangle, angle: f32) -> Triangle {
|
fn rotate(mut tri: Triangle, angle: f32) -> Triangle {
|
||||||
let x1 = tri.1.x;
|
let x1 = tri.1.x;
|
||||||
let y1 = tri.1.y;
|
let y1 = tri.1.y;
|
||||||
|
Loading…
Reference in New Issue
Block a user