From bea38a1d6c13bd2639e3ae8aed1178d20193219c Mon Sep 17 00:00:00 2001 From: D0ubleD0uble Date: Tue, 9 Dec 2025 23:40:24 -0600 Subject: [PATCH 1/2] Update day09.rs Strategy for length heuristic combined with AABB collision detection. This heuristic specializes to the AoC input generation and doesn't generalize to any input shape generation. One of the points is always on a massive horizontal distance compared to adjacent points. If the input were rotated 90 degrees, this solution would stop working. this means we don't need to compare every vertex to every other vertex, we can compare a handful to the others. This idea can probably be taken further yet. --- src/year2025/day09.rs | 111 ++++++++++++++++-------------------------- 1 file changed, 43 insertions(+), 68 deletions(-) diff --git a/src/year2025/day09.rs b/src/year2025/day09.rs index 48ce975..0dd308d 100644 --- a/src/year2025/day09.rs +++ b/src/year2025/day09.rs @@ -1,13 +1,5 @@ -use crate::util::grid::*; use crate::util::hash::*; -use crate::util::iter::*; use crate::util::parse::*; -use crate::util::point::*; -use std::collections::VecDeque; - -const OUTSIDE: i64 = 0; -const INSIDE: i64 = 1; -const UNKNOWN: i64 = 2; type Tile = [u64; 2]; @@ -20,9 +12,7 @@ pub fn part1(tiles: &[Tile]) -> u64 { for (i, &[x1, y1]) in tiles.iter().enumerate() { for &[x2, y2] in tiles.iter().skip(i + 1) { - let dx = x1.abs_diff(x2) + 1; - let dy = y1.abs_diff(y2) + 1; - area = area.max(dx * dy); + area = area.max(rect_area(x1, y1, x2, y2)); } } @@ -31,74 +21,59 @@ pub fn part1(tiles: &[Tile]) -> u64 { pub fn part2(tiles: &[Tile]) -> u64 { let size = tiles.len(); - let shrink_x = shrink(tiles, 0); - let shrink_y = shrink(tiles, 1); - let shrunk: Vec<_> = tiles.iter().map(|&[x, y]| (shrink_x[&x], shrink_y[&y])).collect(); + // Find top K longest edges and collect candidate vertices + let mut edge_lengths: Vec<(u64, usize)> = (0..size) + .map(|i| { + let j = (i + 1) % size; + let dx = tiles[i][0].abs_diff(tiles[j][0]); + let dy = tiles[i][1].abs_diff(tiles[j][1]); + (dx.max(dy), i) + }) + .collect(); + edge_lengths.sort_unstable_by(|a, b| b.0.cmp(&a.0)); + + let mut candidates: Vec = Vec::with_capacity(8); + for &(_, i) in edge_lengths.iter().take(4) { + candidates.push(i); + candidates.push((i + 1) % size); + } + candidates.sort_unstable(); + candidates.dedup(); + + // Build edge AABBs for collision detection + let edges: Vec<_> = (0..size) + .map(|i| { + let j = (i + 1) % size; + let [x1, y1] = tiles[i]; + let [x2, y2] = tiles[j]; + (x1.min(x2), x1.max(x2), y1.min(y2), y1.max(y2)) + }) + .collect(); + + // Check candidates paired with all vertices let mut area = 0; - let mut todo = VecDeque::from([ORIGIN]); - let mut grid = Grid::new(shrink_x.len() as i32, shrink_y.len() as i32, UNKNOWN); - for i in 0..size { - let (x1, y1, x2, y2) = minmax(shrunk[i], shrunk[(i + 1) % size]); + for &c in &candidates { + let [cx, cy] = tiles[c]; - for x in x1..x2 + 1 { - for y in y1..y2 + 1 { - grid[Point::new(x, y)] = INSIDE; + for (i, &[x, y]) in tiles.iter().enumerate() { + if i == c { + continue; } - } - } - while let Some(point) = todo.pop_front() { - for next in ORTHOGONAL.map(|o| point + o) { - if grid.contains(next) && grid[next] == UNKNOWN { - grid[next] = OUTSIDE; - todo.push_back(next); - } - } - } + let (min_x, max_x) = if cx < x { (cx, x) } else { (x, cx) }; + let (min_y, max_y) = if cy < y { (cy, y) } else { (y, cy) }; - for y in 1..grid.height { - for x in 1..grid.width { - let point = Point::new(x, y); - let value = i64::from(grid[point] != OUTSIDE); - grid[point] = value + grid[point + UP] + grid[point + LEFT] - grid[point + UP + LEFT]; - } - } + let valid = !edges.iter().any(|&(ex1, ex2, ey1, ey2)| { + min_x < ex2 && max_x > ex1 && min_y < ey2 && max_y > ey1 + }); - for i in 0..size { - for j in i + 1..size { - let (x1, y1, x2, y2) = minmax(shrunk[i], shrunk[j]); - - let expected = (x2 - x1 + 1) as i64 * (y2 - y1 + 1) as i64; - let actual = grid[Point::new(x2, y2)] - - grid[Point::new(x1 - 1, y2)] - - grid[Point::new(x2, y1 - 1)] - + grid[Point::new(x1 - 1, y1 - 1)]; - - if expected == actual { - let [x1, y1] = tiles[i]; - let [x2, y2] = tiles[j]; - let dx = x1.abs_diff(x2) + 1; - let dy = y1.abs_diff(y2) + 1; - area = area.max(dx * dy); + if valid { + area = area.max(rect_area(cx, cy, x, y)); } } } area } - -fn shrink(tiles: &[Tile], index: usize) -> FastMap { - let mut axis: Vec<_> = tiles.iter().map(|tile| tile[index]).collect(); - axis.push(u64::MIN); - axis.push(u64::MAX); - axis.sort_unstable(); - axis.dedup(); - axis.iter().enumerate().map(|(i, &n)| (n, i as i32)).collect() -} - -#[inline] -fn minmax((x1, y1): (i32, i32), (x2, y2): (i32, i32)) -> (i32, i32, i32, i32) { - (x1.min(x2), y1.min(y2), x1.max(x2), y1.max(y2)) -} From e46701c7a73c4df743f8e1a9fd3e24c7e4e270ce Mon Sep 17 00:00:00 2001 From: D0ubleD0uble Date: Wed, 10 Dec 2025 08:41:33 -0600 Subject: [PATCH 2/2] Update day09.rs crate unused and included rect_area fn --- src/year2025/day09.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/year2025/day09.rs b/src/year2025/day09.rs index 0dd308d..237a1a4 100644 --- a/src/year2025/day09.rs +++ b/src/year2025/day09.rs @@ -1,4 +1,3 @@ -use crate::util::hash::*; use crate::util::parse::*; type Tile = [u64; 2]; @@ -18,6 +17,10 @@ pub fn part1(tiles: &[Tile]) -> u64 { area } + #[inline] + fn rect_area(x1: u64, y1: u64, x2: u64, y2: u64) -> u64 { + (x1.abs_diff(x2) + 1) * (y1.abs_diff(y2) + 1) + } pub fn part2(tiles: &[Tile]) -> u64 { let size = tiles.len();