123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275 |
- package main
- import (
- "bufio"
- "fmt"
- "log"
- "os"
- "sort"
- )
- const (
- Diff = 48
- MaxMoves = 3
- )
- 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) []Destination {
- var destinations []Destination
- moves := 0
- if lava.direction == North {
- moves = lava.moves
- }
- if moves > MaxMoves {
- return destinations
- }
- 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
- }
- 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) []Destination {
- var destinations []Destination
- moves := 0
- if lava.direction == East {
- moves = lava.moves
- }
- if moves > MaxMoves {
- return destinations
- }
- 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
- }
- destinations = append(destinations, Destination{pos: Point{y: lava.pos.y, x: x}, moves: moves, cost: cost, direction: East})
- if moves > MaxMoves {
- break
- }
- }
- return destinations
- }
- func getSouth(board [][]int, height int, width int, lava Destination) []Destination {
- var destinations []Destination
- moves := 0
- if lava.direction == South {
- moves = lava.moves
- }
- if moves > MaxMoves {
- return destinations
- }
- 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
- }
- 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) []Destination {
- var destinations []Destination
- moves := 0
- if lava.direction == West {
- moves = lava.moves
- }
- if moves > MaxMoves {
- return destinations
- }
- 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
- }
- 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{West, North, East}
- case East:
- return []int{North, East, South}
- case South:
- return []int{West, South, East}
- case West:
- return []int{South, West, North}
- }
- return []int{}
- }
- func getDestinations(board [][]int, height int, width int, lava Destination) []Destination {
- var destinations []Destination
- directions := getDirections(lava.direction)
- for i := range directions {
- switch i {
- case North:
- destinations = append(destinations, getNorth(board, height, width, lava)...)
- case East:
- destinations = append(destinations, getEast(board, height, width, lava)...)
- case South:
- destinations = append(destinations, getSouth(board, height, width, lava)...)
- case West:
- destinations = append(destinations, getWest(board, height, width, lava)...)
- }
- }
- return destinations
- }
- func part1(board [][]int) int {
- min := 1000000
- height := len(board)
- width := len(board[0])
- goal := Point{y: height - 1, x: width - 1}
- explored := make(map[Point]int)
- lava := Destination{pos: Point{x: 0, y: 0}, moves: 0, direction: East}
- prev := lava
- frontier := []Destination{lava}
- for {
- if len(frontier) == 0 {
- break
- }
- current := frontier[0]
- frontier = frontier[1:]
- if current.direction == prev.direction && current.moves+prev.moves > MaxMoves {
- continue
- }
- prev = current
- if current.pos == goal {
- if min > current.cost {
- min = current.cost
- }
- }
- successors := getDestinations(board, height, width, current)
- fmt.Println(current, successors)
- for i := range successors {
- newCost := successors[i].cost + successors[i].moves
- value, ok := explored[successors[i].pos]
- if !ok || value > newCost {
- explored[successors[i].pos] = newCost
- frontier = append(frontier, successors[i])
- }
- }
- sort.Slice(frontier, func(i, j int) bool {
- return frontier[i].cost < frontier[j].cost
- })
- }
- 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:", part1(board))
- }
|