diff --git a/src/display.rs b/src/display.rs index a9efd63..4992d65 100644 --- a/src/display.rs +++ b/src/display.rs @@ -8,7 +8,7 @@ use std::fs; use super::*; -pub(crate) fn save_tri(filename: &str, tri: Triangle) { +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) { @@ -25,7 +25,7 @@ pub(crate) fn save_world(filename: &str, world: &World) { .unwrap(); } -pub(crate) fn generate_svg(tris: &[Triangle]) -> String { +pub(crate) fn generate_svg(tris: &[Triangle]) -> String { let mut document = Document::new() // view box at (x,y), (w,h) .set("viewBox", (-350.0, -150.0, 360.0, 150.0)) diff --git a/src/input.rs b/src/input.rs index 18170d0..1271e24 100644 --- a/src/input.rs +++ b/src/input.rs @@ -3,11 +3,11 @@ use super::*; use std::io; use std::io::prelude::*; -pub(crate) fn read_input() -> Vec> { +pub(crate) fn read_input() -> Vec { read_stdin() } -fn read_stdin() -> Vec> { +fn read_stdin() -> Vec { let stdin = io::stdin(); let stdin = stdin.lock(); @@ -21,7 +21,7 @@ fn read_stdin() -> Vec> { .trim() .split(' ') .skip(1) - .map(|x| x.parse::().unwrap()) + .map(|x| x.parse().unwrap()) .collect::>(); tris.push([[n[0], n[1]], [n[2], n[3]], [n[4], n[5]]].into()); } diff --git a/src/main.rs b/src/main.rs index 71f15bb..bf843e3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,4 @@ use geo::prelude::*; -use geo::{Coordinate, Triangle}; use std::cmp; use std::collections::BinaryHeap; @@ -9,10 +8,13 @@ use std::f32::consts::PI; mod display; mod input; +type Coordinate = geo::Coordinate; +type Triangle = geo::Triangle; + #[derive(Debug, Clone)] struct World { /// Dreiecksgrundstücke - tris: Vec<(usize, Triangle)>, + tris: Vec<(usize, Triangle)>, /// Gesamtabstand width: f32, } @@ -148,6 +150,258 @@ fn main() { let save_prefix = "start_"; save_counter = 0; let mut worlds = BinaryHeap::new(); + + for t in &left_tris { + let mut world = World { + tris: vec![*t], + width: 0.0, + }; + world.normalize(); + display::save_world(&format!("{}{}.svg", save_prefix, save_counter), &world); + save_counter += 1; + worlds.push(world); + } + + let mut best = World { + tris: vec![], + width: f32::MAX, + }; + let save_prefix = "world_"; + save_counter = 0; + let mut counter = 0; + while !worlds.is_empty() { + counter += 1; + if counter % 10000 == 0 { + println!("{}/∞", counter); + } + let w = worlds.pop().unwrap(); + //println!("working"); + if w.width >= best.width { + continue; + } + //println!("s: {:?}", w); + + /* + if save_counter < 1000000 { + display::save_world(&format!("{}a_{}.svg", save_prefix, save_counter), &w); + } + */ + let save_iterations = false; + + let mut new = vec![]; + for (next_idx, next_tri) in left_tris + .iter() + .filter(|(idx1, _tri)| w.tris.iter().all(|(idx2, _tri)| idx1 != idx2)) + { + let next_tri = *next_tri; + //println!("trying {:?}", next_idx); + let (last_idx, last_tri) = *w.tris.last().unwrap(); + + let free_angle = right_of(last_tri); + + let next_angle = left_of(next_tri); + + //println!("{:?} -> {:?}: {:?} {:?}", last_idx, next_idx, free_angle.to_degrees(), next_angle.to_degrees()); + let target = (next_angle - PI + free_angle).to_degrees(); + for angle in [0.0] { + //println!("angle {:?}", angle); + let radians = angle.to_radians(); + let next_angle = next_angle - radians; + let mut tri = rotate(next_tri, radians); + + // "binary search"-like optimization here + for &delta in &[ + /*80.0, 30.0, 10.0, 5.0,*/ 1.0, 0.6, 0.32, 0.18, 0.1, 0.06, 0.03, 0.01, + 0.003, + ] { + let initial_state = last_tri.intersects(&tri); + let dx = if initial_state { delta } else { -delta }; + while last_tri.intersects(&tri) == initial_state { + tri.0.x += dx; + tri.1.x += dx; + tri.2.x += dx; + } + } + let mut w = w.clone(); + w.tris.push((*next_idx, tri)); + w.normalize(); + w.calc_width(); + if save_iterations { + display::save_world(&format!("{}b_{}.svg", save_prefix, save_counter), &w); + } + if w.tris.len() == count_tris { + if w.width < best.width { + println!("[{}] new best: {}", save_counter, w.width); + //println!("{:?}", w.tris); + display::save_world( + &format!("{}best_{}.svg", save_prefix, save_counter), + &w, + ); + best = w; + } + } else if w.width < best.width { + w.width -= (2.0 * PI - free_angle - next_angle) * 0.01; + //if (angle > 50 && angle < 55) || (angle > 88 && angle < 92) { + //println!("{:?} -> {:?}", angle, w.width); + //} + new.push(w); + } + save_counter += 1; + } + } + worlds.extend(new); + } + println!("best width = {:?}", best.width); + display::save_world("world_best.svg", &best); +} + +fn left_of(tri: Triangle) -> f32 { + let vertex1 = tri.1; + let vertex2 = tri.2; + + let next_angle = if vertex1.y > 0.001 && vertex2.y > 0.001 { + let angle1 = 0.5 * PI + vertex1.x.signum() * (vertex1.x.abs() / vertex1.y).atan(); + let angle2 = 0.5 * PI + vertex2.x.signum() * (vertex2.x.abs() / vertex2.y).atan(); + min(angle1, angle2) + } else if vertex1.y < 0.001 { + 0.5 * PI + vertex2.x.signum() * (vertex2.x.abs() / vertex2.y).atan() + } else { + 0.5 * PI + vertex1.x.signum() * (vertex1.x.abs() / vertex1.y).atan() + }; + next_angle +} + +fn right_of(tri: Triangle) -> f32 { + let vertex1 = tri.1; + let vertex2 = tri.2; + + let free_angle = if vertex1.y > 0.001 && vertex2.y < 0.001 { + PI - (vertex1.y / (vertex2.x - vertex1.x).abs()).atan() + } else if vertex2.y > 0.001 && vertex1.y < 0.001 { + PI - (vertex2.y / (vertex2.x - vertex1.x).abs()).atan() + } else { + let angle1 = 0.5 * PI - vertex1.x.signum() * (vertex1.x.abs() / vertex1.y).atan(); + let angle2 = 0.5 * PI - vertex2.x.signum() * (vertex2.x.abs() / vertex2.y).atan(); + min(angle1, angle2) + }; + free_angle +} + +fn main2() { + let tris = input::read_input(); + let count_tris = tris.len(); + let tris = transformations(&tris); + + let save_prefix = "tri_"; + let mut save_counter = 0; + let mut normalized_tris = Vec::with_capacity(tris.len() * 2); + let mut left_tris = Vec::with_capacity(tris.len()); + for (mut idx, t) in tris.into_iter().enumerate() { + idx /= 6; + let prev_angle = angle_of(t, t.1); + let mut added = false; + let rotated = rotate(t, 0.5 * PI - prev_angle); + if rotated.1.y >= -0.001 + && rotated.2.y >= -0.001 + && (rotated.1.y < 0.1 || rotated.2.y < 0.1) + { + normalized_tris.push((idx, rotated)); + if rotated.1.x > -0.001 && rotated.2.x > -0.001 { + left_tris.push((idx, rotated)); + } + added = true; + } + let rotated = rotate(rotated, PI); + if !added + && rotated.1.y >= -0.001 + && rotated.2.y >= -0.001 + && (rotated.1.y < 0.1 || rotated.2.y < 0.1) + { + normalized_tris.push((idx, rotated)); + if rotated.1.x > -0.001 && rotated.2.x > -0.001 { + left_tris.push((idx, rotated)); + } + added = true; + } + let rotated = rotate(t, -0.5 * PI + prev_angle); + if !added + && rotated.1.y >= -0.001 + && rotated.2.y >= -0.001 + && (rotated.1.y < 0.1 || rotated.2.y < 0.1) + { + normalized_tris.push((idx, rotated)); + if rotated.1.x > -0.001 && rotated.2.x > -0.001 { + left_tris.push((idx, rotated)); + } + added = true; + } + let rotated = rotate(rotated, PI); + if !added + && rotated.1.y >= -0.001 + && rotated.2.y >= -0.001 + && (rotated.1.y < 0.1 || rotated.2.y < 0.1) + { + normalized_tris.push((idx, rotated)); + if rotated.1.x > -0.001 && rotated.2.x > -0.001 { + left_tris.push((idx, rotated)); + } + } + let prev_angle = angle_of(t, t.2); + added = false; + let rotated = rotate(t, 0.5 * PI - prev_angle); + if rotated.1.y >= -0.001 + && rotated.2.y >= -0.001 + && (rotated.1.y < 0.1 || rotated.2.y < 0.1) + { + normalized_tris.push((idx, rotated)); + if rotated.1.x > -0.001 && rotated.2.x > -0.001 { + left_tris.push((idx, rotated)); + } + added = true; + } + let rotated = rotate(rotated, PI); + if !added + && rotated.1.y >= -0.001 + && rotated.2.y >= -0.001 + && (rotated.1.y < 0.1 || rotated.2.y < 0.1) + { + normalized_tris.push((idx, rotated)); + if rotated.1.x > -0.001 && rotated.2.x > -0.001 { + left_tris.push((idx, rotated)); + } + added = true; + } + let rotated = rotate(t, -0.5 * PI + prev_angle); + if !added + && rotated.1.y >= -0.001 + && rotated.2.y >= -0.001 + && (rotated.1.y < 0.1 || rotated.2.y < 0.1) + { + normalized_tris.push((idx, rotated)); + if rotated.1.x > -0.001 && rotated.2.x > -0.001 { + left_tris.push((idx, rotated)); + } + added = true; + } + let rotated = rotate(rotated, PI); + if !added + && rotated.1.y >= -0.001 + && rotated.2.y >= -0.001 + && (rotated.1.y < 0.1 || rotated.2.y < 0.1) + { + normalized_tris.push((idx, rotated)); + if rotated.1.x > -0.001 && rotated.2.x > -0.001 { + left_tris.push((idx, rotated)); + } + } + } + for (_idx, t) in &normalized_tris { + display::save_tri(&format!("{}{}.svg", save_prefix, save_counter), *t); + save_counter += 1; + } + let save_prefix = "start_"; + save_counter = 0; + let mut worlds = BinaryHeap::new(); //let left_tris = vec![left_tris[1], left_tris[11], left_tris[17], left_tris[22], left_tris[28]]; for t in &left_tris { let mut world = World { @@ -361,7 +615,7 @@ impl World { } } -fn rotate(mut tri: Triangle, angle: f32) -> Triangle { +fn rotate(mut tri: Triangle, angle: f32) -> Triangle { let x1 = tri.1.x; let y1 = tri.1.y; tri.1.x = x1 * angle.cos() - y1 * angle.sin(); @@ -373,35 +627,35 @@ fn rotate(mut tri: Triangle, angle: f32) -> Triangle { tri } -fn angle_of(_tri: Triangle, point: Coordinate) -> f32 { +fn angle_of(_tri: Triangle, point: Coordinate) -> f32 { let d = (point.x.powi(2) + point.y.powi(2)).sqrt(); (point.y / d).acos() } -fn transformations(tris: &[Triangle]) -> Vec> { - let mut new = Vec::with_capacity(tris.len() * 6); +fn transformations(tris: &[Triangle]) -> Vec { + let mut new: Vec = Vec::with_capacity(tris.len() * 6); for t in tris { - let n = Triangle( - (0.0, 0.0).into(), - (t.1.x - t.0.x, t.1.y - t.0.y).into(), - (t.2.x - t.0.x, t.2.y - t.0.y).into(), - ); + let n = [ + (0.0, 0.0), + (t.1.x - t.0.x, t.1.y - t.0.y), + (t.2.x - t.0.x, t.2.y - t.0.y), + ].into(); new.push(n); let n = [[-n.0.x, n.0.y], [-n.1.x, n.1.y], [-n.2.x, n.2.y]].into(); new.push(n); - let n = Triangle( - (0.0, 0.0).into(), - (t.2.x - t.1.x, t.2.y - t.1.y).into(), - (t.0.x - t.1.x, t.0.y - t.1.y).into(), - ); + let n = [ + (0.0, 0.0), + (t.2.x - t.1.x, t.2.y - t.1.y), + (t.0.x - t.1.x, t.0.y - t.1.y), + ].into(); new.push(n); let n = [[-n.0.x, n.0.y], [-n.1.x, n.1.y], [-n.2.x, n.2.y]].into(); new.push(n); - let n = Triangle( - (0.0, 0.0).into(), - (t.0.x - t.2.x, t.0.y - t.2.y).into(), - (t.1.x - t.2.x, t.1.y - t.2.y).into(), - ); + let n = [ + (0.0, 0.0), + (t.0.x - t.2.x, t.0.y - t.2.y), + (t.1.x - t.2.x, t.1.y - t.2.y), + ].into(); new.push(n); let n = [[-n.0.x, n.0.y], [-n.1.x, n.1.y], [-n.2.x, n.2.y]].into(); new.push(n);