From 58f9bab125b5d62e069cf545bd2e6c4cf6e32b1c Mon Sep 17 00:00:00 2001 From: Luke Else Date: Tue, 9 Dec 2025 21:57:41 +0000 Subject: [PATCH] feat: Completed day 9 part 1.. Part 2 just takes way too long to run --- input/day09 | 496 +++++++++++++++++++++++++++++++++++++++++ input/day09_test1 | 8 + input/day09_test2 | 8 + src/main.rs | 15 +- src/solutions/day09.rs | 292 +++++++++++++++++++++++- 5 files changed, 798 insertions(+), 21 deletions(-) diff --git a/input/day09 b/input/day09 index e69de29..5446b1f 100644 --- a/input/day09 +++ b/input/day09 @@ -0,0 +1,496 @@ +97924,50345 +97924,51571 +98267,51571 +98267,52762 +97672,52762 +97672,54006 +98033,54006 +98033,55223 +97930,55223 +97930,56355 +97187,56355 +97187,57620 +97455,57620 +97455,58755 +96898,58755 +96898,60029 +97081,60029 +97081,61275 +97045,61275 +97045,62296 +96120,62296 +96120,63411 +95626,63411 +95626,64658 +95567,64658 +95567,65962 +95623,65962 +95623,66827 +94448,66827 +94448,68020 +94185,68020 +94185,69214 +93900,69214 +93900,70281 +93316,70281 +93316,71397 +92837,71397 +92837,72352 +92049,72352 +92049,73659 +91911,73659 +91911,74892 +91598,74892 +91598,75591 +90405,75591 +90405,76766 +89983,76766 +89983,77931 +89520,77931 +89520,78545 +88292,78545 +88292,79538 +87598,79538 +87598,80413 +86755,80413 +86755,81391 +86038,81391 +86038,82481 +85435,82481 +85435,83432 +84666,83432 +84666,84001 +83514,84001 +83514,85275 +83050,85275 +83050,86053 +82104,86053 +82104,86679 +81032,86679 +81032,87054 +79771,87054 +79771,88208 +79137,88208 +79137,89041 +78237,89041 +78237,89353 +76971,89353 +76971,90543 +76303,90543 +76303,91193 +75265,91193 +75265,91236 +73875,91236 +73875,92568 +73227,92568 +73227,92557 +71841,92557 +71841,93088 +70751,93088 +70751,93718 +69706,93718 +69706,94302 +68634,94302 +68634,95167 +67665,95167 +67665,95074 +66325,95074 +66325,95758 +65273,95758 +65273,95986 +64064,95986 +64064,96370 +62907,96370 +62907,96471 +61675,96471 +61675,96573 +60454,96573 +60454,97441 +59393,97441 +59393,97281 +58122,97281 +58122,97594 +56940,97594 +56940,97532 +55705,97532 +55705,97315 +54467,97315 +54467,97784 +53292,97784 +53292,97610 +52070,97610 +52070,97786 +50866,97786 +50866,98242 +49652,98242 +49652,98030 +48436,98030 +48436,98037 +47216,98037 +47216,97990 +45997,97990 +45997,97488 +44824,97488 +44824,97993 +43535,97993 +43535,97477 +42375,97477 +42375,97150 +41197,97150 +41197,97360 +39910,97360 +39910,96634 +38823,96634 +38823,96666 +37557,96666 +37557,96488 +36334,96488 +36334,95918 +35228,95918 +35228,95121 +34212,95121 +34212,94577 +33123,94577 +33123,94857 +31705,94857 +31705,93771 +30841,93771 +30841,93738 +29520,93738 +29520,92937 +28552,92937 +28552,92599 +27355,92599 +27355,92067 +26252,92067 +26252,91311 +25278,91311 +25278,90840 +24133,90840 +24133,90199 +23088,90199 +23088,88851 +22540,88851 +22540,88832 +21051,88832 +21051,87608 +20452,87608 +20452,87102 +19299,87102 +19299,86330 +18353,86330 +18353,85553 +17409,85553 +17409,84855 +16386,84855 +16386,83659 +15852,83659 +15852,83143 +14625,83143 +14625,81803 +14283,81803 +14283,81266 +13043,81266 +13043,80286 +12304,80286 +12304,79007 +11961,79007 +11961,78307 +10862,78307 +10862,77157 +10375,77157 +10375,76323 +9424,76323 +9424,75099 +9076,75099 +9076,73909 +8704,73909 +8704,73119 +7629,73119 +7629,71799 +7523,71799 +7523,70975 +6447,70975 +6447,69897 +5858,69897 +5858,68675 +5600,68675 +5600,67341 +5660,67341 +5660,66365 +4812,66365 +4812,65200 +4459,65200 +4459,64020 +4157,64020 +4157,62919 +3584,62919 +3584,61586 +3884,61586 +3884,60595 +2797,60595 +2797,59348 +2787,59348 +2787,58082 +2955,58082 +2955,56942 +2390,56942 +2390,55696 +2543,55696 +2543,54506 +2266,54506 +2266,53302 +2065,53302 +2065,52073 +2310,52073 +2310,50861 +2471,50861 +2471,50321 +94552,50321 +94552,48440 +2091,48440 +2091,47247 +2499,47247 +2499,45975 +1750,45975 +1750,44746 +1790,44746 +1790,43644 +2817,43644 +2817,42335 +2272,42335 +2272,41178 +2747,41178 +2747,39918 +2675,39918 +2675,38910 +3727,38910 +3727,37719 +3942,37719 +3942,36385 +3684,36385 +3684,35253 +4159,35253 +4159,34244 +4967,34244 +4967,32829 +4645,32829 +4645,31939 +5715,31939 +5715,30641 +5772,30641 +5772,29771 +6797,29771 +6797,28353 +6662,28353 +6662,27683 +8016,27683 +8016,26189 +7820,26189 +7820,25275 +8682,25275 +8682,24304 +9430,24304 +9430,23426 +10305,23426 +10305,22541 +11149,22541 +11149,21158 +11311,21158 +11311,20566 +12536,20566 +12536,19309 +12910,19309 +12910,18275 +13579,18275 +13579,17372 +14405,17372 +14405,16451 +15212,16451 +15212,16154 +16639,16154 +16639,14608 +16840,14608 +16840,14147 +18075,14147 +18075,13608 +19212,13608 +19212,12928 +20214,12928 +20214,11846 +20904,11846 +20904,11174 +21918,11174 +21918,10743 +23094,10743 +23094,9637 +23814,9637 +23814,8950 +24822,8950 +24822,8144 +25765,8144 +25765,7608 +26869,7608 +26869,6967 +27915,6967 +27915,6471 +29036,6471 +29036,5965 +30150,5965 +30150,5315 +31204,5315 +31204,5542 +32611,5542 +32611,5091 +33734,5091 +33734,4100 +34679,4100 +34679,4289 +36019,4289 +36019,3710 +37115,3710 +37115,3331 +38274,3331 +38274,2798 +39404,2798 +39404,2844 +40663,2844 +40663,2936 +41914,2936 +41914,2855 +43125,2855 +43125,2289 +44272,2289 +44272,2471 +45512,2471 +45512,2554 +46730,2554 +46730,2366 +47928,2366 +47928,1748 +49125,1748 +49125,2381 +50343,2381 +50343,1681 +51573,1681 +51573,2173 +52771,2173 +52771,1917 +54010,1917 +54010,2346 +55193,2346 +55193,1968 +56469,1968 +56469,2224 +57671,2224 +57671,2462 +58874,2462 +58874,2875 +60039,2875 +60039,3188 +61219,3188 +61219,3530 +62390,3530 +62390,3672 +63618,3672 +63618,3901 +64829,3901 +64829,4864 +65792,4864 +65792,5525 +66837,5525 +66837,5472 +68159,5472 +68159,6112 +69209,6112 +69209,6929 +70166,6929 +70166,7451 +71252,7451 +71252,7552 +72564,7552 +72564,8010 +73703,8010 +73703,8995 +74537,8995 +74537,9644 +75559,9644 +75559,10487 +76451,10487 +76451,10811 +77697,10811 +77697,11508 +78693,11508 +78693,12208 +79691,12208 +79691,13360 +80317,13360 +80317,14116 +81256,14116 +81256,14472 +82566,14472 +82566,15209 +83551,15209 +83551,16126 +84365,16126 +84365,17393 +84800,17393 +84800,18024 +85908,18024 +85908,19134 +86483,19134 +86483,19631 +87796,19631 +87796,21067 +87939,21067 +87939,21616 +89242,21616 +89242,22794 +89694,22794 +89694,23663 +90594,23663 +90594,24868 +90974,24868 +90974,26021 +91413,26021 +91413,26925 +92287,26925 +92287,28097 +92676,28097 +92676,29149 +93292,29149 +93292,30350 +93590,30350 +93590,31557 +93844,31557 +93844,32548 +94619,32548 +94619,33610 +95252,33610 +95252,34795 +95551,34795 +95551,36023 +95698,36023 +95698,37160 +96125,37160 +96125,38352 +96359,38352 +96359,39381 +97306,39381 +97306,40749 +96718,40749 +96718,41836 +97519,41836 +97519,43042 +97708,43042 +97708,44278 +97660,44278 +97660,45480 +97870,45480 +97870,46724 +97536,46724 +97536,47910 +98058,47910 +98058,49129 +98032,49129 +98032,50345 \ No newline at end of file diff --git a/input/day09_test1 b/input/day09_test1 index e69de29..7fb70de 100644 --- a/input/day09_test1 +++ b/input/day09_test1 @@ -0,0 +1,8 @@ +7,1 +11,1 +11,7 +9,7 +9,5 +2,5 +2,3 +7,3 \ No newline at end of file diff --git a/input/day09_test2 b/input/day09_test2 index e69de29..7fb70de 100644 --- a/input/day09_test2 +++ b/input/day09_test2 @@ -0,0 +1,8 @@ +7,1 +11,1 +11,7 +9,7 +9,5 +2,5 +2,3 +7,3 \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 04966ec..96fb560 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,23 +20,10 @@ async fn main() -> Result<(), Box> { Box::new(day06::Day06 {}), Box::new(day07::Day07 {}), Box::new(day08::Day08 {}), - // Box::new(day09::Day09 {}), + Box::new(day09::Day09 {}), // Box::new(day10::Day10 {}), // Box::new(day11::Day11 {}), // Box::new(day12::Day12 {}), - // Box::new(day13::Day13 {}), - // Box::new(day14::Day14 {}), - // Box::new(day15::Day15 {}), - // Box::new(day16::Day16 {}), - // Box::new(day17::Day17 {}), - // Box::new(day18::Day18 {}), - // Box::new(day19::Day19 {}), - // Box::new(day20::Day20 {}), - // Box::new(day21::Day21 {}), - // Box::new(day22::Day22 {}), - // Box::new(day23::Day23 {}), - // Box::new(day24::Day24 {}), - // Box::new(day25::Day25 {}), ]; // Vector to store every solution as a thread handle diff --git a/src/solutions/day09.rs b/src/solutions/day09.rs index 3ce348a..9bf5b76 100644 --- a/src/solutions/day09.rs +++ b/src/solutions/day09.rs @@ -1,20 +1,24 @@ use super::Solution; +use std::collections::VecDeque; + pub struct Day09 {} impl Solution for Day09 { fn part1( &self, - _input: &mut Vec, + input: &mut Vec, ) -> Result, Box> { - Ok(Box::new("Ready")) + let points = self.parse_points(input)?; + Ok(Box::new(self.max_rectangle_area(&points))) } fn part2( &self, - _input: &mut Vec, + input: &mut Vec, ) -> Result, Box> { - Ok(Box::new("Ready")) + let red = self.parse_points(input)?; + Ok(Box::new(self.largest_red_green_rectangle(&red))) } fn get_day(&self) -> u8 { @@ -22,7 +26,281 @@ impl Solution for Day09 { } } -impl Day09 {} +impl Day09 { + fn parse_points(&self, reader: &[String]) -> Result, Box> { + let mut pts = Vec::new(); + + for line in reader.iter() { + let trimmed = line.trim(); + if trimmed.is_empty() { + continue; + } + let mut parts = trimmed.split(','); + let x_str = parts.next().ok_or("Missing x")?.trim(); + let y_str = parts.next().ok_or("Missing y")?.trim(); + + let x = x_str.parse::()?; + let y = y_str.parse::()?; + pts.push(Point { x, y }); + } + + Ok(pts) + } + + fn max_rectangle_area(&self, points: &[Point]) -> i64 { + let n = points.len(); + let mut best = 0i64; + + for i in 0..n { + for j in (i + 1)..n { + let a = points[i]; + let b = points[j]; + let area = self.rectangle_area(a, b); + if area > best { + best = area; + } + } + } + + best + } + + fn build_edges(&self, red: &[Point]) -> Vec { + let mut v = Vec::new(); + let n = red.len(); + for i in 0..n { + let a = red[i]; + let b = red[(i + 1) % n]; + if a.x == b.x { + let x = a.x; + let y1 = a.y.min(b.y); + let y2 = a.y.max(b.y); + for y in y1..=y2 { + v.push(Point { x, y }); + } + } else if a.y == b.y { + let y = a.y; + let x1 = a.x.min(b.x); + let x2 = a.x.max(b.x); + for x in x1..=x2 { + v.push(Point { x, y }); + } + } else { + panic!("Adjacent red points must be aligned."); + } + } + v + } + + fn flood_fill_interior( + &self, + edges: &[Point], + min_x: i32, + min_y: i32, + width: usize, + height: usize, + ) -> BitGrid { + let mut grid = BitGrid::new(min_x, min_y, width, height); + for &p in edges { + grid.set(p.x, p.y); + } + + // Flood-fill from outside + let mut visited = BitGrid::new(min_x, min_y, width, height); + + let mut q = VecDeque::new(); + let start = Point { x: min_x, y: min_y }; + q.push_back(start); + visited.set(start.x, start.y); + + let dirs = [(1, 0), (-1, 0), (0, 1), (0, -1)]; + + while let Some(p) = q.pop_front() { + for (dx, dy) in dirs { + let np = Point { + x: p.x + dx, + y: p.y + dy, + }; + if np.x < min_x + || np.x >= min_x + width as i32 + || np.y < min_y + || np.y >= min_y + height as i32 + { + continue; + } + if grid.get(np.x, np.y) { + // edge blocks movement + continue; + } + if !visited.get(np.x, np.y) { + visited.set(np.x, np.y); + q.push_back(np); + } + } + } + + // Build final valid = edges + interior + let mut valid = BitGrid::new(min_x, min_y, width, height); + for y in 0..height { + for block in 0..valid.blocks_per_row { + let outside = visited.rows[y][block]; + let edge = grid.rows[y][block]; + let interior = (!outside) & (!edge); + valid.rows[y][block] = edge | interior; + } + } + + valid + } + + fn rectangle_ok(&self, a: Point, b: Point, grid: &BitGrid) -> bool { + let x1 = a.x.min(b.x); + let x2 = a.x.max(b.x); + let y1 = a.y.min(b.y); + let y2 = a.y.max(b.y); + + let start = (x1 - grid.min_x) as usize; + let end = (x2 - grid.min_x) as usize; + + let block_start = start / 64; + let block_end = end / 64; + + for y in y1..=y2 { + let row_index = (y - grid.min_y) as usize; + let row = &grid.rows[row_index]; + + for (blk, _) in row.iter().enumerate().take(block_end + 1).skip(block_start) { + let left_bit = if blk == block_start { start % 64 } else { 0 }; + let right_bit = if blk == block_end { end % 64 } else { 63 }; + let mask = if left_bit == 0 && right_bit == 63 { + !0u64 + } else { + ((1u64 << (right_bit + 1)) - 1) & !((1u64 << left_bit) - 1) + }; + + let bits = row[blk]; + if (bits & mask) != mask { + return false; + } + } + } + true + } + + fn largest_red_green_rectangle(&self, red: &[Point]) -> i64 { + // Determine bounding box (expand by 2 for flood-fill safety) + let min_x = red.iter().map(|p| p.x).min().unwrap() - 2; + let max_x = red.iter().map(|p| p.x).max().unwrap() + 2; + let min_y = red.iter().map(|p| p.y).min().unwrap() - 2; + let max_y = red.iter().map(|p| p.y).max().unwrap() + 2; + + let width = (max_x - min_x + 1) as usize; + let height = (max_y - min_y + 1) as usize; + + let edges = self.build_edges(red); + let valid = self.flood_fill_interior(&edges, min_x, min_y, width, height); + + // Ensure red tiles are "valid" + let mut valid = valid; + for &p in red { + valid.set(p.x, p.y); + } + + // Compute max rectangle + let mut best = 0; + for i in 0..red.len() { + for j in (i + 1)..red.len() { + let a = red[i]; + let b = red[j]; + if self.rectangle_ok(a, b, &valid) { + let area = self.rectangle_area(a, b); + if area > best { + best = area; + } + } + } + } + + best + } + + fn rectangle_area(&self, a: Point, b: Point) -> i64 { + let width = ((a.x - b.x).abs() + 1) as i64; + let height = ((a.y - b.y).abs() + 1) as i64; + width * height + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +struct Point { + x: i32, + y: i32, +} + +#[derive(Clone)] +struct BitGrid { + min_x: i32, + min_y: i32, + width: usize, + height: usize, + blocks_per_row: usize, + rows: Vec>, +} + +impl BitGrid { + fn new(min_x: i32, min_y: i32, width: usize, height: usize) -> Self { + let blocks_per_row = width.div_ceil(64); + let mut rows = Vec::with_capacity(height); + for _ in 0..height { + rows.push(vec![0u64; blocks_per_row]); + } + Self { + min_x, + min_y, + width, + height, + blocks_per_row, + rows, + } + } + + #[inline] + fn coord_to_block_bit(&self, x: i32, y: i32) -> Option<(usize, u64)> { + let dx = x - self.min_x; + let dy = y - self.min_y; + if dx < 0 || dy < 0 { + return None; + } + let ux = dx as usize; + let uy = dy as usize; + if ux >= self.width || uy >= self.height { + return None; + } + let block = ux / 64; + let bit = 1u64 << (ux % 64); + Some((uy * self.blocks_per_row + block, bit)) + } + + #[inline] + fn set(&mut self, x: i32, y: i32) { + if let Some((flat_index, bit)) = self.coord_to_block_bit(x, y) { + let row = flat_index / self.blocks_per_row; + let col = flat_index % self.blocks_per_row; + self.rows[row][col] |= bit; + } + } + + #[inline] + fn get(&self, x: i32, y: i32) -> bool { + if let Some((flat_index, bit)) = self.coord_to_block_bit(x, y) { + let row = flat_index / self.blocks_per_row; + let col = flat_index % self.blocks_per_row; + (self.rows[row][col] & bit) != 0 + } else { + false + } + } +} /// Test from puzzle input #[cfg(test)] @@ -44,7 +322,7 @@ mod test { .unwrap() .to_string(); - assert_eq!(answer, "Ready"); + assert_eq!(answer, "50"); } #[test] @@ -61,6 +339,6 @@ mod test { .unwrap() .to_string(); - assert_eq!(answer, "Ready"); + assert_eq!(answer, "24"); } }