Skip to content

Commit 7634261

Browse files
committed
Year 2024 Day 4
1 parent f8a959b commit 7634261

File tree

7 files changed

+108
-1
lines changed

7 files changed

+108
-1
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ Performance is reasonable even on older hardware, for example a 2011 MacBook Pro
8080
| 1 | [Historian Hysteria](https://adventofcode.com/2024/day/1) | [Source](src/year2024/day01.rs) | 21 |
8181
| 2 | [Red-Nosed Reports](https://adventofcode.com/2024/day/2) | [Source](src/year2024/day02.rs) | 43 |
8282
| 3 | [Mull It Over](https://adventofcode.com/2024/day/3) | [Source](src/year2024/day03.rs) | 8 |
83+
| 4 | [Ceres Search](https://adventofcode.com/2024/day/4) | [Source](src/year2024/day04.rs) | 77 |
8384

8485
## 2023
8586

benches/benchmark.rs

+1
Original file line numberDiff line numberDiff line change
@@ -295,4 +295,5 @@ mod year2024 {
295295
benchmark!(year2024, day01);
296296
benchmark!(year2024, day02);
297297
benchmark!(year2024, day03);
298+
benchmark!(year2024, day04);
298299
}

src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -294,4 +294,5 @@ pub mod year2024 {
294294
pub mod day01;
295295
pub mod day02;
296296
pub mod day03;
297+
pub mod day04;
297298
}

src/main.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -360,5 +360,10 @@ fn year2023() -> Vec<Solution> {
360360
}
361361

362362
fn year2024() -> Vec<Solution> {
363-
vec![solution!(year2024, day01), solution!(year2024, day02), solution!(year2024, day03)]
363+
vec![
364+
solution!(year2024, day01),
365+
solution!(year2024, day02),
366+
solution!(year2024, day03),
367+
solution!(year2024, day04),
368+
]
364369
}

src/year2024/day04.rs

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
//! # Ceres Search
2+
//!
3+
//! For part one we search each vertical, horizontal and diagonal line individually.
4+
//! By using a `u32` bitmask of ASCII values we can check in both directions efficiently at the
5+
//! same time.
6+
//!
7+
//! For part two the difference of the ASCII values of "M" and "S" is 6. No other combination of
8+
//! letter has this value, so if both diagonals are a 6 then we have a match.
9+
use crate::util::grid::*;
10+
use crate::util::point::*;
11+
12+
pub fn parse(input: &str) -> Grid<u8> {
13+
Grid::parse(input)
14+
}
15+
16+
pub fn part1(grid: &Grid<u8>) -> u32 {
17+
let size = grid.width;
18+
let mut result = 0;
19+
20+
// Horizontal and vertical
21+
for i in 0..size {
22+
result += scan_line(grid, Point::new(i, 0), DOWN, size);
23+
result += scan_line(grid, Point::new(0, i), RIGHT, size);
24+
}
25+
26+
// Diagonals
27+
for i in 0..size - 3 {
28+
result += scan_line(grid, Point::new(i, 0), DOWN + RIGHT, size - i);
29+
result += scan_line(grid, Point::new(0, i + 1), DOWN + RIGHT, size - 1 - i);
30+
result += scan_line(grid, Point::new(size - 1 - i, 0), DOWN + LEFT, size - i);
31+
result += scan_line(grid, Point::new(size - 1, i + 1), DOWN + LEFT, size - 1 - i);
32+
}
33+
34+
result
35+
}
36+
37+
pub fn part2(grid: &Grid<u8>) -> u32 {
38+
let mut result = 0;
39+
40+
for x in 1..grid.width - 1 {
41+
for y in 1..grid.height - 1 {
42+
let point = Point::new(x, y);
43+
44+
if grid[point] == b'A' {
45+
let ul = grid[Point::new(x - 1, y - 1)];
46+
let ur = grid[Point::new(x + 1, y - 1)];
47+
let dl = grid[Point::new(x - 1, y + 1)];
48+
let dr = grid[Point::new(x + 1, y + 1)];
49+
// ASCII "M" is 77 and "S" is 53 so the absolute difference is 6.
50+
// No other combination of letters causes this difference.
51+
// "MS" on both diagonals is a match.
52+
result += (ul.abs_diff(dr) == 6 && ur.abs_diff(dl) == 6) as u32;
53+
}
54+
}
55+
}
56+
57+
result
58+
}
59+
60+
/// Searches a horizontal, vertical or diagonal line in both directions at once.
61+
fn scan_line(grid: &Grid<u8>, mut point: Point, direction: Point, size: i32) -> u32 {
62+
let mut bytes = 0;
63+
let mut result = 0;
64+
65+
for _ in 0..size {
66+
bytes = (bytes << 8) | (grid[point] as u32);
67+
point += direction;
68+
// "XMAS" and "SAMX" in hex.
69+
result += (bytes == 0x584d4153 || bytes == 0x53414d58) as u32;
70+
}
71+
72+
result
73+
}

tests/test.rs

+1
Original file line numberDiff line numberDiff line change
@@ -284,4 +284,5 @@ mod year2024 {
284284
mod day01_test;
285285
mod day02_test;
286286
mod day03_test;
287+
mod day04_test;
287288
}

tests/year2024/day04_test.rs

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
use aoc::year2024::day04::*;
2+
3+
const EXAMPLE: &str = "\
4+
MMMSXXMASM
5+
MSAMXMSMSA
6+
AMXSXMAAMM
7+
MSAMASMSMX
8+
XMASAMXAMM
9+
XXAMMXXAMA
10+
SMSMSASXSS
11+
SAXAMASAAA
12+
MAMMMXMMMM
13+
MXMXAXMASX";
14+
15+
#[test]
16+
fn part1_test() {
17+
let input = parse(EXAMPLE);
18+
assert_eq!(part1(&input), 18);
19+
}
20+
21+
#[test]
22+
fn part2_test() {
23+
let input = parse(EXAMPLE);
24+
assert_eq!(part2(&input), 9);
25+
}

0 commit comments

Comments
 (0)