package day12 import ( "bufio" "fmt" "os" ) type Point struct { x int y int } func to_level(r rune) int { return int(r - 'a') } func from_level(l int) rune { return rune('a' + l) } func validMove(p1 Point, p2 Point, grid [][]int) bool { if p2.x < 0 || p2.y < 0 || p2.x >= len(grid[0]) || p2.y >= len(grid) { return false } diff := grid[p2.y][p2.x] - grid[p1.y][p1.x] return (diff <= 1) } func updateDistances(grid [][]int, distances [][]int) bool { y := 0 updatedSomething := false for _, row := range distances { for x := range row { best := distances[y][x] nexts := nextSteps(Point{x, y}, grid) for _, next := range nexts { if distances[next.y][next.x] != -1 { if best == -1 || distances[next.y][next.x]+1 < best { fmt.Println("Found a live one; at", Point{x, y}, "with score", distances[y][x], "going to", next, "with score", distances[next.y][next.x]) best = distances[next.y][next.x] + 1 updatedSomething = true } } } distances[y][x] = best } y += 1 } return updatedSomething } func nextSteps(p Point, grid [][]int) []Point { retval := []Point{} // point above above := Point{p.x, p.y - 1} if validMove(p, above, grid) { retval = append(retval, above) } // point below below := Point{p.x, p.y + 1} if validMove(p, below, grid) { retval = append(retval, below) } // point left left := Point{p.x - 1, p.y} if validMove(p, left, grid) { retval = append(retval, left) } // point right right := Point{p.x + 1, p.y} if validMove(p, right, grid) { retval = append(retval, right) } return retval } func printMap(grid [][]int) { for _, line := range grid { for _, v := range line { fmt.Printf("%c", from_level(v)) } fmt.Println() } } func printDistances(grid [][]int, distances [][]int) { for y, line := range distances { for x, v := range line { fmt.Printf("%5d/%c", v, from_level(grid[y][x])) } fmt.Println() } } func Run(filename string) { file, err := os.Open(filename) if err != nil { fmt.Println("Error opening file:", err) return } scanner := bufio.NewScanner(file) scanner.Split(bufio.ScanLines) grid := [][]int{} distances := [][]int{} var start Point var end Point for scanner.Scan() { line := scanner.Text() row := []int{} drow := []int{} y := len(grid) for x, r := range []rune(line) { if r == 'S' { r = 'a' start = Point{x, y} } if r == 'E' { r = 'z' end = Point{x, y} } row = append(row, to_level(r)) drow = append(drow, -1) } grid = append(grid, row) distances = append(distances, drow) } file.Close() distances[end.y][end.x] = 0 printMap(grid) printDistances(grid, distances) fmt.Printf("Start position is (%d, %d)\n", start.x, start.y) fmt.Printf("End position is (%d, %d)\n", end.x, end.y) fmt.Println("Valid first steps:", nextSteps(start, grid)) iteration := 0 keepGoing := true for keepGoing { fmt.Println("Running iteration", iteration) printDistances(grid, distances) keepGoing = updateDistances(grid, distances) iteration += 1 } fmt.Printf("Best path is %d steps long.\n", distances[start.y][start.x]) closestAIs := -1 for y, line := range grid { for x, v := range line { if from_level(v) == 'a' { distance := distances[y][x] fmt.Printf("Point (%d, %d) is level `a`, and is %d steps from end.\n", x, y, distance) if distance != -1 { if closestAIs == -1 || distance < closestAIs { closestAIs = distance } } } } } fmt.Println("The closest 'a' is", closestAIs, "steps away.") }