Skip to content

Commit 6eaba1c

Browse files
committed
Year 2024 Day 12
1 parent fce358d commit 6eaba1c

File tree

7 files changed

+160
-0
lines changed

7 files changed

+160
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ Performance is reasonable even on older hardware, for example a 2011 MacBook Pro
8383
| 9 | [Disk Fragmenter](https://adventofcode.com/2024/day/9) | [Source](src/year2024/day09.rs) | 106 |
8484
| 10 | [Hoof It](https://adventofcode.com/2024/day/10) | [Source](src/year2024/day10.rs) | 38 |
8585
| 11 | [Plutonian Pebbles](https://adventofcode.com/2024/day/11) | [Source](src/year2024/day11.rs) | 248 |
86+
| 12 | [Garden Groups](https://adventofcode.com/2024/day/12) | [Source](src/year2024/day12.rs) | 1582 |
8687

8788
## 2023
8889

benches/benchmark.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,4 +303,5 @@ mod year2024 {
303303
benchmark!(year2024, day09);
304304
benchmark!(year2024, day10);
305305
benchmark!(year2024, day11);
306+
benchmark!(year2024, day12);
306307
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,4 +302,5 @@ pub mod year2024 {
302302
pub mod day09;
303303
pub mod day10;
304304
pub mod day11;
305+
pub mod day12;
305306
}

src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,5 +372,6 @@ fn year2024() -> Vec<Solution> {
372372
solution!(year2024, day09),
373373
solution!(year2024, day10),
374374
solution!(year2024, day11),
375+
solution!(year2024, day12),
375376
]
376377
}

src/year2024/day12.rs

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
//! # Garden Groups
2+
use crate::util::grid::*;
3+
use crate::util::hash::*;
4+
use crate::util::point::*;
5+
use std::collections::VecDeque;
6+
7+
const CLOCKWISE: [Point; 5] = [UP, RIGHT, DOWN, LEFT, UP];
8+
9+
pub fn parse(input: &str) -> Grid<u8> {
10+
Grid::parse(input)
11+
}
12+
13+
pub fn part1(grid: &Grid<u8>) -> i32 {
14+
let mut todo = VecDeque::new();
15+
let mut seen = grid.same_size_with(false);
16+
let mut added = grid.same_size_with(false);
17+
let mut result = 0;
18+
19+
for y in 0..grid.height {
20+
for x in 0..grid.width {
21+
let point = Point::new(x, y);
22+
if seen[point] {
23+
continue;
24+
}
25+
26+
let kind = grid[point];
27+
let mut area = 0;
28+
let mut perm = 0;
29+
30+
todo.push_back(point);
31+
seen[point] = true;
32+
33+
while let Some(point) = todo.pop_front() {
34+
area += 1;
35+
perm += 4;
36+
added[point] = true;
37+
38+
for next in ORTHOGONAL.map(|o| point + o) {
39+
if grid.contains(next) && grid[next] == kind {
40+
if !seen[next] {
41+
seen[next] = true;
42+
todo.push_back(next);
43+
}
44+
if added[next] {
45+
perm -= 2;
46+
}
47+
}
48+
}
49+
}
50+
51+
result += area * perm;
52+
}
53+
}
54+
55+
result
56+
}
57+
58+
pub fn part2(grid: &Grid<u8>) -> u32 {
59+
let mut seen = grid.same_size_with(false);
60+
let mut todo = VecDeque::new();
61+
let mut corner = FastMap::new();
62+
let mut middle = FastMap::new();
63+
let mut result = 0;
64+
65+
for y in 0..grid.height {
66+
for x in 0..grid.width {
67+
let point = Point::new(x, y);
68+
if seen[point] {
69+
continue;
70+
}
71+
72+
let kind = grid[point];
73+
let mut size = 0;
74+
let mut sides = 0;
75+
76+
todo.push_back(point);
77+
seen[point] = true;
78+
79+
while let Some(point) = todo.pop_front() {
80+
size += 1;
81+
let x = 2 * point.x;
82+
let y = 2 * point.y;
83+
84+
*corner.entry(Point::new(x, y)).or_insert(0) += 1;
85+
*corner.entry(Point::new(x + 2, y)).or_insert(0) += 1;
86+
*corner.entry(Point::new(x, y + 2)).or_insert(0) += 1;
87+
*corner.entry(Point::new(x + 2, y + 2)).or_insert(0) += 1;
88+
89+
*middle.entry(Point::new(x + 1, y)).or_insert(0) += 1;
90+
*middle.entry(Point::new(x, y + 1)).or_insert(0) += 1;
91+
*middle.entry(Point::new(x + 2, y + 1)).or_insert(0) += 1;
92+
*middle.entry(Point::new(x + 1, y + 2)).or_insert(0) += 1;
93+
94+
for next in ORTHOGONAL.map(|o| point + o) {
95+
if grid.contains(next) && grid[next] == kind && !seen[next] {
96+
seen[next] = true;
97+
todo.push_back(next);
98+
}
99+
}
100+
}
101+
102+
for (&point, _) in corner.iter().filter(|(_, &v)| v < 4) {
103+
let freq = CLOCKWISE.map(|c| *middle.get(&(point + c)).unwrap_or(&2));
104+
let count = freq.windows(2).filter(|w| w[0] < 2 && w[1] < 2).count();
105+
106+
if count == 1 {
107+
sides += 1;
108+
} else if count == 4 {
109+
sides += 2;
110+
}
111+
}
112+
113+
corner.clear();
114+
middle.clear();
115+
result += size * sides;
116+
}
117+
}
118+
119+
result
120+
}

tests/test.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,4 +292,5 @@ mod year2024 {
292292
mod day09_test;
293293
mod day10_test;
294294
mod day11_test;
295+
mod day12_test;
295296
}

tests/year2024/day12_test.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
use aoc::year2024::day12::*;
2+
3+
const FIRST_EXAMPLE: &str = "\
4+
RRRRIICCFF
5+
RRRRIICCCF
6+
VVRRRCCFFF
7+
VVRCCCJFFF
8+
VVVVCJJCFE
9+
VVIVCCJJEE
10+
VVIIICJJEE
11+
MIIIIIJJEE
12+
MIIISIJEEE
13+
MMMISSJEEE";
14+
15+
const SECOND_EXAMPLE: &str = "\
16+
AAAAAA
17+
AAABBA
18+
AAABBA
19+
ABBAAA
20+
ABBAAA
21+
AAAAAA";
22+
23+
#[test]
24+
fn part1_test() {
25+
let input = parse(FIRST_EXAMPLE);
26+
assert_eq!(part1(&input), 1930);
27+
}
28+
29+
#[test]
30+
fn part2_test() {
31+
let input = parse(FIRST_EXAMPLE);
32+
assert_eq!(part2(&input), 1206);
33+
let input = parse(SECOND_EXAMPLE);
34+
assert_eq!(part2(&input), 368);
35+
}

0 commit comments

Comments
 (0)