diff --git a/src/display.rs b/src/display.rs index 3cba526..67b254e 100644 --- a/src/display.rs +++ b/src/display.rs @@ -24,8 +24,8 @@ pub(crate) fn dump_latex_code(route: &[RunState], polys: &[Polygon]) { } } } - if route[route.len()-1].pos.y()/SCALE > ymax { - ymax = route[route.len()-1].pos.y()/SCALE; + if route[route.len() - 1].pos.y() / SCALE > ymax { + ymax = route[route.len() - 1].pos.y() / SCALE; } println!("\\begin{{tikzpicture}}"); println!("\\tkzInit[xmax={},ymax={}]", xmax + 0.3, ymax + 0.3); @@ -58,10 +58,15 @@ pub(crate) fn dump_latex_code(route: &[RunState], polys: &[Polygon]) { println!(")"); } for (idx, s) in route.iter().enumerate() { - println!("\\tkzDefPoint({},{}){{R{}}}", s.pos.x()/SCALE, s.pos.y()/SCALE, idx); + println!( + "\\tkzDefPoint({},{}){{R{}}}", + s.pos.x() / SCALE, + s.pos.y() / SCALE, + idx + ); } for idx in 1..route.len() { - println!("\\tkzDrawSegment(R{},R{})", idx-1, idx); + println!("\\tkzDrawSegment(R{},R{})", idx - 1, idx); } for idx in 0..route.len() { println!("\\tkzDrawPoint[fill=red,color=black,size=13](R{})", idx); diff --git a/src/main.rs b/src/main.rs index ca12ba0..a9c8b02 100644 --- a/src/main.rs +++ b/src/main.rs @@ -52,9 +52,6 @@ fn main() { let data = input::read_input(); let house = data.start; let polys = data.polys; - for p in &polys { - assert!(p.interiors.is_empty()); - } // alle Ecken der Hindernisse bestimmen let points = polys .iter() @@ -62,7 +59,7 @@ fn main() { .flatten() .collect::>(); - // Startzustand + // Startzustand: Lisa ist an ihrem Haus, Bus noch nicht losgefahren let start = RunState { pos: house, bus: bus, @@ -71,62 +68,69 @@ fn main() { eprintln!("Maximum: {:?}", delay_to_time(start.delay)); + // Zustände in Max-Heap sortieren let mut states = BinaryHeap::new(); states.push(vec![start]); + // beste Lösung speichern let mut best_delay = 0.0; let mut best = vec![]; + // Zwischenlösungen speichern let save_prefix = "tmp_"; let mut save_counter = 0; // weitersuchen, bis kein besserer Zustand mehr vorhanden ist while states.peek().map(|x| x[0].delay > best_delay) == Some(true) { - let s = states.pop().unwrap(); - let last = &s[0]; + // besten Zustand einlesen + let state = states.pop().unwrap(); + let last = &state[0]; // neue Zustände let mut all = vec![]; - // versuche zu jeder anderen Ecke zu gehen + // versuche, zu jeder anderen Ecke zu gehen for next in points .iter() - .filter(|next| !s.iter().any(|x| x.pos == (**next).into())) + // Ecke sollte nicht schon in Route sein + .filter(|next| !state.iter().any(|p| p.pos == (**next).into())) + .map(|x| Point::from(*x)) + // Weg zu dieser Ecke sollte kein Polygon schneiden + .filter(|next| none_intersect(&polys, &Line::new(last.pos, *next))) { - let next = Point::from(*next); - if next != last.pos && none_intersect(&polys, &Line::new(last.pos, next)) { - // Lisa könnte zu dieser Ecke rennen - let bus_next = last.bus.translate(0.0, distance(last.pos, next) * 2.0); - let delay = max_possible_delay(bus_next, next); - if delay > best_delay { - let mut route = s.clone(); - route.insert( - 0, - RunState { - pos: next, - bus: bus_next, - delay, - }, - ); - all.push(route); - } + // Lisa könnte zu dieser Ecke rennen + let bus_next = last.bus.translate(0.0, distance(last.pos, next) * 2.0); + let delay = max_possible_delay(bus_next, next); + if delay > best_delay { + let mut route = state.clone(); + route.insert( + 0, + RunState { + pos: next, + bus: bus_next, + delay, + }, + ); + all.push(route); } } // versuche, direkt zum Bus zu gehen let bus = last.bus; let range = to_bus(bus, last.pos); if range.len() == 2 { + // Lisa trifft Bus mit 60°-Winkel let next = Point::new( 0.0, last.pos.y() + 30.0f64.to_radians().tan() * last.pos.x(), ); let line = Line::new(last.pos, next); + // freier Weg? if none_intersect(&polys, &line) { let delay = line.end.y - bus.y() - line.end_point().euclidean_distance(&line.start_point()) * 2.0; if delay > best_delay { // neue beste Wartezeit - let mut route = s.clone(); + let mut route = state.clone(); route.insert( 0, RunState { @@ -135,8 +139,9 @@ fn main() { delay, }, ); + // Verbesserung anzeigen und speichern eprintln!( - "Verbesserung: {:?} ({:?} Zustände verbleiben)", + "Verbesserung: {:?} ({:?}+ Zustände verbleiben)", delay_to_time(delay), states.len() ); @@ -152,12 +157,16 @@ fn main() { save_counter += 1; } } + // falls Bus überhaupt noch erreicht wird: neu gefundene Routen speichern states.extend(all); } } let route = best; eprintln!("Startzeit: {:?}", delay_to_time(best_delay)); - eprintln!("Zielzeit: {:?}", delay_to_time(route.last().unwrap().bus.y() - bus_start.y())); + eprintln!( + "Zielzeit: {:?}", + delay_to_time(route.last().unwrap().bus.y() - bus_start.y()) + ); eprintln!("Treffpunkt: y={:.0}", route.last().unwrap().pos.y()); let mut length = 0.0; let mut last = &house; @@ -168,7 +177,13 @@ fn main() { } else if s.pos.x() == 0.0 { eprint!("Treffpunkt: ") } else { - eprint!("Polygon {:02}: ", polys.iter().position(|p| p.exterior.0.contains(&s.pos.into())).unwrap() + 1); + eprint!( + "Polygon {:02}: ", + polys + .iter() + .position(|p| p.exterior.0.contains(&s.pos.into())) + .unwrap() + 1 + ); } eprintln!("x={:.02} y={:.02}", s.pos.x(), s.pos.y()); length += last.euclidean_distance(&s.pos); @@ -177,7 +192,8 @@ fn main() { eprintln!("Länge: {:.0}m", length); let seconds = length / (15.0 / 3.6); eprintln!("Dauer: {:02.0}:{:02.0}", seconds / 60.0, seconds % 60.0); - display::dump_latex_code(&route, &polys); + // beste Route grafisch ausgeben + display::dump_svg(&route, &polys); } fn none_intersect(polys: &[Polygon], line: &Line) -> bool { @@ -228,7 +244,7 @@ fn max_possible_delay(bus: Point, start: Point) -> f64 { 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. +/// Gehe direkt zum Bus. Gibt die Punkte zurück, bei denen Lisa nicht auf den Bus warten müsste. fn to_bus(bus: Point, start: Point) -> Vec { let x_l = start.x(); let y_l = start.y();