github.com/frodejac/aoc-2022@v0.0.0-20221213081734-037c741b1c89/internal/aoc/day11/day11.go (about)

     1  package day11
     2  
     3  import (
     4  	"math"
     5  	"sort"
     6  	"strconv"
     7  	"strings"
     8  )
     9  
    10  type point struct {
    11  	x int
    12  	y int
    13  }
    14  
    15  type Day11 struct {
    16  	heightMap [][]int
    17  	startPos  point
    18  	endPos    point
    19  }
    20  
    21  func parseInput(input string) ([][]int, point, point) {
    22  	input = strings.TrimSpace(input)
    23  	lines := strings.Split(input, "\n")
    24  	var startPos, endPos point
    25  	heightMap := make([][]int, len(lines))
    26  	for i, line := range lines {
    27  		heightMap[i] = make([]int, len(line))
    28  		for j, char := range line {
    29  			if char == 'S' {
    30  				startPos = point{x: i, y: j}
    31  				heightMap[i][j] = 0
    32  			} else if char == 'E' {
    33  				endPos = point{x: i, y: j}
    34  				heightMap[i][j] = int('z') - 97
    35  			} else {
    36  				heightMap[i][j] = int(char) - 97
    37  			}
    38  		}
    39  	}
    40  
    41  	return heightMap, startPos, endPos
    42  }
    43  
    44  func Solver(input []byte) *Day11 {
    45  	heightMap, startPos, endPos := parseInput(string(input))
    46  	return &Day11{heightMap: heightMap, startPos: startPos, endPos: endPos}
    47  }
    48  
    49  func dijkstra(w, h int, startPos point, finished func(point) bool, hasPath func(point, point) bool) int16 {
    50  	dist := make([][]int16, w)
    51  	visited := make([][]bool, w)
    52  	for i := range dist {
    53  		visited[i] = make([]bool, h)
    54  		dist[i] = make([]int16, h)
    55  		for j := range dist[i] {
    56  			dist[i][j] = math.MaxInt16
    57  		}
    58  	}
    59  	dist[startPos.x][startPos.y] = 0
    60  	queue := []point{startPos}
    61  	for len(queue) > 0 {
    62  		curr := queue[0]
    63  		queue = queue[1:]
    64  
    65  		// Skip visited nodes
    66  		if visited[curr.x][curr.y] {
    67  			continue
    68  		} else {
    69  			visited[curr.x][curr.y] = true
    70  		}
    71  
    72  		// Check if we're done
    73  		if finished(curr) {
    74  			return dist[curr.x][curr.y]
    75  		}
    76  
    77  		for _, neighbor := range []point{
    78  			{x: curr.x - 1, y: curr.y},
    79  			{x: curr.x + 1, y: curr.y},
    80  			{x: curr.x, y: curr.y - 1},
    81  			{x: curr.x, y: curr.y + 1},
    82  		} {
    83  			// Out of bounds check
    84  			if neighbor.x < 0 || neighbor.x >= w || neighbor.y < 0 || neighbor.y >= h {
    85  				continue
    86  			}
    87  
    88  			// Skip inaccessible neighbors
    89  			if !hasPath(curr, neighbor) {
    90  				continue
    91  			}
    92  
    93  			// Update shortest path distance
    94  			alt := dist[curr.x][curr.y] + 1
    95  			if alt <= dist[neighbor.x][neighbor.y] {
    96  				dist[neighbor.x][neighbor.y] = alt
    97  			}
    98  			queue = append(queue, neighbor)
    99  		}
   100  
   101  		// Sort queue by distance to get a priority queue
   102  		sort.Slice(queue[:], func(i, j int) bool {
   103  			return dist[queue[i].x][queue[i].y] < dist[queue[j].x][queue[j].y]
   104  		})
   105  	}
   106  	return -1
   107  }
   108  
   109  func (d *Day11) SolvePart1() string {
   110  	finished := func(p point) bool {
   111  		return p.x == d.endPos.x && p.y == d.endPos.y
   112  	}
   113  	hasPath := func(p1, p2 point) bool {
   114  		return (d.heightMap[p2.x][p2.y] - d.heightMap[p1.x][p1.y]) <= 1
   115  	}
   116  	w, h := len(d.heightMap), len(d.heightMap[0])
   117  	cost := dijkstra(w, h, d.startPos, finished, hasPath)
   118  	return strconv.Itoa(int(cost))
   119  }
   120  
   121  func (d *Day11) SolvePart2() string {
   122  	finished := func(p point) bool {
   123  		return d.heightMap[p.x][p.y] == 0
   124  	}
   125  	hasPath := func(p1, p2 point) bool {
   126  		return (d.heightMap[p1.x][p1.y] - d.heightMap[p2.x][p2.y]) <= 1
   127  	}
   128  	w, h := len(d.heightMap), len(d.heightMap[0])
   129  	cost := dijkstra(w, h, d.endPos, finished, hasPath)
   130  	return strconv.Itoa(int(cost))
   131  }