Skip to content

Commit 8207df6

Browse files
committed
Faster approach seaching for the bounding box
1 parent e808d24 commit 8207df6

File tree

2 files changed

+66
-42
lines changed

2 files changed

+66
-42
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ Performance is reasonable even on older hardware, for example a 2011 MacBook Pro
8585
| 11 | [Plutonian Pebbles](https://adventofcode.com/2024/day/11) | [Source](src/year2024/day11.rs) | 248 |
8686
| 12 | [Garden Groups](https://adventofcode.com/2024/day/12) | [Source](src/year2024/day12.rs) | 289 |
8787
| 13 | [Claw Contraption](https://adventofcode.com/2024/day/13) | [Source](src/year2024/day13.rs) | 14 |
88-
| 14 | [Restroom Redoubt](https://adventofcode.com/2024/day/14) | [Source](src/year2024/day14.rs) | 456 |
88+
| 14 | [Restroom Redoubt](https://adventofcode.com/2024/day/14) | [Source](src/year2024/day14.rs) | 104 |
8989

9090
## 2023
9191

src/year2024/day14.rs

Lines changed: 65 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,18 @@
44
//! The image appears in part two when the positions of all robots are unique.
55
//!
66
//! The x coordinates repeat every 101 seconds and the y coordinates repeat every 103 seconds.
7-
//! Calculating each axis independently then looking it up is twice as fast
8-
//! as calculating as needed.
7+
//! First we check for times when the robot x coordinates could form the left and right columns
8+
//! of the tree's bounding box. This gives a time `t` mod 101.
9+
//!
10+
//! Then we check the y coordinates looking for the top and bottom rows of the bounding box,
11+
//! giving a time `u` mod 103.
12+
//!
13+
//! Using the [Chinese Remainder Theorem](https://en.wikipedia.org/wiki/Chinese_remainder_theorem)
14+
//! we combine the two times into a single time mod 10403 that is the answer.
15+
use crate::util::grid::*;
916
use crate::util::iter::*;
10-
use crate::util::math::*;
1117
use crate::util::parse::*;
18+
use crate::util::point::*;
1219

1320
type Robot = [i32; 4];
1421

@@ -47,52 +54,69 @@ pub fn part1(input: &[Robot]) -> i32 {
4754
q1 * q2 * q3 * q4
4855
}
4956

50-
pub fn part2(input: &[Robot]) -> usize {
51-
let robots: Vec<_> = input
52-
.iter()
53-
.map(|&[x, y, h, v]| [x, y, h.rem_euclid(101), v.rem_euclid(103)])
54-
.collect();
57+
pub fn part2(robots: &[Robot]) -> i32 {
58+
// Search for times mod 101 when the tree could possibly exist using x coordinates only.
59+
let mut columns = Vec::new();
5560

56-
let coefficient1 = 103 * 103.mod_inv(101).unwrap();
57-
let coefficient2 = 101 * 101.mod_inv(103).unwrap();
58-
let horizontal: Vec<_> = (0..101).map(|n| n.mod_inv(101)).collect();
59-
let vertical: Vec<_> = (0..103).map(|n| n.mod_inv(103)).collect();
61+
for time in 0..101 {
62+
let mut xs = [0; 101];
6063

61-
let mut unique = vec![true; 10403];
64+
for [x, _, dx, _] in robots {
65+
let index = (x + dx * time).rem_euclid(101);
66+
xs[index as usize] += 1;
67+
}
6268

63-
for (i, &[x1, y1, h1, v1]) in robots.iter().enumerate().skip(1) {
64-
for &[x2, y2, h2, v2] in robots.iter().take(i) {
65-
if x1 == x2 && h1 == h2 {
66-
if let Some(b) = vertical[to_index(v2 - v1, 103)] {
67-
let u = to_index((y1 - y2) * b, 103);
69+
// Tree bounding box columns are 33 high.
70+
if xs.iter().filter(|&&c| c >= 33).count() >= 2 {
71+
columns.push(time);
72+
}
73+
}
6874

69-
for n in (0..10403).step_by(103) {
70-
unique[n + u] = false;
71-
}
72-
}
73-
} else if y1 == y2 && v1 == v2 {
74-
if let Some(a) = horizontal[to_index(h2 - h1, 101)] {
75-
let t = to_index((x1 - x2) * a, 101);
75+
// Search for times mod 103 when the tree could possibly exist using y coordinates only.
76+
let mut rows = Vec::new();
7677

77-
for n in (0..10403).step_by(101) {
78-
unique[n + t] = false;
79-
}
80-
}
81-
} else if let Some(a) = horizontal[to_index(h2 - h1, 101)] {
82-
if let Some(b) = vertical[to_index(v2 - v1, 103)] {
83-
let t = (x1 - x2) * a;
84-
let u = (y1 - y2) * b;
85-
let crt = to_index(t * coefficient1 + u * coefficient2, 10403);
86-
unique[crt] = false;
78+
for time in 0..103 {
79+
let mut ys = [0; 103];
80+
81+
for [_, y, _, dy] in robots {
82+
let index = (y + dy * time).rem_euclid(103);
83+
ys[index as usize] += 1;
84+
}
85+
86+
if ys.iter().filter(|&&c| c >= 31).count() >= 2 {
87+
rows.push(time);
88+
}
89+
}
90+
91+
// If there's only one combination then return answer.
92+
if rows.len() == 1 && columns.len() == 1 {
93+
let t = columns[0];
94+
let u = rows[0];
95+
// Combine indices using the Chinese Remainder Theorem to get index mod 10403.
96+
return (5253 * t + 5151 * u) % 10403;
97+
}
98+
99+
// Backup check for time when all robot positions are unique.
100+
let mut grid = Grid::new(101, 103, 0);
101+
102+
for &t in &columns {
103+
'outer: for &u in &rows {
104+
let time = (5253 * t + 5151 * u) % 10403;
105+
106+
for &[x, y, dx, dy] in robots {
107+
let x = (x + dx * time).rem_euclid(101);
108+
let y = (y + dy * time).rem_euclid(103);
109+
110+
let point = Point::new(x, y);
111+
if grid[point] == time {
112+
continue 'outer;
87113
}
114+
grid[point] = time;
88115
}
116+
117+
return time;
89118
}
90119
}
91120

92-
unique.iter().position(|&u| u).unwrap()
93-
}
94-
95-
#[inline]
96-
fn to_index(a: i32, m: i32) -> usize {
97-
a.rem_euclid(m) as usize
121+
unreachable!()
98122
}

0 commit comments

Comments
 (0)