|
| 1 | +use std::{ |
| 2 | + cmp, |
| 3 | + collections::{HashMap, HashSet}, |
| 4 | + fs, |
| 5 | + path::PathBuf, |
| 6 | +}; |
| 7 | + |
| 8 | +use itertools::iproduct; |
| 9 | + |
| 10 | +pub fn day08(mut input_path: PathBuf) { |
| 11 | + input_path.push("08.txt"); |
| 12 | + let content = fs::read_to_string(input_path).unwrap(); |
| 13 | + |
| 14 | + let (antennas, corner) = read(&content); |
| 15 | + |
| 16 | + println!("{:?}", count_antinodes(&antennas, ((0, 0), corner), antinodes_part1)); |
| 17 | + println!("{:?}", count_antinodes(&antennas, ((0, 0), corner), antinodes_part2)); |
| 18 | +} |
| 19 | + |
| 20 | +// Positive y-direction is up! |
| 21 | +type Position = (i32, i32); |
| 22 | +type ResonancePoints = HashMap<char, Vec<Position>>; |
| 23 | + |
| 24 | +fn read(input: &str) -> (ResonancePoints, (i32, i32)) { |
| 25 | + let (mut x_max, mut y_max) = (0, 0); |
| 26 | + let mut antennas = HashMap::new(); |
| 27 | + for (y, line) in input.lines().enumerate() { |
| 28 | + y_max = y_max.max(y); |
| 29 | + for (x, c) in line.chars().enumerate() { |
| 30 | + x_max = x_max.max(x); |
| 31 | + if c != '.' { |
| 32 | + antennas |
| 33 | + .entry(c) |
| 34 | + .or_insert(Vec::new()) |
| 35 | + .push((x as i32, -(y as i32))) |
| 36 | + } |
| 37 | + } |
| 38 | + } |
| 39 | + (antennas, (x_max as i32, -(y_max as i32))) |
| 40 | +} |
| 41 | + |
| 42 | +fn count_antinodes( |
| 43 | + antennas: &ResonancePoints, |
| 44 | + bounds: (Position, Position), |
| 45 | + generate_antinodes: fn(&[Position], (Position, Position)) -> Vec<Position>, |
| 46 | +) -> usize { |
| 47 | + antennas |
| 48 | + .iter() |
| 49 | + .flat_map(|(_, positions)| generate_antinodes(&positions, bounds)) |
| 50 | + .collect::<HashSet<_>>() |
| 51 | + .len() |
| 52 | +} |
| 53 | + |
| 54 | +fn antinodes_part1(antennas: &[Position], bounds: (Position, Position)) -> Vec<Position> { |
| 55 | + iproduct!(antennas, antennas) |
| 56 | + .filter(|(p1, p2)| p1 != p2) |
| 57 | + .map(|((x1, y1), (x2, y2))| (2 * x1 - x2, 2 * y1 - y2)) |
| 58 | + .filter(|p| is_inside(*p, bounds)) |
| 59 | + .collect() |
| 60 | +} |
| 61 | + |
| 62 | +fn antinodes_part2(antennas: &[Position], bounds: (Position, Position)) -> Vec<Position> { |
| 63 | + iproduct!(antennas, antennas) |
| 64 | + .filter(|(p1, p2)| p1 != p2) |
| 65 | + .flat_map(|((x1, y1), (x2, y2))| { |
| 66 | + (0..) |
| 67 | + .map(move |i| ((i+1) * x1 - i * x2, (i+ 1) * y1 - i * y2)) |
| 68 | + .take_while(|p| is_inside(*p, bounds)) |
| 69 | + }) |
| 70 | + .collect() |
| 71 | +} |
| 72 | + |
| 73 | +fn is_inside(p: Position, bounds: (Position, Position)) -> bool { |
| 74 | + // ugh |
| 75 | + let (x, y) = p; |
| 76 | + let ((x1, y1), (x2, y2)) = bounds; |
| 77 | + let [x_min, x_max] = cmp::minmax(x1, x2); |
| 78 | + let [y_min, y_max] = cmp::minmax(y1, y2); |
| 79 | + x_min <= x && x <= x_max && y_min <= y && y <= y_max |
| 80 | +} |
0 commit comments