Skip to content

Commit 0534c82

Browse files
committed
2024: Add solution for Day 12
1 parent eb79323 commit 0534c82

File tree

12 files changed

+381
-34
lines changed

12 files changed

+381
-34
lines changed

2024/Cargo.lock

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

2024/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ cargo run --bin day01
2121
9. [Disk Fragmenter](day09) 🌟🌟
2222
10. [Hoof It](day10) 🌟🌟
2323
11. [Plutonian Pebbles](day11) 🌟🌟
24-
12. [](day12)
24+
12. [Garden Groups](day12) 🌟🌟
2525
13. [](day13)
2626
14. [](day14)
2727
15. [](day15)

2024/day12/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ edition = "2021"
66
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
77

88
[dependencies]
9+
lib = { path = "../lib" }

2024/day12/example1.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
AAAA
2+
BBCD
3+
BBCC
4+
EEEC

2024/day12/example2.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
RRRRIICCFF
2+
RRRRIICCCF
3+
VVRRRCCFFF
4+
VVRCCCJFFF
5+
VVVVCJJCFE
6+
VVIVCCJJEE
7+
VVIIICJJEE
8+
MIIIIIJJEE
9+
MIIISIJEEE
10+
MMMISSJEEE

2024/day12/example3.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
OOOOO
2+
OXOXO
3+
OOOOO
4+
OXOXO
5+
OOOOO

2024/day12/example4.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
EEEEE
2+
EXXXX
3+
EEEEE
4+
EXXXX
5+
EEEEE

2024/day12/example5.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
AAAAAA
2+
AAABBA
3+
AAABBA
4+
ABBAAA
5+
ABBAAA
6+
AAAAAA

2024/day12/input.txt

Lines changed: 140 additions & 0 deletions
Large diffs are not rendered by default.

2024/day12/src/main.rs

Lines changed: 189 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@
22
//! https://adventofcode.com/2024/day/12
33
44
use std::{fs, io};
5+
use std::collections::{BTreeMap, BTreeSet};
6+
use std::ops::Range;
57
use std::path::Path;
8+
use lib::vector::Vector;
69

