|
| 1 | +#![allow(non_snake_case)] |
| 2 | + |
| 3 | +use aoc::geometry::XY; |
| 4 | +use aoc::Puzzle; |
| 5 | +use itertools::Itertools; |
| 6 | + |
| 7 | +struct Rectangle { |
| 8 | + min_x: usize, |
| 9 | + max_x: usize, |
| 10 | + min_y: usize, |
| 11 | + max_y: usize, |
| 12 | +} |
| 13 | + |
| 14 | +impl Rectangle { |
| 15 | + fn from(p1: &XY, p2: &XY) -> Self { |
| 16 | + let min_x = p1.x().min(p2.x()) as usize; |
| 17 | + let max_x = p1.x().max(p2.x()) as usize; |
| 18 | + let min_y = p1.y().min(p2.y()) as usize; |
| 19 | + let max_y = p1.y().max(p2.y()) as usize; |
| 20 | + Rectangle { |
| 21 | + min_x, |
| 22 | + max_x, |
| 23 | + min_y, |
| 24 | + max_y, |
| 25 | + } |
| 26 | + } |
| 27 | + |
| 28 | + fn area(&self) -> usize { |
| 29 | + (self.max_x - self.min_x + 1) * (self.max_y - self.min_y + 1) |
| 30 | + } |
| 31 | + |
| 32 | + fn intersect(&self, other: &Self) -> bool { |
| 33 | + !(self.max_x <= other.min_x |
| 34 | + || self.min_x >= other.max_x |
| 35 | + || self.max_y <= other.min_y |
| 36 | + || self.min_y >= other.max_y) |
| 37 | + } |
| 38 | +} |
| 39 | + |
| 40 | +struct AoC2025_09; |
| 41 | + |
| 42 | +impl AoC2025_09 { |
| 43 | + fn rectangles<'a>( |
| 44 | + reds: &'a <Self as Puzzle>::Input, |
| 45 | + ) -> impl Iterator<Item = Rectangle> + 'a { |
| 46 | + reds.iter() |
| 47 | + .combinations(2) |
| 48 | + .map(move |c| Rectangle::from(c[0], c[1])) |
| 49 | + } |
| 50 | +} |
| 51 | + |
| 52 | +impl aoc::Puzzle for AoC2025_09 { |
| 53 | + type Input = Vec<XY>; |
| 54 | + type Output1 = usize; |
| 55 | + type Output2 = usize; |
| 56 | + |
| 57 | + aoc::puzzle_year_day!(2025, 9); |
| 58 | + |
| 59 | + fn parse_input(&self, lines: Vec<String>) -> Self::Input { |
| 60 | + lines |
| 61 | + .iter() |
| 62 | + .map(|line| { |
| 63 | + let (x, y) = line.split_once(',').unwrap(); |
| 64 | + XY::of(x.parse::<i32>().unwrap(), y.parse::<i32>().unwrap()) |
| 65 | + }) |
| 66 | + .collect::<Vec<_>>() |
| 67 | + } |
| 68 | + |
| 69 | + fn part_1(&self, reds: &Self::Input) -> Self::Output1 { |
| 70 | + Self::rectangles(reds).map(|r| r.area()).max().unwrap() |
| 71 | + } |
| 72 | + |
| 73 | + fn part_2(&self, reds: &Self::Input) -> Self::Output2 { |
| 74 | + let border_segments = reds |
| 75 | + .iter() |
| 76 | + .chain([reds[0]].iter()) |
| 77 | + .tuple_windows() |
| 78 | + .map(|(a, b)| Rectangle::from(a, b)) |
| 79 | + .collect::<Vec<_>>(); |
| 80 | + Self::rectangles(reds) |
| 81 | + .filter(|r| !border_segments.iter().any(|bs| r.intersect(bs))) |
| 82 | + .map(|r| r.area()) |
| 83 | + .max() |
| 84 | + .unwrap() |
| 85 | + } |
| 86 | + |
| 87 | + fn samples(&self) { |
| 88 | + aoc::puzzle_samples! { |
| 89 | + self, part_1, TEST, 50, |
| 90 | + self, part_2, TEST, 24 |
| 91 | + }; |
| 92 | + } |
| 93 | +} |
| 94 | + |
| 95 | +fn main() { |
| 96 | + AoC2025_09 {}.run(std::env::args()); |
| 97 | +} |
| 98 | + |
| 99 | +const TEST: &str = "\ |
| 100 | +7,1 |
| 101 | +11,1 |
| 102 | +11,7 |
| 103 | +9,7 |
| 104 | +9,5 |
| 105 | +2,5 |
| 106 | +2,3 |
| 107 | +7,3 |
| 108 | +"; |
| 109 | + |
| 110 | +#[cfg(test)] |
| 111 | +mod tests { |
| 112 | + use super::*; |
| 113 | + |
| 114 | + #[test] |
| 115 | + pub fn samples() { |
| 116 | + AoC2025_09 {}.samples(); |
| 117 | + } |
| 118 | +} |
0 commit comments