Skip to content
This repository was archived by the owner on Dec 28, 2024. It is now read-only.

Commit 1b970f2

Browse files
committed
Add solution day 16 part 1
1 parent 407c87f commit 1b970f2

File tree

5 files changed

+230
-3
lines changed

5 files changed

+230
-3
lines changed

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ deployments).
3636
### Running with `docker-compose`
3737
Just run `docker-compose up` and it will run all services as well as the `traefik`
3838
gateway. If the watch feature of `docker-compose` is enabled, the services will be
39-
automatically rebuilt and redeployed ever time the `common` module or their own
39+
automatically rebuilt and redeployed every time the `common` module or their own
4040
`solutions/dayX` module is updated.
4141

4242
## Progress
@@ -47,7 +47,7 @@ automatically rebuilt and redeployed ever time the `common` module or their own
4747
|-----|----------|-----|----------|
4848
| 01 | ⭐ ⭐ | 14 | ⭐ ⭐ |
4949
| 02 | ⭐ ⭐ | 15 | ⭐ ⭐ |
50-
| 03 | ⭐ ⭐ | 16 | |
50+
| 03 | ⭐ ⭐ | 16 | |
5151
| 04 | ⭐ ⭐ | 17 | |
5252
| 05 | ⭐ ⭐ | 18 | |
5353
| 06 | ⭐ ⭐ | 19 | |

common/util/coordinate.go

+4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ func In2DArray[T any](
1919
return !invalid
2020
}
2121

22+
func (c Coordinate) IsOrigin() bool {
23+
return c.X == 0 && c.Y == 0
24+
}
25+
2226
func (c Coordinate) Add(x int, y int) Coordinate {
2327
return Coordinate{X: c.X + x, Y: c.Y + y}
2428
}

solutions/day16/main.go

+76-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,84 @@
11
package main
22

33
import (
4+
"fmt"
45
"github.com/terminalnode/adventofcode2024/common"
6+
"math"
57
)
68

79
func main() {
8-
common.Setup(16, nil, nil)
10+
common.Setup(16, part1, nil)
11+
}
12+
13+
func part1(
14+
input string,
15+
) string {
16+
i, err := parse(input)
17+
if err != nil {
18+
return fmt.Sprintf("Failed to parse input: %v", err)
19+
}
20+
21+
// Initialize visited set with end point
22+
set := initializeVisitedSet(i)
23+
loop(initialReindeer(i.s), i, set)
24+
25+
return fmt.Sprintf("Cheapest path: %v", lowestSoFar(i, set))
26+
}
27+
28+
func loop(
29+
r reindeer,
30+
i parsedInput,
31+
set visitedSet,
32+
) {
33+
if r.visitAndCheckIfDead(set) {
34+
return
35+
}
36+
37+
lowest := lowestSoFar(i, set)
38+
if lowest != 0 && r.score > lowest {
39+
return
40+
}
41+
42+
if r.p.Equals(i.e) {
43+
return
44+
}
45+
46+
loop(r.turnClockwise(), i, set)
47+
loop(r.turnCounterClockwise(), i, set)
48+
fwd, err := r.forward(i.m)
49+
if err == nil {
50+
loop(fwd, i, set)
51+
}
52+
53+
return
54+
}
55+
56+
func lowestSoFar(
57+
i parsedInput,
58+
set visitedSet,
59+
) int {
60+
lowest := 0
61+
for _, n := range set[i.e.Y][i.e.X] {
62+
if lowest == 0 || n < lowest {
63+
lowest = n
64+
}
65+
}
66+
return lowest
67+
}
68+
69+
func initializeVisitedSet(
70+
i parsedInput,
71+
) visitedSet {
72+
set := make(visitedSet)
73+
for y, line := range i.m {
74+
set[y] = make(map[intX]map[intDirection]intScore)
75+
for x, _ := range line {
76+
set[y][x] = make(map[intDirection]intScore)
77+
set[y][x][North] = math.MaxInt
78+
set[y][x][South] = math.MaxInt
79+
set[y][x][West] = math.MaxInt
80+
set[y][x][East] = math.MaxInt
81+
}
82+
}
83+
return set
984
}

