use itertools::Itertools; #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] pub struct Position { pub x: usize, pub y: usize, } #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] pub struct Field { rows: usize, columns: usize, astroids: Vec, } fn gcd(mut x: isize, mut y: isize) -> isize { loop { if y == 0 { return x; } x = x % y; if x == 0 { return y; } y = y % x; } } impl Field { pub fn parse(input: &str) -> Self { let lines = input.lines().collect::>(); let rows = lines.len(); let columns = lines[0].len(); let mut astroids = Vec::new(); for line in lines { assert_eq!(columns, line.len()); for cell in line.chars() { astroids.push(match cell { '#' => true, '.' => false, _ => panic!("Unexpected cell: {:?}", cell), }) } } Self { rows, columns, astroids, } } pub fn sees(&self, a: Position, b: Position) -> bool { if a == b { return true; } let diff_x = b.x as isize - a.x as isize; let diff_y = b.y as isize - a.y as isize; let d = gcd(diff_x, diff_y).abs(); let diff_x = diff_x / d; let diff_y = diff_y / d; let mut pos = a; loop { pos.x = (pos.x as isize + diff_x) as usize; pos.y = (pos.y as isize + diff_y) as usize; if pos == b { return true; } if self[pos] { return false; } } } pub fn all_astroids(&self) -> impl Iterator + '_ { (0..self.columns).cartesian_product(0..self.rows).map(|(x, y)| Position { x, y }).filter(move |pos| self[*pos]) } pub fn best_monitoring_station(&self) -> (Position, usize) { self.all_astroids().map(|monitor| { // can see itself, but only count "others" (monitor, self.all_astroids().filter(|astroid| self.sees(monitor, *astroid)).count() - 1) }).max_by_key(|(_, n)| *n).unwrap() } } impl std::ops::Index for Field { type Output = bool; fn index(&self, index: Position) -> &Self::Output { assert!(index.x < self.columns); assert!(index.y < self.rows); &self.astroids[index.y * self.columns + index.x] } } fn main() { let input = include_str!("input.txt"); println!("Best monitoring station sees astroids: {}", Field::parse(input).best_monitoring_station().1); } #[cfg(test)] mod day10test { use super::*; #[test] fn examples1() { assert_eq!( Field::parse(".#..# ..... ##### ....# ...##").best_monitoring_station(), (Position { x: 3, y: 4 }, 8), ); } }