func getPortalName(grid [][]string, row, col int) string {
	leftTwo := grid[row][col-2] + grid[row][col-1]
	rightTwo := grid[row][col+1] + grid[row][col+2]
	upTwo := grid[row-2][col] + grid[row-1][col]
	downTwo := grid[row+1][col] + grid[row+2][col]
	isPortalString := func(str string) bool {
		ascii1 := str[0] - 'A'
		ascii2 := str[1] - 'A'
		if ascii1 >= 0 && ascii1 < 26 && ascii2 >= 0 && ascii2 < 26 {
			return true
		}
		return false
	}
	switch {
	case isPortalString(leftTwo):
		return leftTwo
	case isPortalString(rightTwo):
		return rightTwo
	case isPortalString(upTwo):
		return upTwo
	case isPortalString(downTwo):
		return downTwo
	}
	return ""
}
***********************************************************************************
** SMALL HELPER FUNCTIONS
***********************************************************************************
helper function to run in 4 directions and see if any of them are a capital letter
if that's true, then grab the portal name in that direction and return it (two char string)
package main
import (
	"fmt"
	"math"
	"strings"
	"github.com/alexchao26/advent-of-code-go/util"
)
func main() {
	input := util.ReadFile("../input.txt")
	lines := strings.Split(string(input), "\n")
	grid := make([][]string, len(lines))
	for i, v := range lines {
		grid[i] = strings.Split(v, "")
	}
	dijkstra := MakeDijkstraRecursive(grid)
	for !dijkstra.handleFrontOfQueue() {
	}
	finalLayer, finalRow, finalCol := 0, dijkstra.finishCoordinates[0], dijkstra.finishCoordinates[1]
	fmt.Println("Distance to ZZ", dijkstra.layers[finalLayer].grid[finalRow][finalCol].distance)
}
type DijkstraRecursive struct {
	sanitizedGrid      [][]string  
	layers             []*Layer
	queue              [][3]int
	outerPortalCoords  map[string][2]int  
	innerPortalCoords  map[string][2]int  
	mapCoordsToPortals map[[2]int]string  
	startCoordinates   [2]int             
	finishCoordinates  [2]int             
}
type Layer struct {
	grid       [][]*Node
	layerIndex int  
}
type Node struct {
	value           string
	distance        int
	portalName      string  
	jumpCoordinates [3]int  
}
func MakeDijkstraRecursive(inputGrid [][]string) *DijkstraRecursive {
	dijkstra := DijkstraRecursive{
		sanitizedGrid:      make([][]string, len(inputGrid)-4),  
		layers:             []*Layer{},
		queue:              make([][3]int, 1),
		outerPortalCoords:  map[string][2]int{},
		innerPortalCoords:  map[string][2]int{},
		mapCoordsToPortals: map[[2]int]string{},
	}
	for i := range dijkstra.sanitizedGrid {
		dijkstra.sanitizedGrid[i] = inputGrid[i+2][2 : len(inputGrid[0])-2]
	}
	for row := 2; row < len(inputGrid)-2; row++ {
		for col := 2; col < len(inputGrid[0])-2; col++ {
			portalName := getPortalName(inputGrid, row, col)
			if inputGrid[row][col] == "." && portalName != "" {
				dijkstra.mapCoordsToPortals[[2]int{row - 2, col - 2}] = portalName
				if onEdgeOfGrid(inputGrid, row-2, col-2) || onEdgeOfGrid(inputGrid, row+2, col+2) {
					dijkstra.outerPortalCoords[portalName] = [2]int{row - 2, col - 2}
				} else {
					dijkstra.innerPortalCoords[portalName] = [2]int{row - 2, col - 2}
				}
				if portalName == "AA" {
					dijkstra.startCoordinates = [2]int{row - 2, col - 2}
					dijkstra.queue[0] = [3]int{0, row - 2, col - 2}
				}
				if portalName == "ZZ" {
					dijkstra.finishCoordinates = [2]int{row - 2, col - 2}
				}
			}
		}
	}
	dijkstra.AddLayer()
	return &dijkstra
}
func (dijkstra *DijkstraRecursive) AddLayer() (layerCount int) {
	sanitizedGrid := dijkstra.sanitizedGrid
	grid := make([][]*Node, len(sanitizedGrid))
	layerIndex := len(dijkstra.layers)
	innerPortalCoords, outerPortalCoords := map[string][3]int{}, map[string][3]int{}
	for key, val := range dijkstra.outerPortalCoords {
		outerPortalCoords[key] = [3]int{
			layerIndex + 1,
			val[0],
			val[1],
		}
	}
	if layerIndex != 0 {
		for key, val := range dijkstra.innerPortalCoords {
			innerPortalCoords[key] = [3]int{
				layerIndex - 1,
				val[0],
				val[1]}
		}
	}
	for row := 0; row < len(sanitizedGrid); row++ {
		grid[row] = make([]*Node, len(sanitizedGrid[0]))
		for col := 0; col < len(sanitizedGrid); col++ {
			switch value := sanitizedGrid[row][col]; value {
			case "#":
				grid[row][col] = &Node{"#", math.MaxInt32, "", [3]int{0, 0, 0}}
			case ".":
				grid[row][col] = &Node{
					value:    ".",
					distance: math.MaxInt32,
				}
				portalName, found := dijkstra.mapCoordsToPortals[[2]int{row, col}]
				if found {
					grid[row][col].portalName = portalName
					if onEdgeOfGrid(sanitizedGrid, row, col) {
						grid[row][col].jumpCoordinates = innerPortalCoords[portalName]
					} else {
						grid[row][col].jumpCoordinates = outerPortalCoords[portalName]
					}
				}
				if portalName == "AA" && layerIndex == 0 {
					grid[row][col].distance = 0
				}
			}
		}
	}
	dijkstra.layers = append(dijkstra.layers, &Layer{grid, layerIndex})
	return len(dijkstra.layers)
}
func (dijkstra *DijkstraRecursive) handleFrontOfQueue() (done bool) {
	dRow := [4]int{0, 0, -1, 1}
	dCol := [4]int{-1, 1, 0, 0}
	dequeued := dijkstra.queue[0]
	layer, row, col := dequeued[0], dequeued[1], dequeued[2]
	currentNode := dijkstra.layers[layer].grid[row][col]
	if currentNode.portalName == "ZZ" && layer == 0 {
		return true
	}
	currentLayersGrid := dijkstra.layers[layer].grid
	for i := 0; i < 4; i++ {
		nextRow, nextCol := row+dRow[i], col+dCol[i]
		isInbounds := nextRow >= 0 && nextRow < len(currentLayersGrid) && nextCol >= 0 && nextCol < len(currentLayersGrid[0])
		if isInbounds {
			if nextNode := currentLayersGrid[nextRow][nextCol]; nextNode != nil && nextNode.value == "." && nextNode.distance == math.MaxInt32 {
				nextNode.distance = currentNode.distance + 1
				dijkstra.queue = append(dijkstra.queue, [3]int{layer, nextRow, nextCol})
			}
		}
	}
	if currentNode.portalName != "" {
		jumpLayer := currentNode.jumpCoordinates[0]
		jumpRow := currentNode.jumpCoordinates[1]
		jumpCol := currentNode.jumpCoordinates[2]
		if jumpLayer == len(dijkstra.layers) {
			dijkstra.AddLayer()
		}
		jumpNode := dijkstra.layers[jumpLayer].grid[jumpRow][jumpCol]
		jumpNode.distance = currentNode.distance + 1
		dijkstra.queue = append(dijkstra.queue, currentNode.jumpCoordinates)
	}
	dijkstra.queue = dijkstra.queue[1:]
	if len(dijkstra.queue) == 0 {
		fmt.Println("EMPTY QUEUE")
		return true
	}
	return false
}