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}) }