263 lines
6.8 KiB
Rust
263 lines
6.8 KiB
Rust
use geo::*;
|
|
use geo::prelude::*;
|
|
|
|
use std::cmp;
|
|
use std::collections::BinaryHeap;
|
|
|
|
mod display;
|
|
mod input;
|
|
|
|
type Point = geo::Point<f32>;
|
|
type Line = geo::Line<f32>;
|
|
type LineString = geo::LineString<f32>;
|
|
type Polygon = geo::Polygon<f32>;
|
|
|
|
// 30 km/h = 1 units/unit
|
|
// 15 km/h = 0.5 units/unit
|
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
struct RunState {
|
|
/// Theoretically possible maximum delay
|
|
delay: f32,
|
|
/// Our current location
|
|
pos: Point,
|
|
/// Current location of the bus, not including any delays
|
|
bus: Point
|
|
}
|
|
impl cmp::Eq for RunState {}
|
|
impl cmp::Ord for RunState {
|
|
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
|
self.delay.partial_cmp(&other.delay).unwrap()
|
|
}
|
|
}
|
|
impl cmp::PartialOrd for RunState {
|
|
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
|
Some(self.cmp(other))
|
|
}
|
|
}
|
|
|
|
fn main() {
|
|
let bus = Point::new(0.0.into(), (-2000.0).into());
|
|
|
|
let data = input::read_input();
|
|
let house = data.start;
|
|
let polys = data.polys;
|
|
eprintln!("{:?}", house);
|
|
eprintln!("{:?}", polys);
|
|
for p in &polys {
|
|
assert!(p.interiors.is_empty());
|
|
//assert!(p.is_convex());
|
|
}
|
|
let points = polys.iter().map(|x| x.exterior.0.clone()).flatten().collect::<Vec<_>>();
|
|
|
|
let start = RunState {
|
|
pos: house,
|
|
bus: bus,
|
|
delay: max_possible_delay(bus, house)
|
|
//route: vec![house],
|
|
//length: 0.0.into()
|
|
};
|
|
|
|
eprintln!("# Max delay possible: {:?}", start.delay);
|
|
|
|
let mut states = BinaryHeap::new();
|
|
states.push(vec![start]);
|
|
|
|
let mut best_delay = 0.0;
|
|
let mut best = vec![];
|
|
|
|
while states.peek().map(|x| x[0].delay > best_delay) == Some(true) {
|
|
eprintln!(". {:?} states left:", states.len());
|
|
/*
|
|
if states.len() > 10 {
|
|
for s in &states {
|
|
eprintln!("{:?}", s);
|
|
}
|
|
panic!();
|
|
}
|
|
*/
|
|
let s = states.pop().unwrap();
|
|
let last = &s[0]; //s.last().unwrap();
|
|
eprintln!("{},{}", last.pos.x(), last.pos.y());
|
|
//eprintln!("{:?}", states.peek());
|
|
|
|
// new states
|
|
let mut all = vec![];
|
|
|
|
// attempt to go to any other vertex/point
|
|
for next in &points {
|
|
let next = Point::from(*next);
|
|
if next != last.pos && none_intersect(&polys, &Line::new(last.pos, next)) {
|
|
// could run to that point
|
|
let bus_next = last.bus.translate(0.0, distance(last.pos, next) * 2.0);
|
|
let mut route = s.clone();
|
|
//eprintln!("{},{} would go {},{}", last.pos.x(), last.pos.y(), next.x(), next.y());
|
|
route.insert(0, RunState {
|
|
pos: next,
|
|
bus: bus_next,
|
|
delay: max_possible_delay(bus_next, next)
|
|
});
|
|
all.push(route);
|
|
}
|
|
}
|
|
// attempt to go to the bus, with varying delays
|
|
let mut bus_reached = false;
|
|
for delay in float_range(last.delay.into(), 0.0) {
|
|
let bus = last.bus.translate(0.0.into(), delay);
|
|
let range = to_bus(bus, last.pos);
|
|
if range.len() == 2 {
|
|
bus_reached = true; // TODO: what if effectively unreachable?
|
|
let range = Line::new(range[0], range[1]);
|
|
for percent in float_range(0.0, 1.0) {
|
|
let mut next = range.start;
|
|
next.x += range.dx() * percent;
|
|
next.y += range.dy() * percent;
|
|
let next = Point::from(next);
|
|
if none_intersect(&polys, &Line::new(last.pos, next)) {
|
|
if delay > best_delay {
|
|
// new high score!
|
|
let mut route = s.clone();
|
|
route.insert(0, RunState {
|
|
pos: next,
|
|
bus: next,
|
|
delay
|
|
});
|
|
eprintln!("# New best delay {:?}", delay);
|
|
best = route;
|
|
best.reverse();
|
|
best_delay = delay;
|
|
} else {
|
|
// not worth it
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if bus_reached {
|
|
//eprintln!("-> adding all to queue");
|
|
states.extend(all);
|
|
}
|
|
|
|
}
|
|
eprintln!("d = {:?}", best_delay);
|
|
let route = best;
|
|
eprintln!("Route:");
|
|
for s in &route {
|
|
eprintln!("{:.02},{:.02}", s.pos.x(), s.pos.y());
|
|
}
|
|
let first = route.first().unwrap();
|
|
let last = route.last().unwrap();
|
|
let points = vec![(house, "red"), (first.bus.translate(0.0.into(), best_delay), "yellow"), (last.bus, "orange")];
|
|
let lines = route.iter().map(|x| x.pos).collect::<LineString>().lines().map(|x| {
|
|
(x, "gray")
|
|
}).collect::<Vec<_>>();
|
|
|
|
display::dump_svg(&points, &lines, &polys, &route.iter().map(|x| x.pos).collect::<Vec<_>>(), first.bus.translate(0.0.into(), best_delay));
|
|
}
|
|
|
|
/// [a; b]
|
|
fn float_range(a: f32, b: f32) -> impl Iterator<Item = f32> {
|
|
const STEPS: usize = 25;
|
|
let d = b - a;
|
|
(0..=STEPS).map(move |s| a + ((s as f32) * d)/STEPS as f32)
|
|
}
|
|
|
|
fn none_intersect(polys: &[Polygon], line: &Line) -> bool {
|
|
/*
|
|
if line.end.x != 0.0 {
|
|
eprintln!("checking {:?}", line);
|
|
}
|
|
*/
|
|
/*
|
|
if line.end.x > 0.0 {
|
|
eprintln!("{:?}", line);
|
|
}
|
|
*/
|
|
let mut middle = line.start;
|
|
middle.x += line.dx() / 2.0;
|
|
middle.y += line.dy() / 2.0;
|
|
let middle: Point = middle.into();
|
|
'poly: for p in polys {
|
|
for l in p.exterior.lines().chain(vec![Line::new(p.exterior.0[0], *p.exterior.0.last().unwrap())]) {
|
|
if (l.start == line.start && l.end == line.end) || (l.start == line.end && l.end == line.start) {
|
|
// point is on polygon border
|
|
continue 'poly;
|
|
}
|
|
if l.start == line.start || l.end == line.end || l.start == line.end || l.end == line.start {
|
|
/*
|
|
if line.end.x > 0.0 {
|
|
eprintln!("skipping {:?}", l);
|
|
}
|
|
*/
|
|
continue; // would always intersect with itself
|
|
}
|
|
/*
|
|
if line.end.x > 0.0 {
|
|
eprintln!("intersect with {:?}", l);
|
|
}
|
|
*/
|
|
if l.intersects(line) {
|
|
/*
|
|
if line.end.x != 0.0 {
|
|
eprintln!("{:?} intersects {:?}", l, line);
|
|
}
|
|
*/
|
|
return false;
|
|
}
|
|
}
|
|
if p.euclidean_distance(&middle) == 0.0 {
|
|
/*
|
|
if line.end.x != 0.0 {
|
|
eprintln!("{:?} is in polygon {:?}, contains = {}", middle, p.exterior.lines().collect::<Vec<_>>(), p.contains(&middle));
|
|
}
|
|
*/
|
|
return false;
|
|
}
|
|
}
|
|
true
|
|
}
|
|
|
|
fn distance(a: Point, b: Point) -> f32 {
|
|
((a.x() - b.x()).powi(2) + (a.y() - b.y()).powi(2)).sqrt()
|
|
}
|
|
|
|
fn max_possible_delay(bus: Point, start: Point) -> f32 {
|
|
let a = start.x();
|
|
let b = start.y();
|
|
let c = bus.x();
|
|
assert_eq!(c, 0.0);
|
|
let d = bus.y();
|
|
|
|
b - ((a.powi(2)) * (3.0)).sqrt() - d
|
|
}
|
|
|
|
// Go straight to the bus. Returns the points where the bus can be reached.
|
|
fn to_bus(bus: Point, start: Point) -> Vec<Point> {
|
|
let a = start.x();
|
|
let b = start.y();
|
|
let c = bus.x();
|
|
let d = bus.y();
|
|
|
|
let v = -a.powi(2) * (3.0) + (6.0) * a * c + b.powi(2) - (2.0) * b * d - (3.0) * c.powi(2) + d.powi(2);
|
|
if v >= 0.0 {
|
|
// v = sqrt(-3 A^2 + 6 A C + B^2 - 2 B D - 3 C^2 + D^2)
|
|
let v = v.sqrt();
|
|
// x = 1/3 (+-v + 2 B - 2 D)
|
|
let x1 = (v + 2.0 * b - 2.0 * d) / 3.0;
|
|
let x2 = (-v + 2.0 * b - 2.0 * d) / 3.0;
|
|
if x1 > (0.0) && x2 > (0.0) {
|
|
vec![bus.translate(0.0, (2.0) * x1), bus.translate(0.0, (2.0) * x2)]
|
|
} else {
|
|
vec![]
|
|
}
|
|
} else {
|
|
vec![]
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
#[test]
|
|
fn test_to_bus() {
|
|
assert!(to_bus(Coordinate { x: 0.0.into(), y: 0.0.into() }.into(), Coordinate { x: 1.0.into(), y: 23.0.into() }.into()).len() == 2);
|
|
assert_eq!(to_bus(Coordinate { x: 0.0.into(), y: 168.7601848778319.into() }.into(), Coordinate { x: 1.0.into(), y: 23.0.into() }.into()), vec![]);
|
|
} |