Skip to content

Commit bea38a1

Browse files
authored
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.
1 parent b6cc7ab commit bea38a1

File tree

1 file changed

+43
-68
lines changed

1 file changed

+43
-68
lines changed

src/year2025/day09.rs

Lines changed: 43 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,5 @@
1-
use crate::util::grid::*;
21
use crate::util::hash::*;
3-
use crate::util::iter::*;
42
use crate::util::parse::*;
5-
use crate::util::point::*;
6-
use std::collections::VecDeque;
7-
8-
const OUTSIDE: i64 = 0;
9-
const INSIDE: i64 = 1;
10-
const UNKNOWN: i64 = 2;
113

124
type Tile = [u64; 2];
135

@@ -20,9 +12,7 @@ pub fn part1(tiles: &[Tile]) -> u64 {
2012

2113
for (i, &[x1, y1]) in tiles.iter().enumerate() {
2214
for &[x2, y2] in tiles.iter().skip(i + 1) {
23-
let dx = x1.abs_diff(x2) + 1;
24-
let dy = y1.abs_diff(y2) + 1;
25-
area = area.max(dx * dy);
15+
area = area.max(rect_area(x1, y1, x2, y2));
2616
}
2717
}
2818

@@ -31,74 +21,59 @@ pub fn part1(tiles: &[Tile]) -> u64 {
3121

3222
pub fn part2(tiles: &[Tile]) -> u64 {
3323
let size = tiles.len();
34-
let shrink_x = shrink(tiles, 0);
35-
let shrink_y = shrink(tiles, 1);
36-
let shrunk: Vec<_> = tiles.iter().map(|&[x, y]| (shrink_x[&x], shrink_y[&y])).collect();
3724

25+
// Find top K longest edges and collect candidate vertices
26+
let mut edge_lengths: Vec<(u64, usize)> = (0..size)
27+
.map(|i| {
28+
let j = (i + 1) % size;
29+
let dx = tiles[i][0].abs_diff(tiles[j][0]);
30+
let dy = tiles[i][1].abs_diff(tiles[j][1]);
31+
(dx.max(dy), i)
32+
})
33+
.collect();
34+
edge_lengths.sort_unstable_by(|a, b| b.0.cmp(&a.0));
35+
36+
let mut candidates: Vec<usize> = Vec::with_capacity(8);
37+
for &(_, i) in edge_lengths.iter().take(4) {
38+
candidates.push(i);
39+
candidates.push((i + 1) % size);
40+
}
41+
candidates.sort_unstable();
42+
candidates.dedup();
43+
44+
// Build edge AABBs for collision detection
45+
let edges: Vec<_> = (0..size)
46+
.map(|i| {
47+
let j = (i + 1) % size;
48+
let [x1, y1] = tiles[i];
49+
let [x2, y2] = tiles[j];
50+
(x1.min(x2), x1.max(x2), y1.min(y2), y1.max(y2))
51+
})
52+
.collect();
53+
54+
// Check candidates paired with all vertices
3855
let mut area = 0;
39-
let mut todo = VecDeque::from([ORIGIN]);
40-
let mut grid = Grid::new(shrink_x.len() as i32, shrink_y.len() as i32, UNKNOWN);
4156

42-
for i in 0..size {
43-
let (x1, y1, x2, y2) = minmax(shrunk[i], shrunk[(i + 1) % size]);
57+
for &c in &candidates {
58+
let [cx, cy] = tiles[c];
4459

45-
for x in x1..x2 + 1 {
46-
for y in y1..y2 + 1 {
47-
grid[Point::new(x, y)] = INSIDE;
60+
for (i, &[x, y]) in tiles.iter().enumerate() {
61+
if i == c {
62+
continue;
4863
}
49-
}
50-
}
5164

52-
while let Some(point) = todo.pop_front() {
53-
for next in ORTHOGONAL.map(|o| point + o) {
54-
if grid.contains(next) && grid[next] == UNKNOWN {
55-
grid[next] = OUTSIDE;
56-
todo.push_back(next);
57-
}
58-
}
59-
}
65+
let (min_x, max_x) = if cx < x { (cx, x) } else { (x, cx) };
66+
let (min_y, max_y) = if cy < y { (cy, y) } else { (y, cy) };
6067

61-
for y in 1..grid.height {
62-
for x in 1..grid.width {
63-
let point = Point::new(x, y);
64-
let value = i64::from(grid[point] != OUTSIDE);
65-
grid[point] = value + grid[point + UP] + grid[point + LEFT] - grid[point + UP + LEFT];
66-
}
67-
}
68+
let valid = !edges.iter().any(|&(ex1, ex2, ey1, ey2)| {
69+
min_x < ex2 && max_x > ex1 && min_y < ey2 && max_y > ey1
70+
});
6871

69-
for i in 0..size {
70-
for j in i + 1..size {
71-
let (x1, y1, x2, y2) = minmax(shrunk[i], shrunk[j]);
72-
73-
let expected = (x2 - x1 + 1) as i64 * (y2 - y1 + 1) as i64;
74-
let actual = grid[Point::new(x2, y2)]
75-
- grid[Point::new(x1 - 1, y2)]
76-
- grid[Point::new(x2, y1 - 1)]
77-
+ grid[Point::new(x1 - 1, y1 - 1)];
78-
79-
if expected == actual {
80-
let [x1, y1] = tiles[i];
81-
let [x2, y2] = tiles[j];
82-
let dx = x1.abs_diff(x2) + 1;
83-
let dy = y1.abs_diff(y2) + 1;
84-
area = area.max(dx * dy);
72+
if valid {
73+
area = area.max(rect_area(cx, cy, x, y));
8574
}
8675
}
8776
}
8877

8978
area
9079
}
91-
92-
fn shrink(tiles: &[Tile], index: usize) -> FastMap<u64, i32> {
93-
let mut axis: Vec<_> = tiles.iter().map(|tile| tile[index]).collect();
94-
axis.push(u64::MIN);
95-
axis.push(u64::MAX);
96-
axis.sort_unstable();
97-
axis.dedup();
98-
axis.iter().enumerate().map(|(i, &n)| (n, i as i32)).collect()
99-
}
100-
101-
#[inline]
102-
fn minmax((x1, y1): (i32, i32), (x2, y2): (i32, i32)) -> (i32, i32, i32, i32) {
103-
(x1.min(x2), y1.min(y2), x1.max(x2), y1.max(y2))
104-
}

0 commit comments

Comments
 (0)