Skip to content

Commit 439c708

Browse files
committed
Solution for 2023, day 17
1 parent bd6ec98 commit 439c708

File tree

5 files changed

+231
-0
lines changed

5 files changed

+231
-0
lines changed

2023/17/Makefile

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
main1:
2+
go build -o main1 main1.go common.go priority_queue.go
3+
4+
main2:
5+
go build -o main2 main2.go common.go priority_queue.go
6+
7+
.PHONY: run1 run2 clean
8+
9+
run1: main1
10+
./main1 <input
11+
12+
run2: main2
13+
./main2 <input
14+
15+
clean:
16+
rm -f main1 main2
17+

2023/17/common.go

+140
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
package main
2+
3+
import (
4+
"bufio"
5+
"os"
6+
)
7+
8+
type Direction uint8
9+
10+
const (
11+
N Direction = iota
12+
E
13+
S
14+
W
15+
)
16+
17+
type P struct{ x, y int }
18+
19+
var delta = map[Direction]P{
20+
N: {-1, 0},
21+
E: {0, 1},
22+
S: {1, 0},
23+
W: {0, -1},
24+
}
25+
26+
func (p P) Step(dir Direction) P {
27+
return P{p.x + delta[dir].x, p.y + delta[dir].y}
28+
}
29+
30+
type Grid struct {
31+
Heat [][]int
32+
Size int
33+
}
34+
35+
func (g Grid) Contains(p P) bool {
36+
return p.x >= 0 && p.x < g.Size && p.y >= 0 && p.y < g.Size
37+
}
38+
39+
func (g Grid) Goal(p P) bool {
40+
return p.x == g.Size-1 && p.y == g.Size-1
41+
}
42+
43+
func (g Grid) HeatLoss(p P) int {
44+
return g.Heat[p.x][p.y]
45+
}
46+
47+
func parse() Grid {
48+
scanner := bufio.NewScanner(os.Stdin)
49+
50+
g := Grid{}
51+
52+
for scanner.Scan() {
53+
line := scanner.Text()
54+
var row []int
55+
for c := 0; c < len(line); c++ {
56+
row = append(row, int(line[c]-'0'))
57+
}
58+
g.Heat = append(g.Heat, row)
59+
}
60+
g.Size = len(g.Heat)
61+
return g
62+
}
63+
64+
func PossibleDirections(curr Direction, streak, minSteps, maxSteps int) []Direction {
65+
if streak < minSteps {
66+
return []Direction{
67+
curr, // straight
68+
}
69+
}
70+
71+
if streak >= maxSteps {
72+
return []Direction{
73+
(curr + 1) % 4, // turn right
74+
(curr + 3) % 4, // turn left
75+
}
76+
}
77+
78+
return []Direction{
79+
(curr + 1) % 4, // turn right
80+
curr, // straight
81+
(curr + 3) % 4, // turn left
82+
}
83+
}
84+
85+
type Node struct {
86+
Pos P
87+
Dir Direction
88+
Streak int
89+
}
90+
91+
func Dijkstra(g Grid, minSteps, maxSteps int) int {
92+
93+
// starting nodes
94+
startNode := Node{P{0, 0}, E, 0}
95+
startNode2 := Node{P{0, 0}, S, 0}
96+
visited := map[Node]int{startNode: 0, startNode2: 0}
97+
98+
pq := NewPriorityQueue()
99+
pq.Insert(startNode, 0)
100+
pq.Insert(startNode2, 0)
101+
102+
for pq.Len() > 0 {
103+
currentNode, currentHeatLoss := pq.PopMin()
104+
105+
if g.Goal(currentNode.Pos) {
106+
return currentHeatLoss
107+
}
108+
109+
for _, nextDir := range PossibleDirections(currentNode.Dir, currentNode.Streak, minSteps, maxSteps) {
110+
111+
nextStreak := 1
112+
if nextDir == currentNode.Dir {
113+
nextStreak = currentNode.Streak + 1
114+
}
115+
116+
nextPos := currentNode.Pos.Step(nextDir)
117+
118+
// ignore points out of bound
119+
if !g.Contains(nextPos) {
120+
continue
121+
}
122+
123+
nextNode := Node{
124+
Pos: nextPos,
125+
Dir: nextDir,
126+
Streak: nextStreak,
127+
}
128+
nextHeatLoss := currentHeatLoss + g.HeatLoss(nextPos)
129+
130+
if oldHeatLoss, ok := visited[nextNode]; ok && oldHeatLoss <= nextHeatLoss {
131+
continue
132+
}
133+
134+
visited[nextNode] = nextHeatLoss
135+
pq.Insert(nextNode, nextHeatLoss)
136+
}
137+
}
138+
139+
return 0
140+
}

2023/17/main1.go

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package main
2+
3+
import "fmt"
4+
5+
func main() {
6+
g := parse()
7+
8+
fmt.Println(Dijkstra(g, 1, 3))
9+
}

2023/17/main2.go

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package main
2+
3+
import "fmt"
4+
5+
func main() {
6+
g := parse()
7+
8+
fmt.Println(Dijkstra(g, 4, 10))
9+
}

2023/17/priority_queue.go

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package main
2+
3+
import "container/heap"
4+
5+
type item struct {
6+
value Node
7+
priority int
8+
}
9+
10+
type PriorityQueue []*item
11+
12+
func (pq PriorityQueue) Len() int {
13+
return len(pq)
14+
}
15+
16+
func (pq PriorityQueue) Less(i, j int) bool {
17+
return pq[i].priority < pq[j].priority
18+
}
19+
20+
func (pq PriorityQueue) Swap(i, j int) {
21+
pq[i], pq[j] = pq[j], pq[i]
22+
}
23+
24+
func (pq *PriorityQueue) Push(x interface{}) {
25+
it := x.(*item)
26+
*pq = append(*pq, it)
27+
}
28+
29+
func (pq *PriorityQueue) Pop() interface{} {
30+
old := *pq
31+
n := len(old)
32+
it := old[n-1]
33+
old[n-1] = nil
34+
*pq = old[0 : n-1]
35+
return it
36+
}
37+
38+
func NewPriorityQueue() *PriorityQueue {
39+
pq := make(PriorityQueue, 0)
40+
heap.Init(&pq)
41+
return &pq
42+
}
43+
44+
// typed wrappers around heap package
45+
46+
func (pq *PriorityQueue) Insert(node Node, priority int) {
47+
heap.Push(pq, &item{
48+
value: node,
49+
priority: priority,
50+
})
51+
}
52+
53+
func (pq *PriorityQueue) PopMin() (Node, int) {
54+
it := heap.Pop(pq).(*item)
55+
return it.value, it.priority
56+
}

0 commit comments

Comments
 (0)