123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 |
- package main
- import (
- "bufio"
- "fmt"
- "log"
- "os"
- )
- const (
- Diff = 48
- )
- func readInput(file *os.File) [][]int {
- scanner := bufio.NewScanner(file)
- var board [][]int
- for scanner.Scan() {
- line := scanner.Text()
- if line == "" {
- break
- }
- var row []int
- for i := range line {
- row = append(row, int(line[i]-Diff))
- }
- board = append(board, row)
- }
- return board
- }
- const (
- North = iota
- East
- South
- West
- )
- type Point struct {
- y, x int
- }
- type Destination struct {
- pos Point
- cost int
- moves int
- direction int
- }
- func getNorth(board [][]int, height int, width int, lava Destination, minMoves int, maxMoves int) []Destination {
- var destinations []Destination
- moves := 0
- if lava.direction == North {
- moves = lava.moves
- }
- end := lava.pos.y - maxMoves
- if end < 0 {
- end = 0
- }
- cost := lava.cost
- for y := lava.pos.y - 1; y >= end; y-- {
- cost += board[y][lava.pos.x]
- moves++
- if moves > maxMoves {
- break
- }
- if moves > minMoves {
- destinations = append(destinations, Destination{pos: Point{y: y, x: lava.pos.x}, moves: moves, cost: cost, direction: North})
- }
- }
- return destinations
- }
- func getEast(board [][]int, height int, width int, lava Destination, minMoves int, maxMoves int) []Destination {
- var destinations []Destination
- moves := 0
- if lava.direction == East {
- moves = lava.moves
- }
- end := lava.pos.x + maxMoves
- if end >= width {
- end = width - 1
- }
- cost := lava.cost
- for x := lava.pos.x + 1; x <= end; x++ {
- cost += board[lava.pos.y][x]
- moves++
- if moves > maxMoves {
- break
- }
- if moves > minMoves {
- destinations = append(destinations, Destination{pos: Point{y: lava.pos.y, x: x}, moves: moves, cost: cost, direction: East})
- }
- }
- return destinations
- }
- func getSouth(board [][]int, height int, width int, lava Destination, minMoves int, maxMoves int) []Destination {
- var destinations []Destination
- moves := 0
- if lava.direction == South {
- moves = lava.moves
- }
- end := lava.pos.y + maxMoves
- if end >= height {
- end = height - 1
- }
- cost := lava.cost
- for y := lava.pos.y + 1; y <= end; y++ {
- cost += board[y][lava.pos.x]
- moves++
- if moves > maxMoves {
- break
- }
- if moves > minMoves {
- destinations = append(destinations, Destination{pos: Point{y: y, x: lava.pos.x}, moves: moves, cost: cost, direction: South})
- }
- }
- return destinations
- }
- func getWest(board [][]int, height int, width int, lava Destination, minMoves int, maxMoves int) []Destination {
- var destinations []Destination
- moves := 0
- if lava.direction == West {
- moves = lava.moves
- }
- end := lava.pos.x - maxMoves
- if end < 0 {
- end = 0
- }
- cost := lava.cost
- for x := lava.pos.x - 1; x >= end; x-- {
- cost += board[lava.pos.y][x]
- moves++
- if moves > maxMoves {
- break
- }
- if moves > minMoves {
- destinations = append(destinations, Destination{pos: Point{y: lava.pos.y, x: x}, moves: moves, cost: cost, direction: West})
- }
- }
- return destinations
- }
- func getDirections(direction int) []int {
- switch direction {
- case North:
- return []int{East, North, West}
- case East:
- return []int{East, South, North}
- case South:
- return []int{East, South, West}
- case West:
- return []int{South, West, North}
- }
- return []int{}
- }
- func getDestinations(board [][]int, height int, width int, lava Destination, minMoves int, maxMoves int) []Destination {
- var destinations []Destination
- directions := getDirections(lava.direction)
- for i := range directions {
- switch directions[i] {
- case North:
- destinations = append(destinations, getNorth(board, height, width, lava, minMoves, maxMoves)...)
- case East:
- destinations = append(destinations, getEast(board, height, width, lava, minMoves, maxMoves)...)
- case South:
- destinations = append(destinations, getSouth(board, height, width, lava, minMoves, maxMoves)...)
- case West:
- destinations = append(destinations, getWest(board, height, width, lava, minMoves, maxMoves)...)
- }
- }
- return destinations
- }
- type Visited struct {
- pos Point
- direction int
- }
- func calculate(board [][]int, minMoves int, maxMoves int) int {
- min := 1000000
- height := len(board)
- width := len(board[0])
- goal := Point{y: height - 1, x: width - 1}
- explored := make(map[Visited]int)
- lava := Destination{pos: Point{x: 0, y: 0}, moves: 0, direction: East}
- frontier := []Destination{lava}
- for {
- if len(frontier) == 0 {
- break
- }
- current := frontier[0]
- frontier = frontier[1:]
- if current.pos == goal {
- if min > current.cost {
- min = current.cost
- }
- }
- successors := getDestinations(board, height, width, current, minMoves, maxMoves)
- for i := range successors {
- v := Visited{pos: successors[i].pos, direction: successors[i].direction}
- value, ok := explored[v]
- if !ok || value > successors[i].cost {
- explored[v] = successors[i].cost
- frontier = append(frontier, successors[i])
- }
- }
- }
- return min
- }
- func main() {
- if len(os.Args) < 2 {
- log.Fatal("You need to specify a file!")
- }
- filePath := os.Args[1]
- file, err := os.Open(filePath)
- if err != nil {
- log.Fatalf("Failed to open %s!\n", filePath)
- }
- board := readInput(file)
- fmt.Println("Part1:", calculate(board, 0, 3))
- fmt.Println("Part2:", calculate(board, 3, 10))
- }
|