diff --git a/Cargo.toml b/Cargo.toml index a5a2b2b..3ee5431 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,4 +32,8 @@ path = "src/day7/main.rs" [[bin]] name = "day9" -path = "src/day9/main.rs" \ No newline at end of file +path = "src/day9/main.rs" + +[[bin]] +name = "day10" +path = "src/day10/main.rs" \ No newline at end of file diff --git a/input10 b/input10 new file mode 100644 index 0000000..51520ab --- /dev/null +++ b/input10 @@ -0,0 +1,24 @@ +.###.#...#.#.##.#.####.. +.#....#####...#.######.. +#.#.###.###.#.....#.#### +##.###..##..####.#.####. +###########.#######.##.# +##########.#########.##. +.#.##.########.##...###. +###.#.##.#####.#.###.### +##.#####.##..###.#.##.#. +.#.#.#####.####.#..##### +.###.#####.#..#..##.#.## +########.##.#...######## +.####..##..#.###.###.#.# +....######.##.#.######.# +###.####.######.#....### +############.#.#.##.#### +##...##..####.####.#..## +.###.#########.###..#.## +#.##.#.#...##...#####..# +##.#..###############.## +##.###.#####.##.######.. +##.#####.#.#.##..####### +...#######.######...#### +#....#.#.#.####.#.#.#.## diff --git a/src/day10/main.rs b/src/day10/main.rs new file mode 100644 index 0000000..9366ac0 --- /dev/null +++ b/src/day10/main.rs @@ -0,0 +1,399 @@ +use std::cmp::Ordering; +use std::collections::{HashMap, HashSet}; +use std::fs::File; +use std::io::{BufRead, BufReader}; +use std::ops::Sub; + +#[derive(Clone, Debug, PartialEq)] +enum Position { + Empty, + Astroid, +} + +impl From<&u8> for Position { + fn from(c: &u8) -> Self { + match c { + b'.' => Position::Empty, + b'#' => Position::Astroid, + _ => panic!("invalid input"), + } + } +} + +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +struct XY { + x: isize, + y: isize, +} + +impl From<(usize, usize)> for XY { + fn from((x, y): (usize, usize)) -> Self { + XY { + x: x as isize, + y: y as isize, + } + } +} + +impl Sub for &XY { + type Output = XY; + fn sub(self, other: &XY) -> Self::Output { + let xy = XY { + x: self.x - other.x, + y: self.y - other.y, + }; + xy + } +} + +impl PartialOrd for XY { + fn partial_cmp(&self, other: &XY) -> Option { + self.angle().partial_cmp(&other.angle()) + } +} + +impl Ord for XY { + fn cmp(&self, other: &XY) -> Ordering { + self.angle().partial_cmp(&other.angle()).unwrap() + } +} + +impl XY { + fn direction(&self) -> Self { + let d = gcd(self.y, self.x).abs(); + if d != 0 { + XY { + x: self.x / d, + y: self.y / d, + } + } else { + self.clone() + } + } + + fn distance(&self) -> f64 { + let sum = (self.x.pow(2) + self.y.pow(2)) as f64; + (sum as f64).sqrt() + } + + fn angle(&self) -> f64 { + let a = (-self.y as f64).atan2(-self.x as f64) * (180 as f64 / std::f64::consts::PI) + - 90 as f64; + if a < 0 as f64 { + a + 360 as f64 + } else { + a + } + } +} + +struct MonitoringStation { + map: HashMap, +} + +impl MonitoringStation { + fn laser(&self, root: &XY, skip: usize, n: usize) -> Vec { + let mut map: HashMap> = HashMap::new(); + // collect vec for each direction + map = self + .map + .iter() + .filter_map(|(xy, pos)| { + if *pos == Position::Empty { + return None; + } + if xy == root { + return None; + } + let rel = xy - &root; + Some((xy, rel.direction())) + }) + .fold(map, |mut acc, (xy, dir)| { + acc.entry(dir).or_insert(vec![]).push(xy.clone()); + acc + }); + + // order angle vecs by distance + map.iter_mut().for_each(|(_, val)| { + val.sort_by(|a, b| { + ((root - b).distance().partial_cmp(&(root - a).distance())).unwrap() + }); + }); + + // lets iterate over sorted angles + let mut angles = map.keys().cloned().collect::>(); + angles.sort(); + + angles + .iter() + .cycle() + .map(|xy| match map.get_mut(xy) { + Some(xy) => { + if let Some(xy) = xy.pop() { + Some(xy) + } else { + None + } + } + None => None, + }) + .flatten() + .skip(skip) + .take(n) + .collect::>() + } + + fn find(&self) -> (usize, XY) { + let best = (0, XY::from((0, 0))); + self.map + .iter() + .filter(|(_, pos)| **pos == Position::Astroid) + .map(|(root, _)| { + let insight = self + .map + .iter() + // get direction to any of the astroids + .filter_map(|(xy, pos)| { + if *pos == Position::Empty { + return None; + } + let rel = xy - root; + Some(rel.direction()) + }) + // and keep only one in each direction + .fold(HashSet::new(), |mut acc, xy| { + acc.insert(xy); + acc + }) + .len() + - 1; + (insight, root) + }) + .fold(best, |acc, astroid| { + if astroid.0 > acc.0 { + return (astroid.0, astroid.1.clone()); + } + acc + }) + } +} + +impl From for MonitoringStation +where + T: BufRead, +{ + fn from(reader: T) -> Self { + let map: HashMap = HashMap::new(); + let map = reader + .split(b'\n') + .enumerate() + .flat_map(|(y, line)| { + line.unwrap() + .iter() + .enumerate() + .map(|(x, c)| { + let xy = XY::from((x, y)); + let p = Position::from(c); + (xy, p) + }) + .collect::>() + }) + .fold(map, |mut map, (xy, p)| { + map.insert(xy.clone(), p.clone()); + map + }); + MonitoringStation { map } + } +} + +fn main() -> Result<(), Box> { + let f = File::open("input10")?; + let reader = BufReader::new(f); + let station = MonitoringStation::from(reader); + let best = station.find(); + println!("best: {:?}", best); + let lasered = station.laser(&best.1, 199, 1); + let lasered = lasered.iter().next().unwrap(); + println!("lasered: {:?}", lasered); + Ok(()) +} + +fn gcd(x: isize, y: isize) -> isize { + let mut x = x; + let mut y = y; + while y != 0 { + let t = y; + y = x % y; + x = t; + } + x +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn example1() { + let reader = BufReader::new(".#..#\n.....\n#####\n....#\n...##".as_bytes()); + let (count, xy) = MonitoringStation::from(reader).find(); + assert_eq!(xy, XY { x: 3, y: 4 }); + assert_eq!(count, 8); + } + + #[test] + fn example2() { + let reader = BufReader::new( + " +......#.#. +#..#.#.... +..#######. +.#.#.###.. +.#..#..... +..#....#.# +#..#....#. +.##.#..### +##...#..#. +.#....####" + .trim() + .as_bytes(), + ); + let (count, xy) = MonitoringStation::from(reader).find(); + assert_eq!(xy, XY { x: 5, y: 8 }); + assert_eq!(count, 33); + } + + #[test] + fn example3() { + let reader = BufReader::new( + " +#.#...#.#. +.###....#. +.#....#... +##.#.#.#.# +....#.#.#. +.##..###.# +..#...##.. +..##....## +......#... +.####.###." + .trim() + .as_bytes(), + ); + let (count, xy) = MonitoringStation::from(reader).find(); + assert_eq!(xy, XY { x: 1, y: 2 }); + assert_eq!(count, 35); + } + + #[test] + fn example4() { + let reader = BufReader::new( + " +.#..#..### +####.###.# +....###.#. +..###.##.# +##.##.#.#. +....###..# +..#.#..#.# +#..#.#.### +.##...##.# +.....#.#.." + .trim() + .as_bytes(), + ); + let (count, xy) = MonitoringStation::from(reader).find(); + assert_eq!(xy, XY { x: 6, y: 3 }); + assert_eq!(count, 41); + } + + #[test] + fn example5() { + let reader = BufReader::new( + " +.#..##.###...####### +##.############..##. +.#.######.########.# +.###.#######.####.#. +#####.##.#.##.###.## +..#####..#.######### +#################### +#.####....###.#.#.## +##.################# +#####.##.###..####.. +..######..##.####### +####.##.####...##..# +.#####..#.######.### +##...#.##########... +#.##########.####### +.####.#.###.###.#.## +....##.##.###..##### +.#.#.###########.### +#.#.#.#####.####.### +###.##.####.##.#..##" + .trim() + .as_bytes(), + ); + let (count, xy) = MonitoringStation::from(reader).find(); + assert_eq!(xy, XY { x: 11, y: 13 }); + assert_eq!(count, 210); + } + + #[test] + fn example6() { + let reader = BufReader::new( + " +.#....#####...#.. +##...##.#####..## +##...#...#.#####. +..#.....#...###.. +..#.#.....#....## +" + .trim() + .as_bytes(), + ); + let ms = MonitoringStation::from(reader); + ms.find(); + assert_eq!( + ms.laser(&XY::from((8, 3)), 0, 36), + vec![ + XY { x: 8, y: 1 }, + XY { x: 9, y: 0 }, + XY { x: 9, y: 1 }, + XY { x: 10, y: 0 }, + XY { x: 9, y: 2 }, + XY { x: 11, y: 1 }, + XY { x: 12, y: 1 }, + XY { x: 11, y: 2 }, + XY { x: 15, y: 1 }, + XY { x: 12, y: 2 }, + XY { x: 13, y: 2 }, + XY { x: 14, y: 2 }, + XY { x: 15, y: 2 }, + XY { x: 12, y: 3 }, + XY { x: 16, y: 4 }, + XY { x: 15, y: 4 }, + XY { x: 10, y: 4 }, + XY { x: 4, y: 4 }, + XY { x: 2, y: 4 }, + XY { x: 2, y: 3 }, + XY { x: 0, y: 2 }, + XY { x: 1, y: 2 }, + XY { x: 0, y: 1 }, + XY { x: 1, y: 1 }, + XY { x: 5, y: 2 }, + XY { x: 1, y: 0 }, + XY { x: 5, y: 1 }, + XY { x: 6, y: 1 }, + XY { x: 6, y: 0 }, + XY { x: 7, y: 0 }, + XY { x: 8, y: 0 }, + XY { x: 10, y: 1 }, + XY { x: 14, y: 0 }, + XY { x: 16, y: 1 }, + XY { x: 13, y: 3 }, + XY { x: 14, y: 3 } + ] + ) + } +}