aoc24/day16/main.go
2024-12-16 22:10:13 +01:00

129 lines
3.1 KiB
Go

package main
import (
"math"
"git.mstar.dev/mstar/goutils/sliceutils"
"git.mstar.dev/mstar/aoc24/util"
)
// A crossing is a path location with 3 or 4 other paths next to it
type Crossing struct {
At util.Vec2
LowestScore int
LowestFrom int // Index into list of crossings
}
var (
DirUp = util.Vec2{X: 0, Y: -1}
DirRight = util.Vec2{X: 1, Y: 0}
DirDown = util.Vec2{X: 0, Y: 1}
DirLeft = util.Vec2{X: -1, Y: 0}
)
var startDir = DirRight
// Get the size of the bord
func getSize(lines []string) util.Vec2 {
return util.Vec2{X: int64(len(lines[0])), Y: int64(len(lines))}
}
// Count the paths next to a given positon
func countPathsAt(lines [][]rune, at util.Vec2) int {
center := lines[at.Y][at.X]
if center == '#' {
return 0
}
if center == 'E' {
return 1
}
if center == 'S' {
return 1
}
acc := 0
if util.SafeGet(lines, at.Up()) != '#' {
acc++
}
if util.SafeGet(lines, at.Down()) != '#' {
acc++
}
if util.SafeGet(lines, at.Left()) != '#' {
acc++
}
if util.SafeGet(lines, at.Right()) != '#' {
acc++
}
return acc
}
func findAllCrossings(lines [][]rune) []Crossing {
out := []Crossing{}
for iy, line := range lines {
for ix := range line {
vec := util.Vec2{X: int64(ix), Y: int64(iy)}
pathCount := countPathsAt(lines, vec)
if pathCount >= 3 {
out = append(out, Crossing{At: vec, LowestScore: math.MaxInt, LowestFrom: -1})
}
}
}
return out
}
// Convert a list of crossings into x y map of pointers to those crossings
func crossingListToPointerMap(crossings []Crossing) map[int64]map[int64]*Crossing {
return nil
}
func findStart(lines [][]rune) util.Vec2 {
for iy, line := range lines {
for ix, char := range line {
if char == 'S' {
return util.Vec2{X: int64(ix), Y: int64(iy)}
}
}
}
// Return invalid positon if none found
return util.Vec2{X: -1, Y: -1}
}
func findEnd(lines [][]rune) util.Vec2 {
for iy, line := range lines {
for ix, char := range line {
if char == 'E' {
return util.Vec2{X: int64(ix), Y: int64(iy)}
}
}
}
// Return invalid positon if none found
return util.Vec2{X: -1, Y: -1}
}
func walk(area [][]rune, from util.Vec2, dir util.Vec2) {
// Idea: walk forward from start until crossing or wall
// At crossing, defer a walker for both sides (if not walled off) but prioritise forwards
// Keep track of current score, one crossing = 1 point
// walking length doesn't matter because negligable compared to turn cost
// At crossing, check if own score is lower than current score
// If it is, update score and set own origin
// Special case:
// If at end, update end "crossing" following the earlier rules
}
func main() {
inputLines := util.FileContentToNonEmptyLines(util.LoadFileFromArgs())
// size := getSize(inputLines)
area := sliceutils.Map(inputLines, func(t string) []rune { return []rune(t) })
crossings := findAllCrossings(area)
start := findStart(area)
// Start always has the lowest score
crossings = append(crossings, Crossing{At: start, LowestScore: math.MinInt, LowestFrom: -1})
end := findEnd(area)
// While end is just another "crossing"
crossings = append(crossings, Crossing{At: end, LowestScore: math.MaxInt, LowestFrom: -1})
}