Day 8: Resonant Collinearity
Megathread guidelines
- Keep top level comments as only solutions, if you want to say something other than a solution put it in a new post. (replies to comments can be whatever)
- You can send code in code blocks by using three backticks, the code, and then three backticks or use something such as https://topaz.github.io/paste/ if you prefer sending it through a URL
FAQ
- What is this?: Here is a post with a large amount of details: https://programming.dev/post/6637268
- Where do I participate?: https://adventofcode.com/
- Is there a leaderboard for the community?: We have a programming.dev leaderboard with the info on how to join in this post: https://programming.dev/post/6631465
Rust
use std::collections::{HashMap, HashSet}; use crate::solver::DaySolver; use crate::grid::{Coordinate, Grid}; fn add_distance(coordinate: Coordinate, distance: (i64, i64)) -> Option<Coordinate> { coordinate.try_add(distance) } fn sub_distance(coordinate: Coordinate, distance: (i64, i64)) -> Option<Coordinate> { coordinate.try_sub(distance) } fn part2_possible_antinodes<F>( grid: &Grid<Option<char>>, coordinate: Coordinate, distance: (i64, i64), op: F, mut accumulator: Vec<Coordinate> ) -> Vec<Coordinate> where F: Fn(Coordinate, (i64, i64)) -> Option<Coordinate> { match op(coordinate, distance).filter(|c| grid.get(*c).is_some()) { None => accumulator, Some(next_coord) => { accumulator.push(next_coord); part2_possible_antinodes(grid, next_coord, distance, op, accumulator) } } } trait Pairable<T> { fn pairs(&self) -> Vec<(&T, &T)>; } impl<T> Pairable<T> for HashSet<T> { fn pairs(&self) -> Vec<(&T, &T)> { let v: Vec<&T> = self.iter().collect(); let mut p = vec![]; for i in 0..v.len() { let thing1 = v[i]; for thing2 in &v[i+1..] { p.push((thing1, *thing2)); } } p } } fn parse_input(input: String) -> (Grid<Option<char>>, HashMap<char, HashSet<Coordinate>>) { let g: Grid<Option<char>> = input.lines() .map(|line| line.chars() .map(|c| if c == '.' { None } else { Some(c) }).collect::<Vec<Option<char>>>() ) .collect::<Vec<Vec<Option<char>>>>() .into(); let mut freq_to_coords: HashMap<char, HashSet<Coordinate>> = HashMap::new(); for (coord, freq_opt) in g.iter() { match freq_opt { None => (), Some(freq) => { freq_to_coords.entry(*freq) .and_modify(|coords| { coords.insert(coord); }) .or_insert(HashSet::from([coord])); } } } (g, freq_to_coords) } pub struct Day08Solver; impl DaySolver for Day08Solver { fn part1(&self, input: String) -> usize { let (g, freq_to_coords) = parse_input(input); let mut antinodes: HashSet<Coordinate> = HashSet::new(); for (_, coords) in freq_to_coords { // println!("Freq = {}", freq); for (c1, c2) in coords.pairs() { let distance = c1.xy_distance_to(c2); let possible_antinodes: Vec<Coordinate> = [c1.try_sub(distance), c2.try_add(distance)].into_iter() .flat_map(|co| co.filter(|c| g.get(*c).is_some())) .collect(); // println!("Pair = ({},{}), antinodes = {:?}", c1, c2, possible_antinodes); for antinode in possible_antinodes { antinodes.insert(antinode); } } } antinodes.len() } fn part2(&self, input: String) -> usize { let (g, freq_to_coords) = parse_input(input); let mut antinodes: HashSet<Coordinate> = HashSet::new(); for (freq, coords) in freq_to_coords { println!("Freq = {}", freq); for (c1, c2) in coords.pairs() { let distance = c1.xy_distance_to(c2); let possible_antinodes: Vec<Coordinate> = [ part2_possible_antinodes(&g, *c1, distance, add_distance, vec![*c1]), part2_possible_antinodes(&g, *c1, distance, sub_distance, vec![*c1]), part2_possible_antinodes(&g, *c2, distance, add_distance, vec![*c2]), part2_possible_antinodes(&g, *c2, distance, sub_distance, vec![*c2]), ].into_iter().flatten().collect(); println!("Pair = ({},{}), antinodes = {:?}", c1, c2, possible_antinodes); for antinode in possible_antinodes { antinodes.insert(antinode); } } } antinodes.len() } }
https://gitlab.com/bricka/advent-of-code-2024-rust/-/blob/main/src/days/day08.rs?ref_type=heads