use std::collections::{HashMap, HashSet}; struct Antenna { x: usize, y: usize, marker: char, } struct AntennaMap { width: usize, height: usize, antennas: Vec, } #[derive(Hash, PartialEq, Eq)] struct Antinode { x: usize, y: usize, } fn load_input() -> AntennaMap { let input = std::fs::read_to_string("./input.txt").expect("Should be able to read input file"); let lines: Vec<&str> = input.lines().collect(); let antennas: Vec = lines .iter() .enumerate() .flat_map(|(y, line)| { line.chars().enumerate().filter_map(move |(x, ch)| { if ch != '.' { Some(Antenna { x, y, marker: ch }) } else { None } }) }) .collect(); AntennaMap { width: lines.iter().next().unwrap().len(), height: lines.len(), antennas, } } fn group_antennas(antennas: &Vec) -> HashMap> { let mut map = HashMap::new(); for antenna in antennas.into_iter() { if !map.contains_key(&antenna.marker) { map.insert(antenna.marker, vec![]); } map.get_mut(&antenna.marker).unwrap().push(antenna); } map } fn get_nth_antinode(antenna_a: &Antenna, antenna_b: &Antenna, n: usize) -> Option { if (n + 1) * antenna_a.x >= n * antenna_b.x && (n + 1) * antenna_a.y >= n * antenna_b.y { Some(Antinode { x: (n + 1) * antenna_a.x - n * antenna_b.x, y: (n + 1) * antenna_a.y - n * antenna_b.y, }) } else { None } } fn get_antinodes( antennas: &Vec<&Antenna>, width: usize, height: usize, max_n: usize, include_self: bool, ) -> Vec { let mut antinodes = vec![]; for (idx, antenna_a) in antennas.iter().enumerate() { for antenna_b in antennas[idx + (if include_self { 0 } else { 1 })..].iter() { for n in 1..=max_n { antinodes.push(get_nth_antinode(antenna_a, antenna_b, n)); antinodes.push(get_nth_antinode(antenna_b, antenna_a, n)); } } } antinodes .into_iter() .filter_map(|node_opt| node_opt) .filter(|node| node.x < width && node.y < height) .collect() } fn task1() { let antenna_map = load_input(); let by_frequency = group_antennas(&antenna_map.antennas); let all_antinodes = by_frequency .iter() .flat_map(|entry| get_antinodes(entry.1, antenna_map.width, antenna_map.height, 1, false)); let unique_antinodes: HashSet = all_antinodes.collect(); println!("Task 1: Unique nodes: {}", unique_antinodes.len()); } fn task2() { let antenna_map = load_input(); let by_frequency = group_antennas(&antenna_map.antennas); let all_antinodes = by_frequency .iter() .flat_map(|entry| get_antinodes(entry.1, antenna_map.width, antenna_map.height, 50, true)); // arbitrarily high n to make sure we get everything inside the map let unique_antinodes: HashSet = all_antinodes.collect(); println!("Task 2: Unique nodes: {}", unique_antinodes.len()); } fn main() { task1(); task2(); }