advent-of-code/day17/main.go

138 lines
3.2 KiB
Go
Raw Normal View History

2023-12-26 12:41:46 -07:00
package main
import (
"bufio"
_ "embed"
"fmt"
aoc "go.sour.is/advent-of-code"
)
// var log = aoc.Log
func main() { aoc.MustResult(aoc.Runner(run)) }
type result struct {
valuePT1 int
valuePT2 int
}
func (r result) String() string { return fmt.Sprintf("%#v", r) }
func run(scan *bufio.Scanner) (*result, error) {
2023-12-28 18:57:22 -07:00
var m aoc.Map[rune]
2023-12-26 12:41:46 -07:00
for scan.Scan() {
text := scan.Text()
m = append(m, []rune(text))
}
result := result{}
result.valuePT1 = search(m, 1, 3)
result.valuePT2 = search(m, 4, 10)
return &result, nil
}
2023-12-28 18:57:22 -07:00
func search(m aoc.Map[rune], minSteps, maxSteps int) int {
type direction int8
type rotate int8
2023-12-26 12:41:46 -07:00
2023-12-28 18:57:22 -07:00
const (
CW rotate = 1
CCW rotate = -1
)
2023-12-26 12:41:46 -07:00
2023-12-28 18:57:22 -07:00
var (
U = aoc.Point{-1, 0}
R = aoc.Point{0, 1}
D = aoc.Point{1, 0}
L = aoc.Point{0, -1}
)
2023-12-26 12:41:46 -07:00
2023-12-29 21:10:59 -07:00
var Direction = []aoc.Point{U, R, D, L}
var Directions = make(map[aoc.Point]direction, len(Direction))
for k, v := range Direction {
Directions[v] = direction(k)
2023-12-26 12:41:46 -07:00
}
rows, cols := m.Size()
2023-12-28 18:57:22 -07:00
target := aoc.Point{rows - 1, cols - 1}
2023-12-26 12:41:46 -07:00
2023-12-28 18:57:22 -07:00
type position struct {
loc aoc.Point
direction aoc.Point
steps int
2023-12-26 12:41:46 -07:00
}
2023-12-28 18:57:22 -07:00
step := func(p position) position {
return position{p.loc.Add(p.direction), p.direction, p.steps + 1}
2023-12-26 12:41:46 -07:00
}
2023-12-28 18:57:22 -07:00
rotateAndStep := func(p position, towards rotate) position {
2023-12-29 21:10:59 -07:00
d := Direction[(int8(Directions[p.direction])+int8(towards)+4)%4]
// fmt.Println(towards, Directions[p.direction], "->", Directions[d])
2023-12-28 18:57:22 -07:00
return position{p.loc.Add(d), d, 1}
2023-12-26 12:41:46 -07:00
}
2023-12-28 18:57:22 -07:00
type memo struct {
cost int
position
}
less := func(a, b memo) bool {
if a.cost != b.cost {
return a.cost < b.cost
}
if a.position.loc != b.position.loc {
return b.position.loc.Less(a.position.loc)
}
if a.position.direction != b.position.direction {
return b.position.direction.Less(a.position.direction)
}
2023-12-29 21:10:59 -07:00
return a.steps < b.steps
2023-12-26 12:41:46 -07:00
}
2023-12-28 18:57:22 -07:00
pq := aoc.PriorityQueue(less)
pq.Enqueue(memo{position: position{direction: D}})
pq.Enqueue(memo{position: position{direction: R}})
visited := aoc.Set[position]()
2023-12-26 12:41:46 -07:00
2023-12-28 18:57:22 -07:00
for !pq.IsEmpty() {
current, _ := pq.Dequeue()
2023-12-26 12:41:46 -07:00
2023-12-28 18:57:22 -07:00
if current.loc == target && current.steps >= minSteps {
return current.cost
}
2023-12-26 12:41:46 -07:00
2023-12-29 21:10:59 -07:00
seen := position{loc: current.loc, direction: current.direction, steps: current.steps}
2023-12-28 18:57:22 -07:00
if visited.Has(seen) {
2023-12-29 21:10:59 -07:00
// fmt.Println("visited", seen)
2023-12-26 12:41:46 -07:00
continue
}
2023-12-28 18:57:22 -07:00
visited.Add(seen)
2023-12-26 12:41:46 -07:00
2023-12-29 21:10:59 -07:00
// fmt.Print("\033[2J\033[H")
// fmt.Println("step ", current.steps, " dir ", Directions[current.direction], " steps ", " score ", current.cost, current.loc)
2023-12-28 18:57:22 -07:00
if left := rotateAndStep(current.position, CCW); current.steps >= minSteps && m.Valid(left.loc) {
_, cost, _ := m.Get(left.loc)
2023-12-29 21:10:59 -07:00
// fmt.Println("turn left", current, left)
2023-12-28 18:57:22 -07:00
pq.Enqueue(memo{cost: current.cost + int(cost-'0'), position: left})
2023-12-26 12:41:46 -07:00
}
2023-12-28 18:57:22 -07:00
if right := rotateAndStep(current.position, CW); current.steps >= minSteps && m.Valid(right.loc) {
_, cost, _ := m.Get(right.loc)
2023-12-29 21:10:59 -07:00
// fmt.Println("turn right", current, right)
2023-12-28 18:57:22 -07:00
pq.Enqueue(memo{cost: current.cost + int(cost-'0'), position: right})
}
2023-12-26 12:41:46 -07:00
2023-12-28 18:57:22 -07:00
if forward := step(current.position); current.steps < maxSteps && m.Valid(forward.loc) {
_, cost, _ := m.Get(forward.loc)
2023-12-29 21:10:59 -07:00
// fmt.Println("go forward", current, forward)
2023-12-28 18:57:22 -07:00
pq.Enqueue(memo{cost: current.cost + int(cost-'0'), position: forward})
2023-12-26 12:41:46 -07:00
}
}
2023-12-28 18:57:22 -07:00
return -1
2023-12-26 12:41:46 -07:00
}