Skip to content

Commit 0863841

Browse files
committed
2024 day 12 (and bump go version)
1 parent ff8080b commit 0863841

File tree

5 files changed

+228
-2
lines changed

5 files changed

+228
-2
lines changed

.vscode/launch.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
// Use IntelliSense to learn about possible attributes.
3+
// Hover to view descriptions of existing attributes.
4+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5+
"version": "0.2.0",
6+
"configurations": [
7+
{
8+
"name": "Launch Package",
9+
"type": "go",
10+
"request": "launch",
11+
"mode": "auto",
12+
"program": "${fileDirname}",
13+
}
14+
]
15+
}

2024/go/day12/example-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

2024/go/day12/main.go

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
package main
2+
3+
import (
4+
"aoc-shared/pkg/sharedcode"
5+
"aoc-shared/pkg/sharedstruct"
6+
"os"
7+
"path/filepath"
8+
"runtime"
9+
)
10+
11+
func getCurrentDirectory() string {
12+
_, filename, _, _ := runtime.Caller(0)
13+
dirname := filepath.Dir(filename)
14+
return dirname
15+
}
16+
17+
// Default Input path is current directory + example-input
18+
var inputPath = filepath.Join(getCurrentDirectory(), "example-input")
19+
var isUsingExample = true
20+
21+
func main() {
22+
// If another cmd argument has been passed, use that as the input path:
23+
if len(os.Args) > 1 {
24+
inputPath = os.Args[1]
25+
isUsingExample = false
26+
}
27+
28+
var _, contents = sharedcode.ParseFile(inputPath)
29+
30+
partOne(contents)
31+
partTwo(contents)
32+
}
33+
34+
type plotStruct struct {
35+
letter byte
36+
points [][2]int
37+
perimeter int
38+
sides int
39+
}
40+
41+
func partOne(contents []string) {
42+
// Use a BFS and not just a loop;
43+
plots := buildPlots(contents, false)
44+
45+
totalPrice := 0
46+
for i := 0; i < len(plots); i++ {
47+
totalPrice += (len(plots[i].points) * plots[i].perimeter)
48+
}
49+
50+
sharedstruct.PrintOutput(sharedstruct.Output{
51+
Day: 12,
52+
Part: 1,
53+
Value: totalPrice,
54+
})
55+
}
56+
57+
func partTwo(contents []string) {
58+
// Use a BFS and not just a loop;
59+
plots := buildPlots(contents, true)
60+
61+
totalPrice := 0
62+
for i := 0; i < len(plots); i++ {
63+
totalPrice += (len(plots[i].points) * plots[i].sides)
64+
}
65+
66+
sharedstruct.PrintOutput(sharedstruct.Output{
67+
Day: 12,
68+
Part: 2,
69+
Value: totalPrice,
70+
})
71+
}
72+
73+
type queueStruct struct {
74+
pos [2]int
75+
currChar byte
76+
}
77+
78+
func buildPlots(contents []string, partTwo bool) []plotStruct {
79+
plots := make([]plotStruct, 0)
80+
visited := make(map[[2]int]bool, 0)
81+
82+
for i, line := range contents {
83+
for j := range line {
84+
if _, ok := visited[[2]int{i, j}]; ok {
85+
continue
86+
}
87+
88+
plots = append(plots, buildPlot(i, j, contents, &visited, partTwo))
89+
}
90+
}
91+
92+
return plots
93+
}
94+
95+
func buildPlot(i int, j int, contents []string, visited *map[[2]int]bool, partTwo bool) plotStruct {
96+
plot := plotStruct{}
97+
98+
directions := [4][2]int{
99+
{1, 0}, // Down
100+
{0, 1}, // Right
101+
{-1, 0}, // Up
102+
{0, -1}, // Left
103+
}
104+
105+
currentValue := contents[i][j]
106+
plot.letter = currentValue
107+
queue := make([]queueStruct, 0)
108+
queue = append(queue, queueStruct{
109+
pos: [2]int{i, j},
110+
currChar: currentValue,
111+
})
112+
113+
var element queueStruct
114+
115+
for {
116+
if len(queue) == 0 {
117+
break
118+
}
119+
120+
// Grab the next element in queue
121+
element, queue = queue[0], queue[1:]
122+
123+
// if visited, exit
124+
_, ok := (*visited)[element.pos]
125+
if ok {
126+
continue
127+
}
128+
129+
(*visited)[element.pos] = true
130+
131+
plot.points = append(plot.points, element.pos)
132+
133+
for _, dir := range directions {
134+
newI := element.pos[0] + dir[0]
135+
newJ := element.pos[1] + dir[1]
136+
137+
// Out of bounds checks
138+
if newI < 0 || newJ < 0 || newI > len(contents)-1 || newJ > len(contents[0])-1 {
139+
plot.perimeter++
140+
continue
141+
}
142+
143+
// Rules check; it must be the same char
144+
if plot.letter != contents[newI][newJ] {
145+
plot.perimeter++
146+
continue
147+
}
148+
149+
queue = append(queue, queueStruct{
150+
pos: [2]int{newI, newJ},
151+
currChar: element.currChar,
152+
})
153+
}
154+
}
155+
156+
if partTwo {
157+
corners := 0
158+
// For part 2, we count sides. This is equivalent to counting corners which is easier
159+
orthogonalPairs := [][2][2]int{}
160+
for i := 0; i < 4; i++ {
161+
orthogonalPairs = append(orthogonalPairs, [2][2]int{directions[i], directions[(i+1)%4]})
162+
}
163+
for _, point := range plot.points {
164+
cornerCount := 0
165+
for _, pair := range orthogonalPairs {
166+
// To check fo a corner, we check that the either: At least one orthogonal direction pair is not in plot OR both match but diagonal is not in plot
167+
posOne := [2]int{point[0] + pair[0][0], point[1] + pair[0][1]}
168+
posTwo := [2]int{point[0] + pair[1][0], point[1] + pair[1][1]}
169+
// 1. One orth. direction pair is not in plot
170+
if !isInSlice(plot.points, posOne) && !isInSlice(plot.points, posTwo) {
171+
cornerCount++
172+
continue
173+
}
174+
175+
//2. both match but diagonal is not in plot
176+
diagonal := [2]int{point[0] + pair[0][0] + pair[1][0], point[1] + pair[0][1] + pair[1][1]}
177+
if isInSlice(plot.points, posOne) && isInSlice(plot.points, posTwo) && !isInSlice(plot.points, diagonal) {
178+
cornerCount++
179+
continue
180+
}
181+
182+
}
183+
if cornerCount > 0 {
184+
corners += cornerCount
185+
}
186+
}
187+
188+
plot.sides = corners
189+
}
190+
191+
return plot
192+
}
193+
194+
func isInSlice(haystack [][2]int, needle [2]int) bool {
195+
for _, point := range haystack {
196+
if point == needle {
197+
return true
198+
}
199+
}
200+
return false
201+
}

go.work

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
go 1.21.5
1+
go 1.23.4
22

33
use ./shared/go
44
use ./2015/go

shared/go/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module aoc-shared
22

3-
go 1.21.5
3+
go 1.23.4

0 commit comments

Comments
 (0)