129 lines
3.1 KiB
Go
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})
|
|
}
|