diff --git a/src/tasks/day10.rs b/src/tasks/day10.rs index 33edd15..c67fe90 100644 --- a/src/tasks/day10.rs +++ b/src/tasks/day10.rs @@ -1,3 +1,4 @@ +use itertools::Itertools; use std::collections::HashSet; #[allow(dead_code)] @@ -15,13 +16,88 @@ pub fn run() { .flatten() .collect(); task1(&asteroids); + task2(&asteroids); } fn task1(asteroids: &HashSet<(i32, i32)>) { - let max = asteroids + let (max, _) = get_best_station(&asteroids); + println!( + "Task 1: maximum visible asteroids from another asteroid are {}", + max + ); +} + +fn task2(asteroids: &HashSet<(i32, i32)>) { + let (_, station) = get_best_station(&asteroids); + println!("Task 2: The station is at {:?}", station); + let mut others = asteroids.clone(); + others.remove(&station); + // map to polar coordinate + let mut others: Vec<((i32, i32), (f32, f32))> = others + .into_iter() + .map(|other| (other, to_polar(station, other))) + .collect(); + use std::cmp::Ordering; + others.sort_by(|(_, a), (_, b)| { + // sort by angle + if a.0 < b.0 { + Ordering::Less + } else { + Ordering::Greater + } + }); + let mut v: Vec<(f32, Vec<_>)> = others + .iter() + .group_by(|(_, (alpha, _))| alpha) + .into_iter() + .map(|(angle, group)| (*angle, group.collect())) + .collect(); + + let mut i = 0; + let mut shot = Vec::new(); + while shot.len() < 200 { + while v[i].1.is_empty() { + i = (i + 1) % v.len(); + } + shot.push(v[i].1.remove(0)); + i = (i + 1) % v.len(); + } + + let ((x, y), _) = shot.last().unwrap(); + println!("Task 2: {}", x * 100 + y); +} + +fn d(a: (i32, i32), b: (i32, i32)) -> f32 { + ((a.0 - b.0) as f32).powi(2) + ((a.1 - b.1) as f32).powi(2).sqrt() +} + +/** + * generate polar coordinate relative to POV. + * rotate angles such that "up" is 0. +*/ +fn to_polar(pov: (i32, i32), other: (i32, i32)) -> (f32, f32) { + let mut alpha = + ((other.1 - pov.1) as f32).atan2((other.0 - pov.0) as f32) + std::f32::consts::FRAC_PI_2; + if alpha < 0.0 { + alpha += 2.0 * std::f32::consts::PI; + } + (alpha, d(pov, other)) +} + +fn blocks_view(pov: (i32, i32), target: (i32, i32), occluder: (i32, i32)) -> bool { + if occluder == target { + return false; + } + // using polar coordinates + let (ao, d_o) = to_polar(pov, occluder); + let (at, d_t) = to_polar(pov, target); + (ao - at).abs() <= std::f32::EPSILON && d_t > d_o +} + +fn get_best_station(asteroids: &HashSet<(i32, i32)>) -> (usize, (i32, i32)) { + asteroids .iter() .map(|asteroid| { - println!("inspecting {:?}", asteroid); // map to number of other asteroids it sees let mut others = asteroids.clone(); others.remove(&asteroid); @@ -32,35 +108,13 @@ fn task1(asteroids: &HashSet<(i32, i32)>) { let blocker = others .iter() .find(|occluder| blocks_view(*asteroid, **target, **occluder)); - if let Some(occluder) = blocker { - println!("{:?} -> X{:?}X -> {:?}", asteroid, occluder, target); - } !blocker.is_some() }) - .inspect(|target| println!("sees {:?}", target)) .count(); - println!("{:?}: {}", asteroid, count); - count + (count, *asteroid) }) - .max() - .unwrap(); - println!( - "Task 1: maximum visible asteroids from another asteroid are {}", - max - ); -} - -fn blocks_view(pov: (i32, i32), target: (i32, i32), occluder: (i32, i32)) -> bool { - if occluder == target { - return false; - } - fn d(a: (i32, i32), b: (i32, i32)) -> f32 { - ((a.0 - b.0) as f32).powi(2) + ((a.1 - b.1) as f32).powi(2).sqrt() - } - // using polar coordinates - let alpha_occluder = ((occluder.1 - pov.1) as f32).atan2((occluder.0 - pov.0) as f32); - let alpha_target = ((target.1 - pov.1) as f32).atan2((target.0 - pov.0) as f32); - (alpha_occluder - alpha_target).abs() <= std::f32::EPSILON && d(pov, target) > d(pov, occluder) + .max_by_key(|(count, _)| *count) + .unwrap() } mod test {