Documentation updates; misc. changes
This commit is contained in:
parent
d97e51c3bc
commit
c93c0d3043
@ -6,6 +6,7 @@ edition = "2018"
|
||||
|
||||
[profile.release]
|
||||
codegen-units = 1
|
||||
opt-level = 3
|
||||
|
||||
[dependencies]
|
||||
geo = { path = "/home/arne/Documents/Code/Github/geo/geo" }
|
||||
|
@ -1,6 +1,5 @@
|
||||
\documentclass[a4paper,10pt,ngerman]{scrartcl}
|
||||
\usepackage{babel}
|
||||
\usepackage[T1]{fontenc}
|
||||
\usepackage[a4paper,margin=2.5cm]{geometry}
|
||||
|
||||
% automatische Quotes
|
||||
@ -139,15 +138,19 @@ Zunächst kann man feststellen, dass der letzte Abschnitt jeder Route zum Bus im
|
||||
&= \sqrt{x_{L}^2 + (y_{L}-y_{M})^2} &&\text{($x_{B} = x_{M} = 0$)} \\
|
||||
d^2 &= x_{L}^2 + (y_{L}-y_{M})^2 \\
|
||||
&= x_{L}^2 + (y_{L}-y_{B}-2d)^2 &&\text{($y_{M} = y_{B} + 2d$)} \\
|
||||
&= x_{L}^2 + y_{L}^2 + y_{B}^2 + 4d^2 - 2y_{B}y_{M} - 4dy_{L} + 4dy_{B} \\
|
||||
-3d^2 + 4\cdot(y_{B}-y_{L})\cdot{}d &= x_{L}^2 + y_{L}^2 + y_{B}^2 - 2y_{B}y_{M} \\
|
||||
d &= \frac{a\pm\sqrt{a^2 + 12\cdot{}(-x_{L}^2 - y_{L}^2 - y_{B}^2 + 2y_{B}y_{M})}}{-6} &&\text{($a = 4\cdot(y_{B}-y_{L})$)}\qedhere
|
||||
&= x_{L}^2 + y_{L}^2 + y_{B}^2 + 4d^2 - 2y_{B}y_{L} - 4dy_{L} + 4dy_{B} \\
|
||||
-3d^2 + 4\cdot(y_{L}-y_{B})\cdot{}d &= x_{L}^2 + y_{L}^2 + y_{B}^2 - 2y_{B}y_{L} \\
|
||||
d &= \frac{-b\pm\sqrt{b^2 + 12\cdot{}(-x_{L}^2 - y_{L}^2 - y_{B}^2 + 2y_{B}y_{L})}}{-6} &&\text{($b = 4\cdot(y_{L}-y_{B})$)}
|
||||
\end{align*}
|
||||
Für diese Gleichung gibt es maximal zwei nicht-negative Lösungen.\qedhere
|
||||
\end{proof}
|
||||
|
||||
Für diese Gleichung gibt es maximal zwei nicht-negative Lösungen.
|
||||
Um den letzten Zeitpunkt, bei dem sie den Bus gerade noch erwischt, zu bestimmen, kann man die Wurzel in der letzen Gleichung gleich Null setzen. Wenn man nach $y_{B}$ auflöst, erhält man die Position des Busses, bei dessen Durchquerung Lisa anfangen sollte zu laufen. So kann man für jeden Startpunkt berechnen, wann Lisa spätestens von dort loslaufen müsste.
|
||||
|
||||
Außerdem wird Lisa auf einer optimalen Route vor dem letzten Abschnitt immer von einer Polygonecke zur nächsten gehen. Andernfalls würde sie in Kurven um jene Ecken wertvolle Zeit verschwenden.
|
||||
|
||||
\section{Umsetzung}
|
||||
Das Programm durchsucht in einer Breitensuche alle Wege, die über Polygonecken führen. Priorisiert werden jene Wege, bei denen Lisa theoretisch am längsten warten könnte. Um die Wege abzuschließen, probiert das Programm verschiedene Treffpunkte mit dem Bus durch (je einen pro Meter y-Achse). Derjenige, bei dem Lisa sich am meisten Zeit lassen kann, wird gespeichert, falls nicht schon eine bessere Lösung gefunden wurde.
|
||||
|
||||
\section{Beispiele}
|
||||
|
||||
|
@ -10,15 +10,30 @@ use super::*;
|
||||
|
||||
pub(crate) fn dump_route(house: Point, polys: &[Polygon], route: &Vec<RunState>) {
|
||||
let (points, lines, route1, route2_start) = gen_params(house, route);
|
||||
print!("{}", generate_svg(&points, &lines, polys, &route1, route2_start));
|
||||
print!(
|
||||
"{}",
|
||||
generate_svg(&points, &lines, polys, &route1, route2_start)
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn save_svg(filename: &str, house: Point, polys: &[Polygon], route: &Vec<RunState>) {
|
||||
let (points, lines, route1, route2_start) = gen_params(house, route);
|
||||
fs::write(filename, generate_svg(&points, &lines, polys, &route1, route2_start)).unwrap();
|
||||
fs::write(
|
||||
filename,
|
||||
generate_svg(&points, &lines, polys, &route1, route2_start),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn gen_params(house: Point, route: &Vec<RunState>) -> (Vec<(Point, &'static str)>, Vec<(Line, &'static str)>, Vec<Point>, Point) {
|
||||
fn gen_params(
|
||||
house: Point,
|
||||
route: &Vec<RunState>,
|
||||
) -> (
|
||||
Vec<(Point, &'static str)>,
|
||||
Vec<(Line, &'static str)>,
|
||||
Vec<Point>,
|
||||
Point,
|
||||
) {
|
||||
let first = route.first().unwrap();
|
||||
let last = route.last().unwrap();
|
||||
let best_delay = last.delay;
|
||||
|
111
src/main.rs
111
src/main.rs
@ -71,20 +71,21 @@ fn main() {
|
||||
|
||||
let mut best_delay = 0.0;
|
||||
let mut best = vec![];
|
||||
let save_prefix = "tmp3_";
|
||||
let save_prefix = "tmp_";
|
||||
let mut save_counter = 0;
|
||||
|
||||
while states.peek().map(|x| x[0].delay > best_delay) == Some(true) {
|
||||
//eprintln!(". {:?} states left:", states.len());
|
||||
let s = states.pop().unwrap();
|
||||
let last = &s[0]; //s.last().unwrap();
|
||||
//eprintln!("{},{}", last.pos.x(), last.pos.y());
|
||||
let last = &s[0];
|
||||
|
||||
// new states
|
||||
let mut all = vec![];
|
||||
|
||||
// attempt to go to any other vertex/point
|
||||
for next in points.iter().filter(|next| !s.iter().any(|x| x.pos == (**next).into())) {
|
||||
for next in points
|
||||
.iter()
|
||||
.filter(|next| !s.iter().any(|x| x.pos == (**next).into()))
|
||||
{
|
||||
let next = Point::from(*next);
|
||||
if next != last.pos && none_intersect(&polys, &Line::new(last.pos, next)) {
|
||||
// could run to that point
|
||||
@ -107,14 +108,15 @@ fn main() {
|
||||
let range = to_bus(bus, last.pos);
|
||||
if range.len() == 2 {
|
||||
let range = Line::new(range[0], range[1]);
|
||||
for percent in float_range(0.0, 1.0) {
|
||||
for next_y in float_range(range.start.y, range.end.y) {
|
||||
let mut next = range.start;
|
||||
next.x += range.dx() * percent;
|
||||
next.y += range.dy() * percent;
|
||||
next.y = next_y;
|
||||
let next = Point::from(next);
|
||||
let line = Line::new(last.pos, next);
|
||||
if none_intersect(&polys, &line) {
|
||||
let delay = line.end.y - bus.y() - line.end_point().euclidean_distance(&line.start_point()) * 2.0;
|
||||
let delay =
|
||||
line.end.y
|
||||
- bus.y() - line.end_point().euclidean_distance(&line.start_point()) * 2.0;
|
||||
if delay > best_delay {
|
||||
// new high score!
|
||||
let mut route = s.clone();
|
||||
@ -126,11 +128,22 @@ fn main() {
|
||||
delay,
|
||||
},
|
||||
);
|
||||
eprintln!("# New best delay {:?} ({:?} states left)", delay_to_time(delay), states.len());
|
||||
eprintln!(
|
||||
"# New best delay {:?} ({:?} states left)",
|
||||
delay_to_time(delay),
|
||||
states.len()
|
||||
);
|
||||
best = route;
|
||||
best.reverse();
|
||||
best_delay = delay;
|
||||
display::save_svg(&format!("{}{}.svg", save_prefix, save_counter), house, &polys, &best);
|
||||
/*
|
||||
display::save_svg(
|
||||
&format!("{}{}.svg", save_prefix, save_counter),
|
||||
house,
|
||||
&polys,
|
||||
&best,
|
||||
);
|
||||
*/
|
||||
save_counter += 1;
|
||||
} else {
|
||||
// not worth it
|
||||
@ -151,22 +164,12 @@ fn main() {
|
||||
|
||||
/// [a; b]
|
||||
fn float_range(a: f64, b: f64) -> impl Iterator<Item = f64> {
|
||||
const STEPS: usize = 1000;
|
||||
const STEPS: usize = 1337;
|
||||
let d = b - a;
|
||||
(0..=STEPS).map(move |s| a + ((s as f64) * d) / STEPS as f64)
|
||||
}
|
||||
|
||||
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;
|
||||
@ -187,33 +190,13 @@ fn none_intersect(polys: &[Polygon], line: &Line) -> bool {
|
||||
|| 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;
|
||||
}
|
||||
}
|
||||
@ -225,34 +208,36 @@ fn distance(a: Point, b: Point) -> f64 {
|
||||
}
|
||||
|
||||
fn max_possible_delay(bus: Point, start: Point) -> f64 {
|
||||
let a = start.x();
|
||||
let b = start.y();
|
||||
let c = bus.x();
|
||||
assert_eq!(c, 0.0);
|
||||
let d = bus.y();
|
||||
let x_l = start.x();
|
||||
let y_l = start.y();
|
||||
let x_b = bus.x();
|
||||
assert_eq!(x_b, 0.0);
|
||||
let y_b = bus.y();
|
||||
|
||||
b - ((a.powi(2)) * (3.0)).sqrt() - d
|
||||
y_l - (3.0 * x_l.powi(2)).sqrt() - y_b
|
||||
}
|
||||
|
||||
// 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 x_l = start.x();
|
||||
let y_l = start.y();
|
||||
let x_b = bus.x();
|
||||
assert_eq!(x_b, 0.0);
|
||||
let y_b = 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) {
|
||||
let b = 4.0 * (y_l - y_b);
|
||||
let unter_wurzel =
|
||||
b.powi(2) + 12.0 * (-x_l.powi(2) - y_l.powi(2) - y_b.powi(2) + 2.0 * y_b * y_l);
|
||||
if unter_wurzel >= 0.0 {
|
||||
let wurzel = unter_wurzel.sqrt();
|
||||
let wurzel_ = wurzel / (-6.0);
|
||||
let b_ = b / 6.0;
|
||||
let d1 = b_ + wurzel_;
|
||||
let d2 = b_ - wurzel_;
|
||||
if d1 > (0.0) && d2 > (0.0) {
|
||||
vec![
|
||||
bus.translate(0.0, (2.0) * x1),
|
||||
bus.translate(0.0, (2.0) * x2),
|
||||
bus.translate(0.0, (2.0) * d1),
|
||||
bus.translate(0.0, (2.0) * d2),
|
||||
]
|
||||
} else {
|
||||
vec![]
|
||||
|
Loading…
Reference in New Issue
Block a user