710
fn main() {
811
let input = Input::from_file(format!("{}/input.txt", env!("CARGO_MANIFEST_DIR"))).expect("failed to read input");
9-
//let input = Input::from_file(format!("{}/example1.txt", env!("CARGO_MANIFEST_DIR"))).expect("failed to read input");
10-
println!("{input:?}");
12+
//let input = Input::from_file(format!("{}/example2.txt", env!("CARGO_MANIFEST_DIR"))).expect("failed to read input");
13+
//println!("{input:?}");
1114

1215
// Part 1
1316
println!("Part 1: {}", part1(&input));
@@ -16,25 +19,162 @@ fn main() {
1619
println!("Part 2: {}", part2(&input));
1720
}
1821

22+
const UP: Vec2 = Vec2::new([0, -1]);
23+
const DOWN: Vec2 = Vec2::new([0, 1]);
24+
const LEFT: Vec2 = Vec2::new([-1, 0]);
25+
const RIGHT: Vec2 = Vec2::new([1, 0]);
26+
1927
fn part1(input: &Input) -> usize {
20-
0
28+
let mut regions = Vec::new();
29+
30+
let mut visitied = BTreeSet::new();
31+
for y in input.bounds.1.clone() {
32+
for x in input.bounds.0.clone() {
33+
let pos = Vec2::new([x, y]);
34+
if visitied.contains(&pos) {
35+
continue;
36+
}
37+
38+
let plant = input.map[&pos];
39+
let mut perimenter = 0;
40+
let mut area = 0;
41+
42+
let mut edge = vec![pos];
43+
while let Some(pos) = edge.pop() {
44+
if visitied.contains(&pos) {
45+
continue;
46+
}
47+
48+
visitied.insert(pos);
49+
50+
let plant = input.map[&pos];
51+
area += 1;
52+
53+
for adj in [UP, DOWN, LEFT, RIGHT].into_iter().map(|d| pos + d) {
54+
if input.map.get(&adj) != Some(&plant) {
55+
perimenter += 1;
56+
}
57+
58+
if !input.map.contains_key(&adj) {
59+
continue;
60+
}
61+
62+
if visitied.contains(&adj) {
63+
continue;
64+
}
65+
66+
if input.map[&adj] == plant {
67+
edge.push(adj);
68+
}
69+
}
70+
}
71+
72+
regions.push((plant, area, perimenter));
73+
}
74+
}
75+
76+
regions.into_iter()
77+
.map(|(_, a, p)| a * p)
78+
.sum()
2179
}
2280

2381
fn part2(input: &Input) -> usize {
24-
0
82+
let mut regions = Vec::new();
83+
84+
let mut visitied = BTreeSet::new();
85+
for y in input.bounds.1.clone() {
86+
for x in input.bounds.0.clone() {
87+
let pos = Vec2::new([x, y]);
88+
if visitied.contains(&pos) {
89+
continue;
90+
}
91+
92+
let plant = input.map[&pos];
93+
let mut area = 0;
94+
let mut edges = 0;
95+
96+
let mut edge = vec![pos];
97+
while let Some(pos) = edge.pop() {
98+
if visitied.contains(&pos) {
99+
continue;
100+
}
101+
102+
visitied.insert(pos);
103+
104+
let plant = input.map[&pos];
105+
area += 1;
106+
107+
for adj in [UP, DOWN, LEFT, RIGHT].into_iter().map(|d| pos + d) {
108+
if !input.map.contains_key(&adj) {
109+
continue;
110+
}
111+
112+
if visitied.contains(&adj) {
113+
continue;
114+
}
115+
116+
if input.map[&adj] == plant {
117+
edge.push(adj);
118+
}
119+
}
120+
121+
// Look for corners
122+
for d1 in [UP, DOWN] {
123+
for d2 in [LEFT, RIGHT] {
124+
let c1 = input.map.get(&(pos + d1)) == Some(&plant);
125+
let c2 = input.map.get(&(pos + d2)) == Some(&plant);
126+
let c3 = input.map.get(&(pos + d1 + d2)) == Some(&plant);
127+
128+
match (c1, c2, c3) {
129+
// A A A X
130+
// A X or X ?
131+
(true, true, false) | (false, false, _) => {
132+
edges += 1;
133+
},
134+
_ => (),
135+
}
136+
}
137+
}
138+
}
139+
140+
regions.push((plant, area, edges));
141+
}
142+
}
143+
144+
regions.into_iter()
145+
.map(|(_, a, p)| a * p)
146+
.sum()
25147
}
26148

149+
type Vec2 = Vector<i32, 2>;
150+
27151
#[derive(Debug, Clone)]
28152
struct Input {
29-
values: Vec<String>,
153+
bounds: (Range<i32>, Range<i32>),
154+
map: BTreeMap<Vec2, char>,
30155
}
31156

32157
impl Input {
33158
fn from_file(path: impl AsRef<Path>) -> io::Result<Self> {
34159
let input = fs::read_to_string(path)?;
35-
let values = input.lines().map(str::to_string).collect();
36160

37-
Ok(Self { values })
161+
let mut map = BTreeMap::new();
162+
let mut width = 0;
163+
let mut height = 0;
164+
165+
for (y, line) in input.lines().enumerate() {
166+
for (x, c) in line.trim().chars().enumerate() {
167+
let pos = Vec2::new([x as i32, y as i32]);
168+
169+
map.insert(pos, c);
170+
width = width.max(x as i32 + 1);
171+
}
172+
height = height.max(y as i32 + 1);
173+
}
174+
175+
let bounds = (0..width, 0..height);
176+
177+
Ok(Self { bounds, map })
38178
}
39179
}
40180

@@ -43,30 +183,66 @@ mod test {
43183
use super::*;
44184

45185
#[test]
46-
fn test_part1() {
186+
fn test_part1_example1() {
47187
let input = Input::from_file("example1.txt").unwrap();
48188

49-
assert_eq!(part1(&input), 0);
189+
assert_eq!(part1(&input), 140);
190+
}
191+
192+
#[test]
193+
fn test_part1_example2() {
194+
let input = Input::from_file("example2.txt").unwrap();
195+
196+
assert_eq!(part1(&input), 1930);
50197
}
51198

52199
#[test]
53200
fn test_part1_solution() {
54201
let input = Input::from_file("input.txt").unwrap();
55202

56-
assert_eq!(part1(&input), 0);
203+
assert_eq!(part1(&input), 1371306);
57204
}
58205

59206
#[test]
60-
fn test_part2() {
207+
fn test_part2_example1() {
61208
let input = Input::from_file("example1.txt").unwrap();
62209

63-
assert_eq!(part2(&input), 0);
210+
assert_eq!(part2(&input), 80);
211+
}
212+
213+
#[test]
214+
fn test_part2_example2() {
215+
let input = Input::from_file("example2.txt").unwrap();
216+
217+
assert_eq!(part2(&input), 1206);
218+
}
219+
220+
#[test]
221+
fn test_part2_example3() {
222+
let input = Input::from_file("example3.txt").unwrap();
223+
224+
assert_eq!(part2(&input), 436);
225+
}
226+
227+
#[test]
228+
fn test_part2_example4() {
229+
let input = Input::from_file("example4.txt").unwrap();
230+
231+
assert_eq!(part2(&input), 236);
232+
}
233+
234+
#[test]
235+
fn test_part2_example5() {
236+
let input = Input::from_file("example5.txt").unwrap();
237+
238+
assert_eq!(part2(&input), 368);
64239
}
65240

66241
#[test]
67242
fn test_part2_solution() {
68243
let input = Input::from_file("input.txt").unwrap();
69244

70-
assert_eq!(part2(&input), 0);
245+
// Not 798452
246+
assert_eq!(part2(&input), 805880);
71247
}
72248
}

0 commit comments

Comments
 (0)