You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
417 lines
8.8 KiB
Go
417 lines
8.8 KiB
Go
package day17
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"math"
|
|
"strings"
|
|
)
|
|
|
|
type NodeID int
|
|
type Distance int
|
|
|
|
type Node struct {
|
|
nodeID NodeID
|
|
dist Distance
|
|
direction rune
|
|
}
|
|
|
|
type Neighbor struct {
|
|
nodeID NodeID
|
|
dist Distance
|
|
direction rune
|
|
}
|
|
|
|
type Step struct {
|
|
nodeID NodeID
|
|
dist Distance
|
|
direction rune
|
|
stepsInDir int
|
|
}
|
|
|
|
func makeStepKey(s *Step) string {
|
|
return fmt.Sprintf("%v", s)
|
|
}
|
|
|
|
func findValidNeighborSteps(neighbors []Neighbor, step *Step, visited map[string]bool, min int, max int) []*Step {
|
|
result := []*Step{}
|
|
|
|
for _, neighbor := range neighbors {
|
|
/*
|
|
if known[neighbor.nodeID] {
|
|
continue
|
|
}
|
|
*/
|
|
|
|
if neighbor.direction != step.direction && step.stepsInDir < min {
|
|
continue
|
|
}
|
|
|
|
if neighbor.direction == step.direction && step.stepsInDir == max {
|
|
continue
|
|
}
|
|
|
|
if neighbor.direction == 'U' && step.direction == 'D' || neighbor.direction == 'D' && step.direction == 'U' ||
|
|
neighbor.direction == 'L' && step.direction == 'R' || neighbor.direction == 'R' && step.direction == 'L' {
|
|
continue
|
|
}
|
|
|
|
countSteps := 0
|
|
if neighbor.direction == step.direction {
|
|
countSteps = step.stepsInDir + 1
|
|
} else {
|
|
countSteps = 1
|
|
}
|
|
|
|
newStep := &Step{
|
|
nodeID: neighbor.nodeID,
|
|
dist: neighbor.dist,
|
|
direction: neighbor.direction,
|
|
stepsInDir: countSteps,
|
|
}
|
|
|
|
newStepKey := makeStepKey(newStep)
|
|
if visited[newStepKey] {
|
|
continue
|
|
}
|
|
result = append(result, newStep)
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func dijkstraShortestPath(adjacencyList [][]Neighbor, start, end NodeID, min int, max int) (map[string]*Step, map[string]Distance) {
|
|
vectorSize := len(adjacencyList)
|
|
prev := make(map[string]*Step, vectorSize)
|
|
dist := make(map[string]Distance, vectorSize)
|
|
queue := make(map[string]*Step, vectorSize)
|
|
visited := make(map[string]bool, vectorSize)
|
|
|
|
step0R := Step{
|
|
nodeID: start,
|
|
dist: 0,
|
|
direction: 'R',
|
|
stepsInDir: 0,
|
|
}
|
|
step0D := Step{
|
|
nodeID: start,
|
|
dist: 0,
|
|
direction: 'D',
|
|
stepsInDir: 0,
|
|
}
|
|
|
|
dist[makeStepKey(&step0R)] = 0
|
|
dist[makeStepKey(&step0D)] = 0
|
|
|
|
queue[makeStepKey(&step0R)] = &step0R
|
|
queue[makeStepKey(&step0D)] = &step0D
|
|
|
|
for len(queue) > 0 {
|
|
var key string
|
|
var step *Step
|
|
minDist := Distance(math.MaxInt32)
|
|
|
|
for k, s := range queue {
|
|
|
|
currDist, found := dist[k]
|
|
if found && currDist < minDist {
|
|
minDist = currDist
|
|
key = k
|
|
step = s
|
|
}
|
|
}
|
|
|
|
if step.nodeID == end {
|
|
break
|
|
}
|
|
|
|
delete(queue, key)
|
|
visited[key] = true
|
|
|
|
neighborSteps := findValidNeighborSteps(adjacencyList[step.nodeID], step, visited, min, max)
|
|
|
|
for _, neighborStep := range neighborSteps {
|
|
queue[makeStepKey(neighborStep)] = neighborStep
|
|
|
|
alt := minDist + neighborStep.dist
|
|
|
|
neighborKey := makeStepKey(neighborStep)
|
|
neighborDist, found := dist[neighborKey]
|
|
if !found || alt < neighborDist {
|
|
dist[neighborKey] = alt
|
|
prev[neighborKey] = step
|
|
}
|
|
}
|
|
}
|
|
|
|
return prev, dist
|
|
}
|
|
|
|
func MaxHeat(lines []string, min int, max int) int {
|
|
height := len(lines)
|
|
width := len(lines[0])
|
|
coordToID := func(row, col int) NodeID {
|
|
return NodeID(row*width + col)
|
|
}
|
|
|
|
adjacencyList := make([][]Neighbor, height*width)
|
|
|
|
for row := range lines {
|
|
for col := range lines[row] {
|
|
|
|
nodeID := coordToID(row, col)
|
|
|
|
neighbors := []Neighbor{}
|
|
|
|
if row > 0 {
|
|
dist := lines[row-1][col] - '0'
|
|
neighbors = append(neighbors, Neighbor{
|
|
nodeID: coordToID(row-1, col),
|
|
dist: Distance(dist),
|
|
direction: 'U',
|
|
})
|
|
}
|
|
|
|
if row < height-1 {
|
|
dist := lines[row+1][col] - '0'
|
|
neighbors = append(neighbors, Neighbor{
|
|
nodeID: coordToID(row+1, col),
|
|
dist: Distance(dist),
|
|
direction: 'D',
|
|
})
|
|
}
|
|
|
|
if col > 0 {
|
|
dist := lines[row][col-1] - '0'
|
|
neighbors = append(neighbors, Neighbor{
|
|
nodeID: coordToID(row, col-1),
|
|
dist: Distance(dist),
|
|
direction: 'L',
|
|
})
|
|
}
|
|
|
|
if col < width-1 {
|
|
dist := lines[row][col+1] - '0'
|
|
neighbors = append(neighbors, Neighbor{
|
|
nodeID: coordToID(row, col+1),
|
|
dist: Distance(dist),
|
|
direction: 'R',
|
|
})
|
|
}
|
|
|
|
adjacencyList[nodeID] = neighbors
|
|
}
|
|
}
|
|
|
|
start := coordToID(0, 0)
|
|
end := coordToID(height-1, width-1)
|
|
prevs, distances := dijkstraShortestPath(adjacencyList, start, end, min, max)
|
|
|
|
minDist := Distance(math.MaxInt32)
|
|
minKey := ""
|
|
for key, dist := range distances {
|
|
if strings.HasPrefix(key, fmt.Sprintf("&{%d", end)) {
|
|
fmt.Println(key, dist)
|
|
if dist < minDist {
|
|
minDist = dist
|
|
minKey = key
|
|
}
|
|
}
|
|
}
|
|
|
|
fmt.Print(minKey, "-")
|
|
step, found := prevs[minKey]
|
|
for found {
|
|
fmt.Printf("(%d,%d) %s - ", int(step.nodeID)/width, int(step.nodeID)%width, string(step.direction))
|
|
step, found = prevs[makeStepKey(step)]
|
|
}
|
|
|
|
/*
|
|
nodeID := end
|
|
for nodeID != start {
|
|
fmt.Printf("(%d,%d) - ", int(nodeID)/width, int(nodeID)%width)
|
|
nodeID = prevs[nodeID]
|
|
}
|
|
*/
|
|
return int(minDist) //int(distances[end])
|
|
}
|
|
|
|
var adjacencyList [][]Node
|
|
var visited []bool
|
|
var currentPath []Node
|
|
var simplePath [][]Node
|
|
var distances []Distance
|
|
var vectorSize int
|
|
var currMin Distance
|
|
var minimums map[string]Distance
|
|
|
|
func key(node1, node2 NodeID) string { return fmt.Sprintf("%d-%d", node1, node2) }
|
|
|
|
func findValidNeighbors(neighbors []Node, directionsTaken string) []Node {
|
|
result := []Node{}
|
|
|
|
for _, neighbor := range neighbors {
|
|
destDirection := directionsTaken + string(neighbor.direction)
|
|
lastFour := ""
|
|
if len(destDirection) >= 4 {
|
|
lastFour = destDirection[len(destDirection)-4:]
|
|
}
|
|
|
|
if lastFour == "UUUU" || lastFour == "DDDD" || lastFour == "LLLL" || lastFour == "RRRR" {
|
|
continue
|
|
}
|
|
|
|
result = append(result, neighbor)
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func DFS(current Node, goalID NodeID, previousDist Distance, directionsTaken string) {
|
|
|
|
if previousDist+current.dist > currMin {
|
|
return
|
|
}
|
|
/*
|
|
k := key(current.nodeID, goalID)
|
|
minDist, found := minimums[k]
|
|
if found && minDist < previousDist+current.dist {
|
|
return
|
|
}
|
|
*/
|
|
if len(simplePath) > 1000 {
|
|
log.Fatalf("loop 1 ?")
|
|
}
|
|
|
|
if len(directionsTaken) > vectorSize {
|
|
log.Fatalf("loop 2 ?")
|
|
}
|
|
|
|
if visited[current.nodeID] {
|
|
return
|
|
}
|
|
|
|
visited[current.nodeID] = true
|
|
currentPath = append(currentPath, current)
|
|
|
|
if current.nodeID == goalID {
|
|
distance := Distance(0)
|
|
for i := len(currentPath) - 1; i >= 0; i-- {
|
|
distance += currentPath[i].dist
|
|
if currentPath[i].nodeID != goalID {
|
|
k := key(currentPath[i].nodeID, goalID)
|
|
minimums[k] = distance
|
|
}
|
|
}
|
|
//distance := utils.Reduce(currentPath, 0, func(acc Distance, curr Node) Distance { return acc + curr.dist })
|
|
if distance < currMin {
|
|
distances = append(distances, distance)
|
|
duplicate := make([]Node, len(currentPath))
|
|
copy(duplicate, currentPath)
|
|
simplePath = append(simplePath, duplicate)
|
|
currMin = distance
|
|
|
|
}
|
|
visited[current.nodeID] = false
|
|
currentPath = currentPath[:len(currentPath)-1]
|
|
return
|
|
}
|
|
|
|
neighbors := findValidNeighbors(adjacencyList[current.nodeID], directionsTaken)
|
|
|
|
for _, neighbor := range neighbors {
|
|
DFS(neighbor, goalID, previousDist+current.dist, directionsTaken+string(neighbor.direction))
|
|
}
|
|
|
|
visited[current.nodeID] = false
|
|
currentPath = currentPath[:len(currentPath)-1]
|
|
}
|
|
|
|
func MaxHeat2(lines []string) int {
|
|
height := len(lines)
|
|
width := len(lines[0])
|
|
|
|
coordToID := func(row, col int) NodeID { return NodeID(row*width + col) }
|
|
|
|
vectorSize = height * width
|
|
visited = make([]bool, vectorSize)
|
|
directionsTaken := ""
|
|
adjacencyList = make([][]Node, vectorSize)
|
|
currMin = Distance(math.MaxInt32)
|
|
|
|
for row := range lines {
|
|
for col := range lines[row] {
|
|
|
|
nodeID := coordToID(row, col)
|
|
|
|
neighbors := []Node{}
|
|
|
|
if row > 0 {
|
|
dist := lines[row-1][col] - '0'
|
|
neighbors = append(neighbors, Node{
|
|
nodeID: coordToID(row-1, col),
|
|
dist: Distance(dist),
|
|
direction: 'U',
|
|
})
|
|
}
|
|
|
|
if row < height-1 {
|
|
dist := lines[row+1][col] - '0'
|
|
neighbors = append(neighbors, Node{
|
|
nodeID: coordToID(row+1, col),
|
|
dist: Distance(dist),
|
|
direction: 'D',
|
|
})
|
|
}
|
|
|
|
if col > 0 {
|
|
dist := lines[row][col-1] - '0'
|
|
neighbors = append(neighbors, Node{
|
|
nodeID: coordToID(row, col-1),
|
|
dist: Distance(dist),
|
|
direction: 'L',
|
|
})
|
|
}
|
|
|
|
if col < width-1 {
|
|
dist := lines[row][col+1] - '0'
|
|
neighbors = append(neighbors, Node{
|
|
nodeID: coordToID(row, col+1),
|
|
dist: Distance(dist),
|
|
direction: 'R',
|
|
})
|
|
}
|
|
|
|
adjacencyList[nodeID] = neighbors
|
|
}
|
|
}
|
|
|
|
start := Node{
|
|
nodeID: coordToID(0, 0),
|
|
dist: 0,
|
|
direction: ' ',
|
|
}
|
|
goalID := coordToID(height-1, width-1)
|
|
currentPath = []Node{}
|
|
simplePath = [][]Node{}
|
|
distances = []Distance{}
|
|
minimums = map[string]Distance{}
|
|
|
|
DFS(start, goalID, 0, directionsTaken)
|
|
|
|
minDist := Distance(math.MaxInt32)
|
|
idx := -1
|
|
for i, v := range distances {
|
|
if v < minDist {
|
|
idx = i
|
|
minDist = v
|
|
}
|
|
}
|
|
|
|
for _, node := range simplePath[idx] {
|
|
fmt.Printf("(%d,%d) - ", int(node.nodeID)/width, int(node.nodeID)%width)
|
|
}
|
|
|
|
return int(minDist)
|
|
}
|