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..8cf7c01 --- /dev/null +++ b/src/day10/main.rs @@ -0,0 +1,268 @@ +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 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() + } + } +} + +struct MonitoringStation { + map: HashMap, + size_x: usize, + size_y: usize, +} + +impl MonitoringStation { + fn find(&self) -> (usize, XY) { + let mut best = (0, XY::from((0, 0))); + for x in 0..self.size_x { + for y in 0..self.size_y { + let root = XY::from((x, y)); + // we only can build on astroids + if *self.map.get(&root).unwrap() == Position::Empty { + continue; + } + 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; + if insight > best.0 { + best = (insight, root) + } + } + } + best + } +} + +impl From for MonitoringStation +where + T: BufRead, +{ + fn from(reader: T) -> Self { + let map: HashMap = HashMap::new(); + let mut size_y = 0; + let map = reader + .split(b'\n') + .enumerate() + .flat_map(|(y, line)| { + size_y += 1; + 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 + }); + let size_x = map.iter().count() / size_y; + MonitoringStation { + map, + size_x, + size_y, + } + } +} + +fn main() -> Result<(), Box> { + let f = File::open("input10")?; + let reader = BufReader::new(f); + let best = MonitoringStation::from(reader).find(); + println!("best: {:?}", best); + 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); + } +}