solutions/day16/parse.go

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package main
2+
3+
import (
4+
"errors"
5+
"github.com/terminalnode/adventofcode2024/common/util"
6+
"strings"
7+
)
8+
9+
type raceMap = [][]int32
10+
type parsedInput struct {
11+
m raceMap
12+
s util.Coordinate
13+
e util.Coordinate
14+
}
15+
16+
const (
17+
Start = 'S'
18+
End = 'E'
19+
Ground = '.'
20+
Wall = '#'
21+
)
22+
23+
func parse(
24+
input string,
25+
) (parsedInput, error) {
26+
lines := strings.Split(input, "\n")
27+
m := make(raceMap, len(lines))
28+
s := util.Coordinate{}
29+
e := util.Coordinate{}
30+
31+
for y, line := range lines {
32+
m[y] = []int32(line)
33+
if s.IsOrigin() || e.IsOrigin() {
34+
for x, ch := range line {
35+
if ch == Start {
36+
s = util.Coordinate{X: x, Y: y}
37+
m[y][x] = Ground
38+
} else if ch == End {
39+
e = util.Coordinate{X: x, Y: y}
40+
m[y][x] = Ground
41+
}
42+
}
43+
}
44+
}
45+
46+
var err error
47+
switch {
48+
case s.IsOrigin() && e.IsOrigin():
49+
err = errors.New("neither start nor end was found")
50+
case s.IsOrigin():
51+
err = errors.New("start not found")
52+
case e.IsOrigin():
53+
err = errors.New("end not found")
54+
}
55+
return parsedInput{m: m, s: s, e: e}, err
56+
}

solutions/day16/reindeer.go

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"github.com/terminalnode/adventofcode2024/common/util"
6+
)
7+
8+
type intX = int
9+
type intY = int
10+
type intDirection = int
11+
type intScore = int
12+
type visitedSet = map[intY]map[intX]map[intDirection]intScore
13+
14+
const (
15+
North = iota
16+
East
17+
South
18+
West
19+
)
20+
21+
type reindeer struct {
22+
p util.Coordinate
23+
score int
24+
direction int
25+
}
26+
27+
func initialReindeer(
28+
start util.Coordinate,
29+
) reindeer {
30+
return reindeer{
31+
p: start,
32+
score: 0,
33+
direction: East,
34+
}
35+
}
36+
37+
func (r reindeer) forward(m raceMap) (reindeer, error) {
38+
var newP util.Coordinate
39+
switch r.direction {
40+
case North:
41+
newP = r.p.North()
42+
case East:
43+
newP = r.p.East()
44+
case South:
45+
newP = r.p.South()
46+
case West:
47+
newP = r.p.West()
48+
default:
49+
return r, fmt.Errorf("illegal direction %d", r.direction)
50+
}
51+
52+
if m[r.p.Y][r.p.X] == '#' {
53+
return r, fmt.Errorf("wall hit")
54+
}
55+
56+
return reindeer{
57+
p: newP,
58+
score: 1 + r.score,
59+
direction: r.direction,
60+
}, nil
61+
}
62+
63+
func (r reindeer) turnClockwise() reindeer {
64+
return reindeer{
65+
p: r.p,
66+
score: 1000 + r.score,
67+
direction: (r.direction + 1) % 4,
68+
}
69+
}
70+
71+
func (r reindeer) turnCounterClockwise() reindeer {
72+
return reindeer{
73+
p: r.p,
74+
score: 1000 + r.score,
75+
direction: (r.direction - 1 + 4) % 4,
76+
}
77+
}
78+
79+
func (r reindeer) visitAndCheckIfDead(
80+
set visitedSet,
81+
) bool {
82+
v := set[r.p.Y][r.p.X][r.direction]
83+
if v == 0 || r.score < v {
84+
set[r.p.Y][r.p.X][r.direction] = r.score
85+
return false
86+
}
87+
return true
88+
}
89+
90+
func (r reindeer) String() string {
91+
return fmt.Sprintf("Reindeer{p:%s, score:%d, direction:%d}", r.p, r.score, r.direction)
92+
}

0 commit comments

Comments
 (0)