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;
|
||||
use svg::node::element::Circle;
|
||||
use svg::node::element::Path;
|
||||
use svg::Document;
|
||||
use svg::Node;
|
||||
|
||||
@ -13,29 +13,42 @@ fn idx_to_char(idx: usize) -> char {
|
||||
0 => 'A',
|
||||
1 => 'B',
|
||||
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);
|
||||
for (idx, (_, tri)) in world.tris.iter().enumerate() {
|
||||
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);
|
||||
let mut coordinates = vec![tri.0, tri.1, tri.2];
|
||||
coordinates.sort_by_key(|x| (x.x * 100.0) as u32);
|
||||
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";
|
||||
}
|
||||
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) {
|
||||
fs::write(
|
||||
filename,
|
||||
|
190
src/main.rs
190
src/main.rs
@ -2,7 +2,6 @@ use geo::prelude::*;
|
||||
use rand::distributions::{Bernoulli, Distribution};
|
||||
use rand::prelude::*;
|
||||
|
||||
use std::cmp;
|
||||
use std::f32;
|
||||
use std::f32::consts::PI;
|
||||
|
||||
@ -11,6 +10,7 @@ mod input;
|
||||
|
||||
type Triangle = geo::Triangle<f32>;
|
||||
|
||||
/// Datenstruktur für eine (evtl. unvollständige) Anordnung
|
||||
#[derive(Debug, Clone)]
|
||||
struct World {
|
||||
/// Dreiecksgrundstücke
|
||||
@ -18,82 +18,35 @@ struct World {
|
||||
/// Gesamtabstand
|
||||
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);
|
||||
let mut world = World { tris: Vec::with_capacity(tris.len()), width: 0.0 };
|
||||
for (idx, tri) in tris.iter().enumerate() {
|
||||
let mut tri = *tri;
|
||||
// je ein Dreieck hinzufügen
|
||||
for idx in 0..tris.len() {
|
||||
// zufällige Werte berechnen und anwenden
|
||||
angles[idx] = rng.gen_range(0.0, 2.0 * PI);
|
||||
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);
|
||||
tri = rotate(tri, angles[idx]);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
world.tris.push((0, tri));
|
||||
world.normalize();
|
||||
}
|
||||
}
|
||||
world.calc_width();
|
||||
world
|
||||
construct_world(&tris, angles, flips)
|
||||
}
|
||||
|
||||
/// Welt mit vorgegebener Reihenfolge und Konfiguration (Winkel, Spiegelungen) erstellen.
|
||||
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() {
|
||||
// Kopie anlegen
|
||||
let mut tri = *tri;
|
||||
// Werte anwenden
|
||||
if flips[idx] {
|
||||
tri.0.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.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);
|
||||
// Dreieck neben den anderen platzieren
|
||||
for &delta in &[1.0, 0.6, 0.32, 0.18, 0.1, 0.06, 0.03, 0.01] {
|
||||
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;
|
||||
// "entlaufene" Dreiecke einfangen
|
||||
if tri.0.x < -1000.0 {
|
||||
tri.0.x += 1000.0;
|
||||
tri.1.x += 1000.0;
|
||||
@ -144,6 +92,7 @@ fn construct_world(tris: &[Triangle], angles: &[f32], flips: &[bool]) -> World {
|
||||
|
||||
fn main() {
|
||||
let mut tris = input::read_input();
|
||||
// Dreiecke zum Ursprung verschieben
|
||||
for tri in &mut tris {
|
||||
tri.1.x -= tri.0.x;
|
||||
tri.2.x -= tri.0.x;
|
||||
@ -155,48 +104,74 @@ fn main() {
|
||||
let original_tris = tris.clone();
|
||||
let mut best = f32::MAX;
|
||||
let mut i = 0u64;
|
||||
let save_prefix = format!("rand_sel_{}_", random::<u64>());
|
||||
println!("save prefix: {}", save_prefix);
|
||||
let save_prefix = format!("rand_{}_", random::<u64>());
|
||||
println!("Dateipräfix: {}", save_prefix);
|
||||
loop {
|
||||
i += 1;
|
||||
let new_best = one_main(&mut tris.clone());
|
||||
let new_best = find_layout(&mut tris.clone());
|
||||
if new_best.width < best {
|
||||
best = new_best.width;
|
||||
eprint!("\r");
|
||||
println!("[{}] BEST {:.3} ", i, best);
|
||||
println!("[{}] BEST {:.3} ", i, 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 {
|
||||
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 flips = vec![false; tris.len()];
|
||||
let mut rng = SmallRng::from_entropy();
|
||||
let mut best_width = f32::MAX;
|
||||
let mut best_all = f32::MAX;
|
||||
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(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 {
|
||||
let mut best = World { tris: vec![], width: f32::MAX };
|
||||
fn optimize(
|
||||
iters: usize,
|
||||
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 iteration = 0;
|
||||
let flip_choice = Bernoulli::from_ratio(1, 11);
|
||||
while found_better {
|
||||
found_better = false;
|
||||
// slightly modify angles
|
||||
// Winkel und Spiegelungen leicht verändern
|
||||
let start_angles = best_angles.clone();
|
||||
let start_flips = best_flips.clone();
|
||||
let mut angles = start_angles.clone();
|
||||
let mut flips = start_flips.clone();
|
||||
for i in 0..iters {
|
||||
if i % 10000 == 0 {
|
||||
eprint!(".");
|
||||
}
|
||||
for a in &mut angles {
|
||||
*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;
|
||||
}
|
||||
}
|
||||
// construct new world
|
||||
// Breite der neuen Anordnung berechnen
|
||||
let new = construct_world(tris, &angles, &flips);
|
||||
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;
|
||||
} else {
|
||||
//eprint!("[{}O{}o{}] best: {} (min: {})", save_counter, iteration, i, new.width, best_all);
|
||||
//eprint!(".");
|
||||
}
|
||||
best = new;
|
||||
eprint!("\r[O{}o{}] {:.3}", iteration, i, best.width);
|
||||
*best_width = best.width;
|
||||
found_better = true;
|
||||
*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();
|
||||
flips = start_flips.clone();
|
||||
}
|
||||
// Winkel einzeln leicht verändern
|
||||
angles = best_angles.clone();
|
||||
for idx in 0..tris.len() {
|
||||
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;
|
||||
let new = construct_world(tris, &angles, &best_flips);
|
||||
if new.width < *best_width {
|
||||
*best_all = new.width;
|
||||
eprint!("\r[{}O{}I{}E{}] at {:.3}", save_counter, iteration, idx, i, new.width);
|
||||
//display::save_world(&format!("{}{}_{}_{}.svg", save_prefix, save_counter, iteration, i), &new);
|
||||
//iters = 99999;
|
||||
eprint!("\r[O{}I{}E{}] {:.3}", iteration, idx, i, new.width);
|
||||
best = new;
|
||||
*best_width = best.width;
|
||||
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_width = best.width;
|
||||
found_better = true;
|
||||
@ -252,6 +231,7 @@ fn optimize(iters: usize, best_all: &mut f32, best_width: &mut f32, tris: &[Tria
|
||||
}
|
||||
|
||||
impl World {
|
||||
/// rechtes Welt zum Koordinatenursprung verschieben
|
||||
fn normalize(&mut self) {
|
||||
let mut maxx = 0.0;
|
||||
for (_, tri) in &self.tris {
|
||||
@ -276,6 +256,7 @@ impl World {
|
||||
tri.2.x -= amount;
|
||||
}
|
||||
}
|
||||
/// Gesamtabstand berechnen
|
||||
fn calc_width(&mut self) {
|
||||
let mut minx = f32::MIN;
|
||||
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 {
|
||||
let x1 = tri.1.x;
|
||||
let y1 = tri.1.y;
|
||||
|
Loading…
Reference in New Issue
Block a user