175 lines
3.5 KiB
Go
175 lines
3.5 KiB
Go
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.")
|
|
}
|