Skip to content

Commit 889b0d2

Browse files
authored
drakeerv, day 12 (#76)
1 parent 84fd379 commit 889b0d2

File tree

3 files changed

+275
-0
lines changed

3 files changed

+275
-0
lines changed

2024/12/drakeerv.v

Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
import datatypes { Set }
2+
import os
3+
4+
struct Position {
5+
mut:
6+
x int
7+
y int
8+
}
9+
10+
fn (pos Position) encode() string {
11+
return '${pos.x},${pos.y}'
12+
}
13+
14+
fn Position.decode(encoded string) Position {
15+
parts := encoded.split(',')
16+
return Position{
17+
x: parts[0].int()
18+
y: parts[1].int()
19+
}
20+
}
21+
22+
enum Side {
23+
top
24+
right
25+
bottom
26+
left
27+
}
28+
29+
fn (side Side) encode() string {
30+
return match side {
31+
.top { 'top' }
32+
.right { 'right' }
33+
.bottom { 'bottom' }
34+
.left { 'left' }
35+
}
36+
}
37+
38+
fn Side.decode(encoded string) Side {
39+
return match encoded {
40+
'top' { .top }
41+
'right' { .right }
42+
'bottom' { .bottom }
43+
'left' { .left }
44+
else { panic('Invalid side') }
45+
}
46+
}
47+
48+
struct Face {
49+
position Position
50+
side Side
51+
}
52+
53+
fn (face Face) encode() string {
54+
return '${face.position.encode()}-${face.side.encode()}'
55+
}
56+
57+
fn Face.decode(encoded string) Face {
58+
parts := encoded.split('-')
59+
return Face{
60+
position: Position.decode(parts[0])
61+
side: Side.decode(parts[1])
62+
}
63+
}
64+
65+
struct Plot {
66+
mut:
67+
positions Set[string]
68+
faces Set[string]
69+
char rune
70+
}
71+
72+
fn (mut plot Plot) area() int {
73+
return plot.positions.size()
74+
}
75+
76+
fn (mut plot Plot) perimeter() int {
77+
return plot.faces.size()
78+
}
79+
80+
struct Direction {
81+
pos Position
82+
side Side
83+
opposite Side
84+
}
85+
86+
fn (mut plot Plot) sides() int {
87+
mut used_faces := Set[string]{}
88+
mut sides := 0
89+
90+
mut face_copy := plot.faces.copy()
91+
for face_copy.size() > 0 {
92+
face_key := face_copy.pop() or { panic('No face found') }
93+
if used_faces.exists(face_key) {
94+
continue
95+
}
96+
97+
current_face := Face.decode(face_key)
98+
mut found_faces := Set[string]{}
99+
if current_face.side == .left || current_face.side == .right {
100+
// Try moving down from the initial position
101+
mut down_cf := current_face
102+
for plot.faces.exists(down_cf.encode()) {
103+
found_faces.add(down_cf.encode())
104+
down_cf = Face{
105+
position: Position{down_cf.position.x, down_cf.position.y + 1}
106+
side: down_cf.side
107+
}
108+
}
109+
110+
// Try moving up from the initial position
111+
mut up_cf := current_face
112+
for plot.faces.exists(up_cf.encode()) {
113+
found_faces.add(up_cf.encode())
114+
up_cf = Face{
115+
position: Position{up_cf.position.x, up_cf.position.y - 1}
116+
side: up_cf.side
117+
}
118+
}
119+
} else {
120+
// Try moving right from the initial position
121+
mut right_cf := current_face
122+
for plot.faces.exists(right_cf.encode()) {
123+
found_faces.add(right_cf.encode())
124+
right_cf = Face{
125+
position: Position{right_cf.position.x + 1, right_cf.position.y}
126+
side: right_cf.side
127+
}
128+
}
129+
130+
// Try moving left from the initial position
131+
mut left_cf := current_face
132+
for plot.faces.exists(left_cf.encode()) {
133+
found_faces.add(left_cf.encode())
134+
left_cf = Face{
135+
position: Position{left_cf.position.x - 1, left_cf.position.y}
136+
side: left_cf.side
137+
}
138+
}
139+
}
140+
141+
// Add all found faces to used_faces
142+
for found_faces.size() > 0 {
143+
used_faces.add(found_faces.pop() or { panic('No face found') })
144+
}
145+
sides++
146+
}
147+
return sides
148+
}
149+
150+
fn get_plot(garden [][]rune, start Position) Plot {
151+
start_char := garden[start.y][start.x]
152+
mut plot := Plot{
153+
positions: Set[string]{}
154+
faces: Set[string]{}
155+
char: start_char
156+
}
157+
158+
plot.positions.add(start.encode())
159+
160+
plot.faces.add_all([
161+
Face{start, .top}.encode(),
162+
Face{start, .right}.encode(),
163+
Face{start, .bottom}.encode(),
164+
Face{start, .left}.encode(),
165+
])
166+
167+
mut visited := [][]bool{len: garden.len, init: []bool{len: garden[0].len, init: false}}
168+
mut to_process := [start]
169+
170+
for i := 0; i < to_process.len; i++ {
171+
pos := to_process[i]
172+
if visited[pos.y][pos.x] {
173+
continue
174+
}
175+
176+
visited[pos.y][pos.x] = true
177+
directions := [
178+
Direction{Position{pos.x - 1, pos.y}, .left, .right},
179+
Direction{Position{pos.x + 1, pos.y}, .right, .left},
180+
Direction{Position{pos.x, pos.y - 1}, .top, .bottom},
181+
Direction{Position{pos.x, pos.y + 1}, .bottom, .top},
182+
]
183+
184+
for dir in directions {
185+
new_pos := dir.pos
186+
new_side := dir.side
187+
opposite_side := dir.opposite
188+
189+
if new_pos.x >= 0 && new_pos.x < garden[0].len && new_pos.y >= 0
190+
&& new_pos.y < garden.len && garden[new_pos.y][new_pos.x] == start_char
191+
&& !visited[new_pos.y][new_pos.x] {
192+
plot.positions.add(new_pos.encode())
193+
to_process << new_pos
194+
195+
plot.faces.remove(Face{pos, new_side}.encode())
196+
plot.faces.remove(Face{new_pos, opposite_side}.encode())
197+
198+
// Add new faces if needed
199+
if !plot.positions.exists(Position{new_pos.x, new_pos.y - 1}.encode()) {
200+
plot.faces.add(Face{new_pos, .top}.encode())
201+
}
202+
if !plot.positions.exists(Position{new_pos.x + 1, new_pos.y}.encode()) {
203+
plot.faces.add(Face{new_pos, .right}.encode())
204+
}
205+
if !plot.positions.exists(Position{new_pos.x, new_pos.y + 1}.encode()) {
206+
plot.faces.add(Face{new_pos, .bottom}.encode())
207+
}
208+
if !plot.positions.exists(Position{new_pos.x - 1, new_pos.y}.encode()) {
209+
plot.faces.add(Face{new_pos, .left}.encode())
210+
}
211+
}
212+
}
213+
}
214+
return plot
215+
}
216+
217+
fn main() {
218+
input := os.read_file('garden.input')!
219+
garden := input.split_into_lines().map(it.runes())
220+
mut plots := []Plot{}
221+
mut garden_copy := garden.clone()
222+
223+
// Find all plots
224+
for {
225+
mut found := false
226+
mut start := Position{0, 0}
227+
for y := 0; y < garden_copy.len; y++ {
228+
for x := 0; x < garden_copy[0].len; x++ {
229+
if garden_copy[y][x] != `.` {
230+
start = Position{x, y}
231+
found = true
232+
break
233+
}
234+
}
235+
if found {
236+
break
237+
}
238+
}
239+
if !found {
240+
break
241+
}
242+
243+
plot := get_plot(garden_copy, start)
244+
mut positions_copy := plot.positions.copy()
245+
for positions_copy.size() > 0 {
246+
pos := Position.decode(positions_copy.pop()!)
247+
garden_copy[pos.y][pos.x] = `.`
248+
}
249+
plots << plot
250+
}
251+
252+
mut part1 := 0
253+
for mut plot in plots {
254+
part1 += plot.area() * plot.perimeter()
255+
}
256+
println('part1: ${part1}')
257+
258+
mut part2 := 0
259+
for mut plot in plots {
260+
part2 += plot.area() * plot.sides()
261+
}
262+
println('part2: ${part2}')
263+
}

2024/12/garden.input

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

known/2024/12/drakeerv.out

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
part1: 1930
2+
part2: 1206

0 commit comments

Comments
 (0)