Advertisement
nohar

AoC 2021 Day 15 in Go

Dec 15th, 2021
316
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Go 3.19 KB | None | 0 0
  1. package main
  2.  
  3. import (
  4.     "aoc/utils"
  5.     "fmt"
  6. )
  7.  
  8. func main() {
  9.     input := utils.ReadLines()
  10.     risk, width, height := parseGrid(input)
  11.     fmt.Println("part 1: ", FindBestPath(risk, width, height))
  12.     risk, width, height = parseExtendedGrid(input)
  13.     fmt.Println("part 2: ", FindBestPath(risk, width, height))
  14. }
  15.  
  16. type state struct {
  17.     risk   []uint16 // table of risks
  18.     best   []uint16 // best[i] = cost of the lowest *known* path to i
  19.     width  int
  20.     height int
  21.     length int
  22. }
  23.  
  24. // FindBestPath returns the cost of the best path from the top-left to
  25. // the bottom-right.
  26. //
  27. // This algorithm assumes that the best path will move right or down most of the time.
  28. // We first estimate best(x, y) to be:
  29. //    min(best(x-1, y), best(x, y-1)) + risk(x, y)
  30. //
  31. // Once this initial guess is done, for each already estimated neighbor (xn, yn),
  32. // we check that:
  33. //    best(xn, yn) <= best(x, y) + risk(xn, yn)
  34. //
  35. // If not, it means that we have found a shorter path to (xn, yn) that involves
  36. // going up or left, and so we fix best(xn, yn) and propagate the check
  37. // recursively.
  38. func FindBestPath(risk []uint16, width, height int) uint16 {
  39.     s := state{
  40.         risk:   risk,
  41.         best:   make([]uint16, len(risk)),
  42.         width:  width,
  43.         height: height,
  44.         length: len(risk),
  45.     }
  46.     s.initFirstRow()
  47.     s.computeBestPaths()
  48.     return s.best[s.length-1]
  49. }
  50.  
  51. func (s *state) initFirstRow() {
  52.     for i := 1; i < s.width; i++ {
  53.         s.best[i] = s.best[i-1] + s.risk[i]
  54.     }
  55. }
  56.  
  57. func (s *state) computeBestPaths() {
  58.     for y := 1; y < s.height; y++ {
  59.         i := y * s.width
  60.         s.best[i] = s.best[i-s.width] + s.risk[i]
  61.         for x := 1; x < s.width; x++ {
  62.             i++
  63.             s.best[i] = s.lowestNeighbor(i) + s.risk[i]
  64.             s.fixPaths(i)
  65.         }
  66.     }
  67. }
  68.  
  69. func (s *state) lowestNeighbor(i int) uint16 {
  70.     return min(s.best[i-1], s.best[i-s.width])
  71. }
  72.  
  73. func (s *state) fixPaths(i int) {
  74.     currentBest := s.best[i]
  75.     s.forEachNeighbor(i, func(j int) {
  76.         if b := currentBest + s.risk[j]; b < s.best[j] {
  77.             // we found a better path to this neighbor, let's propagate it
  78.             s.best[j] = b
  79.             s.fixPaths(j)
  80.         }
  81.     })
  82. }
  83.  
  84. func (s *state) forEachNeighbor(i int, loop func(int)) {
  85.     x := i % s.width
  86.     if i > s.width {
  87.         loop(i - s.width)
  88.     }
  89.     if x != 0 {
  90.         loop(i - 1)
  91.     }
  92.     if x < s.width-1 {
  93.         loop(i + 1)
  94.     }
  95.     if j := i + s.width; j < s.length {
  96.         loop(i + s.width)
  97.     }
  98. }
  99.  
  100. func parseGrid(input []string) ([]uint16, int, int) {
  101.     height, width := len(input), len(input[0])
  102.     grid := make([]uint16, width*height)
  103.     for y, line := range input {
  104.         for x, num := range line {
  105.             grid[y*width+x] = uint16(num - '0')
  106.         }
  107.     }
  108.     return grid, width, height
  109. }
  110.  
  111. func parseExtendedGrid(input []string) ([]uint16, int, int) {
  112.     height, width := 5*len(input), 5*len(input[0])
  113.     grid := make([]uint16, width*height)
  114.     for y, line := range input {
  115.         for x, num := range line {
  116.             for i := uint16(0); i < 5; i++ {
  117.                 yy := y + int(i)*len(input)
  118.                 for j := uint16(0); j < 5; j++ {
  119.                     xx := x + int(j)*len(input[0])
  120.                     grid[yy*width+xx] = wrap(uint16(num-'0') + i + j)
  121.                 }
  122.             }
  123.         }
  124.     }
  125.     return grid, width, height
  126. }
  127.  
  128. func wrap(i uint16) uint16 {
  129.     for i > 9 {
  130.         i -= 9
  131.     }
  132.     return i
  133. }
  134.  
  135. func min(a, b uint16) uint16 {
  136.     if a < b {
  137.         return a
  138.     }
  139.     return b
  140. }
  141.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement