Day 18 part 2 solution (rewrite)
This solves both parts much faster than before, but still on the order of 3-20 seconds, so more improvements (or another rewrite?) are needed. But we're getting there...
This commit is contained in:
394
days/18.go
394
days/18.go
@ -1,17 +1,18 @@
|
|||||||
package days
|
package days
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"container/heap"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"sort"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/beefsack/go-astar"
|
"github.com/edwingeng/deque/v2"
|
||||||
u "parnic.com/aoc2019/utilities"
|
u "parnic.com/aoc2019/utilities"
|
||||||
)
|
)
|
||||||
|
|
||||||
type day18Cell int
|
type day18Cell int
|
||||||
type day18Vec u.Vec2[int]
|
type day18Vec u.Vec2[int]
|
||||||
|
type day18Graph map[rune][]u.Pair[rune, int]
|
||||||
|
|
||||||
const (
|
const (
|
||||||
day18CellWall day18Cell = iota
|
day18CellWall day18Cell = iota
|
||||||
@ -27,68 +28,18 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// variables needed for pathfinding
|
|
||||||
// a better solution would be to keep the current path search state in its
|
|
||||||
// own struct and store these on there, but i don't need to worry about
|
|
||||||
// thread safety right now, so this will do fine
|
|
||||||
var (
|
|
||||||
pathGrid [][]day18Cell
|
|
||||||
pathDoors map[day18Vec]int
|
|
||||||
)
|
|
||||||
|
|
||||||
type day18KeySearchResult struct {
|
|
||||||
keyType int
|
|
||||||
path []astar.Pather
|
|
||||||
distance float64
|
|
||||||
found bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type Day18 struct {
|
type Day18 struct {
|
||||||
entrance day18Vec
|
entrance day18Vec
|
||||||
grid [][]day18Cell
|
grid [][]day18Cell
|
||||||
doors map[day18Vec]int
|
doors map[day18Vec]int
|
||||||
keys map[day18Vec]int
|
keys map[day18Vec]int
|
||||||
knownPaths map[u.Pair[day18Vec, string]]u.Pair[int, bool]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v day18Vec) PathNeighbors() []astar.Pather {
|
|
||||||
ret := make([]astar.Pather, 0)
|
|
||||||
|
|
||||||
for _, offset := range day18AdjacentOffsets {
|
|
||||||
vOffset := day18Vec{X: v.X + offset.X, Y: v.Y + offset.Y}
|
|
||||||
if vOffset.X < 0 || vOffset.Y < 0 || vOffset.Y >= len(pathGrid) || vOffset.X >= len(pathGrid[0]) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, exists := pathDoors[vOffset]; exists {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if pathGrid[vOffset.Y][vOffset.X] == day18CellOpen {
|
|
||||||
ret = append(ret, vOffset)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v day18Vec) PathNeighborCost(to astar.Pather) float64 {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v day18Vec) PathEstimatedCost(to astar.Pather) float64 {
|
|
||||||
// this is insanely more complicated than i feel like it should be.
|
|
||||||
// i guess generics aren't quite as flexible as i'd hoped. Go doesn't
|
|
||||||
// seem to know that a day18Vec is a vec2i which is a vec2[int].
|
|
||||||
return float64(u.ManhattanDistance(u.Vec2[int]{X: v.X, Y: v.Y}, u.Vec2[int]{X: to.(day18Vec).X, Y: to.(day18Vec).Y}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Day18) Parse() {
|
func (d *Day18) Parse() {
|
||||||
d.doors = make(map[day18Vec]int)
|
d.doors = make(map[day18Vec]int)
|
||||||
d.keys = make(map[day18Vec]int)
|
d.keys = make(map[day18Vec]int)
|
||||||
d.knownPaths = make(map[u.Pair[day18Vec, string]]u.Pair[int, bool])
|
|
||||||
|
|
||||||
lines := u.GetStringLines("18s6")
|
lines := u.GetStringLines("18p")
|
||||||
d.grid = make([][]day18Cell, len(lines))
|
d.grid = make([][]day18Cell, len(lines))
|
||||||
for i, line := range lines {
|
for i, line := range lines {
|
||||||
d.grid[i] = make([]day18Cell, len(line))
|
d.grid[i] = make([]day18Cell, len(line))
|
||||||
@ -109,8 +60,6 @@ func (d *Day18) Parse() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pathGrid = d.grid
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d Day18) Num() int {
|
func (d Day18) Num() int {
|
||||||
@ -140,128 +89,226 @@ func (d Day18) Draw(grid [][]day18Cell, keys, doors map[day18Vec]int, entrances
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Day18) pickUpKey(atPosition day18Vec, keys, doors map[day18Vec]int) {
|
func (d Day18) findAdjacentCells(inPos day18Vec, keys, doors map[day18Vec]int, grid [][]day18Cell) []u.Pair[rune, int] {
|
||||||
key, exists := keys[atPosition]
|
found := make([]u.Pair[rune, int], 0)
|
||||||
if !exists {
|
|
||||||
panic("tried to pick up a key that either doesn't exist or was already picked up")
|
getAdjacent := func(pos day18Vec) []day18Vec {
|
||||||
|
retAdjacent := make([]day18Vec, 0, len(day18AdjacentOffsets))
|
||||||
|
for _, off := range day18AdjacentOffsets {
|
||||||
|
offVec := day18Vec{X: pos.X + off.X, Y: pos.Y + off.Y}
|
||||||
|
if grid[offVec.Y][offVec.X] == day18CellWall {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
retAdjacent = append(retAdjacent, offVec)
|
||||||
}
|
}
|
||||||
|
|
||||||
var matchingDoorPos day18Vec
|
return retAdjacent
|
||||||
foundDoor := false
|
}
|
||||||
for doorPos, doorType := range doors {
|
|
||||||
if doorType == key {
|
queue := deque.NewDeque[u.Pair[int, day18Vec]]()
|
||||||
matchingDoorPos = doorPos
|
visited := []day18Vec{inPos}
|
||||||
foundDoor = true
|
for _, adjacent := range getAdjacent(inPos) {
|
||||||
|
queue.PushBack(u.Pair[int, day18Vec]{First: 1, Second: adjacent})
|
||||||
|
}
|
||||||
|
|
||||||
|
for !queue.IsEmpty() {
|
||||||
|
next := queue.PopFront()
|
||||||
|
|
||||||
|
if !u.ArrayContains(visited, next.Second) {
|
||||||
|
visited = append(visited, next.Second)
|
||||||
|
|
||||||
|
key, adjacentIsKey := keys[next.Second]
|
||||||
|
door, adjacentIsDoor := doors[next.Second]
|
||||||
|
if adjacentIsKey || adjacentIsDoor {
|
||||||
|
var rVal rune
|
||||||
|
if adjacentIsKey {
|
||||||
|
rVal = rune('a' + key)
|
||||||
|
} else if adjacentIsDoor {
|
||||||
|
rVal = rune('A' + door)
|
||||||
|
}
|
||||||
|
|
||||||
|
alreadyFound := false
|
||||||
|
for _, p := range found {
|
||||||
|
if p.First == rVal {
|
||||||
|
alreadyFound = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !alreadyFound {
|
||||||
delete(keys, atPosition)
|
found = append(found, u.Pair[rune, int]{First: rVal, Second: next.First})
|
||||||
// the last key has no door, so we should only fail this once. should.
|
continue
|
||||||
if foundDoor {
|
|
||||||
delete(doors, matchingDoorPos)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func astarPathContains(arr []astar.Pather, find astar.Pather) bool {
|
for _, neighbor := range getAdjacent(next.Second) {
|
||||||
for _, v := range arr {
|
if !u.ArrayContains(visited, neighbor) {
|
||||||
if v == find {
|
queue.PushBack(u.Pair[int, day18Vec]{First: next.First + 1, Second: neighbor})
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d Day18) findReachableKeys(pos day18Vec, keys, doors map[day18Vec]int) map[day18Vec]day18KeySearchResult {
|
|
||||||
pathDoors = doors
|
|
||||||
|
|
||||||
ret := make(map[day18Vec]day18KeySearchResult)
|
|
||||||
for keyPos, keyType := range keys {
|
|
||||||
path, distance, found := astar.Path(pos, keyPos)
|
|
||||||
if found {
|
|
||||||
ret[keyPos] = day18KeySearchResult{
|
|
||||||
keyType: keyType,
|
|
||||||
path: path,
|
|
||||||
distance: distance,
|
|
||||||
found: found,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(ret) < 2 {
|
return found
|
||||||
return ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
filtered := make(map[day18Vec]day18KeySearchResult)
|
type day18PriorityQueue struct {
|
||||||
// filter out keys that require you to walk through another key to get to them
|
distance int
|
||||||
for keyPos, key := range ret {
|
neighbor rune
|
||||||
eligible := true
|
}
|
||||||
for checkKeyPos := range ret {
|
type IntHeap []day18PriorityQueue
|
||||||
if keyPos == checkKeyPos {
|
|
||||||
|
func (h IntHeap) Len() int { return len(h) }
|
||||||
|
func (h IntHeap) Less(i, j int) bool { return h[i].distance < h[j].distance }
|
||||||
|
func (h IntHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
|
||||||
|
|
||||||
|
func (h *IntHeap) Push(x any) {
|
||||||
|
// Push and Pop use pointer receivers because they modify the slice's length,
|
||||||
|
// not just its contents.
|
||||||
|
*h = append(*h, x.(day18PriorityQueue))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *IntHeap) Pop() any {
|
||||||
|
old := *h
|
||||||
|
n := len(old)
|
||||||
|
x := old[n-1]
|
||||||
|
*h = old[0 : n-1]
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
type reachableKeysMemo struct {
|
||||||
|
pos rune
|
||||||
|
keysFound int
|
||||||
|
reachableKeys []u.Pair[rune, int]
|
||||||
|
}
|
||||||
|
|
||||||
|
var knownReachableKeys = make([]reachableKeysMemo, 0)
|
||||||
|
|
||||||
|
func (d Day18) reachableKeys(inPos rune, keysFound int, graph day18Graph) []u.Pair[rune, int] {
|
||||||
|
for _, m := range knownReachableKeys {
|
||||||
|
if m.pos == inPos &&
|
||||||
|
m.keysFound == keysFound {
|
||||||
|
return m.reachableKeys
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := make([]u.Pair[rune, int], 0)
|
||||||
|
distance := make(map[rune]int)
|
||||||
|
|
||||||
|
ih := make(IntHeap, 0)
|
||||||
|
|
||||||
|
for _, p := range graph[inPos] {
|
||||||
|
ih = append(ih, day18PriorityQueue{
|
||||||
|
distance: p.Second,
|
||||||
|
neighbor: p.First,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
heap.Init(&ih)
|
||||||
|
|
||||||
|
for ih.Len() > 0 {
|
||||||
|
node := heap.Pop(&ih).(day18PriorityQueue)
|
||||||
|
|
||||||
|
// it's a key and we haven't picked it up yet...
|
||||||
|
if node.neighbor >= 'a' && node.neighbor <= 'z' && (1<<int(node.neighbor-'a')&keysFound) == 0 {
|
||||||
|
ret = append(ret, u.Pair[rune, int]{First: node.neighbor, Second: node.distance})
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if astarPathContains(key.path, checkKeyPos) {
|
// it's a door but we don't have the key yet...
|
||||||
eligible = false
|
if node.neighbor >= 'A' && node.neighbor <= 'Z' && ((1<<int(node.neighbor-'A'))&keysFound) == 0 {
|
||||||
break
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range graph[node.neighbor] {
|
||||||
|
newDistance := node.distance + p.Second
|
||||||
|
if dist, exists := distance[p.First]; !exists || newDistance < dist {
|
||||||
|
distance[p.First] = newDistance
|
||||||
|
heap.Push(&ih, day18PriorityQueue{
|
||||||
|
distance: newDistance,
|
||||||
|
neighbor: p.First,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if eligible {
|
memo := reachableKeysMemo{
|
||||||
filtered[keyPos] = key
|
pos: inPos,
|
||||||
|
keysFound: keysFound,
|
||||||
|
reachableKeys: ret,
|
||||||
|
}
|
||||||
|
knownReachableKeys = append(knownReachableKeys, memo)
|
||||||
|
return memo.reachableKeys
|
||||||
|
}
|
||||||
|
|
||||||
|
type minStepsMemo struct {
|
||||||
|
pos string
|
||||||
|
keysToFind int
|
||||||
|
keysFound int
|
||||||
|
dist int
|
||||||
|
}
|
||||||
|
|
||||||
|
var knownMinimumSteps = make([]minStepsMemo, 0)
|
||||||
|
|
||||||
|
func (d Day18) minimumSteps(inPos string, keysToFind int, keysFound int, graph day18Graph) int {
|
||||||
|
for _, m := range knownMinimumSteps {
|
||||||
|
if m.pos == inPos &&
|
||||||
|
m.keysToFind == keysToFind &&
|
||||||
|
m.keysFound == keysFound {
|
||||||
|
return m.dist
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return filtered
|
if keysToFind == 0 {
|
||||||
}
|
return 0
|
||||||
|
|
||||||
func (d Day18) tryPickupKey(inKeys, inDoors map[day18Vec]int, keyPos day18Vec, keyResult day18KeySearchResult) (int, bool) {
|
|
||||||
keys := u.CopyMap(inKeys)
|
|
||||||
doors := u.CopyMap(inDoors)
|
|
||||||
|
|
||||||
d.pickUpKey(keyPos, keys, doors)
|
|
||||||
totalDist := int(keyResult.distance)
|
|
||||||
|
|
||||||
if len(keys) == 0 {
|
|
||||||
return totalDist, true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
best := math.Inf(1)
|
||||||
|
for _, item := range inPos {
|
||||||
|
for _, p := range d.reachableKeys(item, keysFound, graph) {
|
||||||
sb := strings.Builder{}
|
sb := strings.Builder{}
|
||||||
keyVals := u.MapValues(keys)
|
oldIdx := strings.IndexRune(inPos, item)
|
||||||
sort.Slice(keyVals, func(i, j int) bool { return keyVals[i] < keyVals[j] })
|
for i := range inPos {
|
||||||
for i, v := range keyVals {
|
if i == oldIdx {
|
||||||
if i > 0 {
|
sb.WriteRune(p.First)
|
||||||
sb.WriteRune(',')
|
} else {
|
||||||
|
sb.WriteByte(inPos[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newKeys := keysFound + (1 << (p.First - 'a'))
|
||||||
|
dist := p.Second
|
||||||
|
|
||||||
|
dist += d.minimumSteps(sb.String(), keysToFind-1, newKeys, graph)
|
||||||
|
|
||||||
|
if float64(dist) < best {
|
||||||
|
best = float64(dist)
|
||||||
}
|
}
|
||||||
sb.WriteString(fmt.Sprintf("%d", v))
|
|
||||||
}
|
}
|
||||||
vecToKey := u.Pair[day18Vec, string]{First: keyPos, Second: sb.String()}
|
|
||||||
if v, exists := d.knownPaths[vecToKey]; exists {
|
|
||||||
return totalDist + v.First, v.Second
|
|
||||||
}
|
}
|
||||||
|
|
||||||
shortestDist := math.MaxInt
|
memo := minStepsMemo{
|
||||||
reachableKeys := d.findReachableKeys(keyPos, keys, doors)
|
pos: inPos,
|
||||||
if len(reachableKeys) == 0 {
|
keysToFind: keysToFind,
|
||||||
// panic("no reachable keys")
|
keysFound: keysFound,
|
||||||
return math.MaxInt, false
|
dist: int(best),
|
||||||
}
|
}
|
||||||
// fmt.Println("in.", len(reachableKeys), "reachable keys")
|
knownMinimumSteps = append(knownMinimumSteps, memo)
|
||||||
done := false
|
return memo.dist
|
||||||
for reachableKeyPos, keyResult := range reachableKeys {
|
|
||||||
bestDist, finished := d.tryPickupKey(keys, doors, reachableKeyPos, keyResult)
|
|
||||||
if bestDist < shortestDist {
|
|
||||||
shortestDist = bestDist
|
|
||||||
done = finished
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// fmt.Println("out")
|
|
||||||
d.knownPaths[vecToKey] = u.Pair[int, bool]{First: shortestDist, Second: done}
|
|
||||||
|
|
||||||
totalDist += shortestDist
|
func (d Day18) buildGraph(pos []day18Vec, keys map[day18Vec]int, doors map[day18Vec]int, grid [][]day18Cell) day18Graph {
|
||||||
|
graph := make(day18Graph)
|
||||||
|
for i, p := range pos {
|
||||||
|
adjacent := d.findAdjacentCells(p, keys, doors, grid)
|
||||||
|
graph[rune('1'+i)] = adjacent
|
||||||
|
}
|
||||||
|
for keyPos, keyType := range keys {
|
||||||
|
graph[rune('a'+keyType)] = d.findAdjacentCells(keyPos, keys, doors, grid)
|
||||||
|
}
|
||||||
|
for doorPos, doorType := range doors {
|
||||||
|
graph[rune('A'+doorType)] = d.findAdjacentCells(doorPos, keys, doors, grid)
|
||||||
|
}
|
||||||
|
|
||||||
return totalDist, done
|
return graph
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d Day18) part2PatchMap(grid [][]day18Cell, entrance day18Vec) []day18Vec {
|
func (d Day18) part2PatchMap(grid [][]day18Cell, entrance day18Vec) []day18Vec {
|
||||||
@ -280,70 +327,31 @@ func (d Day18) part2PatchMap(grid [][]day18Cell, entrance day18Vec) []day18Vec {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Day18) Part1() string {
|
func (d *Day18) Part1() string {
|
||||||
pos := d.entrance
|
// fmt.Println("initial state:")
|
||||||
keys := u.CopyMap(d.keys)
|
// d.Draw(d.grid, d.keys, d.doors, d.entrance)
|
||||||
doors := u.CopyMap(d.doors)
|
|
||||||
|
|
||||||
fmt.Println("initial state:")
|
graph := d.buildGraph([]day18Vec{d.entrance}, d.keys, d.doors, d.grid)
|
||||||
d.Draw(d.grid, keys, doors, pos)
|
minSteps := d.minimumSteps("1", len(d.keys), 0, graph)
|
||||||
|
|
||||||
bestDist := math.MaxInt
|
return fmt.Sprintf("Total distance traveled: %s%d%s", u.TextBold, minSteps, u.TextReset)
|
||||||
|
|
||||||
reachableKeys := d.findReachableKeys(pos, keys, doors)
|
|
||||||
if len(reachableKeys) == 0 {
|
|
||||||
panic("failed to find a key to pick up")
|
|
||||||
}
|
|
||||||
for keyPos, keyResult := range reachableKeys {
|
|
||||||
shortestPath, _ := d.tryPickupKey(keys, doors, keyPos, keyResult)
|
|
||||||
if shortestPath < bestDist {
|
|
||||||
bestDist = shortestPath
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// fmt.Printf("Moving to pick up key %c (cost: %d, total cost so far: %d)\n", rune(closestKey+'a'), closestDist, totalDist)
|
|
||||||
// d.Draw(pos, keys, doors)
|
|
||||||
|
|
||||||
return fmt.Sprintf("Total distance traveled: %s%d%s", u.TextBold, bestDist, u.TextReset)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Day18) Part2() string {
|
func (d *Day18) Part2() string {
|
||||||
keys := u.CopyMap(d.keys)
|
// fmt.Println("initial state:")
|
||||||
doors := u.CopyMap(d.doors)
|
|
||||||
d.knownPaths = make(map[u.Pair[day18Vec, string]]u.Pair[int, bool])
|
|
||||||
|
|
||||||
fmt.Println("initial state:")
|
|
||||||
grid := make([][]day18Cell, len(d.grid))
|
grid := make([][]day18Cell, len(d.grid))
|
||||||
for i := range d.grid {
|
for i := range d.grid {
|
||||||
grid[i] = make([]day18Cell, len(d.grid[i]))
|
grid[i] = make([]day18Cell, len(d.grid[i]))
|
||||||
copy(grid[i], d.grid[i])
|
copy(grid[i], d.grid[i])
|
||||||
}
|
}
|
||||||
pathGrid = grid
|
|
||||||
|
|
||||||
entrances := d.part2PatchMap(grid, d.entrance)
|
entrances := d.part2PatchMap(grid, d.entrance)
|
||||||
d.Draw(grid, keys, doors, entrances...)
|
// d.Draw(grid, d.keys, d.doors, entrances...)
|
||||||
|
|
||||||
bestDist := math.MaxInt
|
knownMinimumSteps = make([]minStepsMemo, 0)
|
||||||
|
knownReachableKeys = make([]reachableKeysMemo, 0)
|
||||||
|
|
||||||
// reachableKeys := d.findReachableKeys(entrances[0], keys, doors)
|
graph := d.buildGraph(entrances, d.keys, d.doors, grid)
|
||||||
// if len(reachableKeys) == 0 {
|
minSteps := d.minimumSteps("1234", len(d.keys), 0, graph)
|
||||||
// panic("failed to find a key to pick up")
|
|
||||||
// }
|
|
||||||
done := true
|
|
||||||
for !done {
|
|
||||||
for _, entrance := range entrances {
|
|
||||||
reachableKeys := d.findReachableKeys(entrance, keys, doors)
|
|
||||||
for keyPos, keyResult := range reachableKeys {
|
|
||||||
shortestPath, finished := d.tryPickupKey(keys, doors, keyPos, keyResult)
|
|
||||||
if shortestPath < bestDist {
|
|
||||||
bestDist = shortestPath
|
|
||||||
done = finished
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// fmt.Printf("Moving to pick up key %c (cost: %d, total cost so far: %d)\n", rune(closestKey+'a'), closestDist, totalDist)
|
return fmt.Sprintf("Total distance traveled: %s%d%s", u.TextBold, minSteps, u.TextReset)
|
||||||
// d.Draw(pos, keys, doors)
|
|
||||||
|
|
||||||
return fmt.Sprintf("Total distance traveled: %s%d%s", u.TextBold, bestDist, u.TextReset)
|
|
||||||
}
|
}
|
||||||
|
2
go.mod
2
go.mod
@ -2,4 +2,4 @@ module parnic.com/aoc2019
|
|||||||
|
|
||||||
go 1.18
|
go 1.18
|
||||||
|
|
||||||
require github.com/beefsack/go-astar v0.0.0-20200827232313-4ecf9e304482
|
require github.com/edwingeng/deque/v2 v2.0.1
|
||||||
|
4
go.sum
4
go.sum
@ -1,2 +1,2 @@
|
|||||||
github.com/beefsack/go-astar v0.0.0-20200827232313-4ecf9e304482 h1:p4g4uok3+r6Tg6fxXEQUAcMAX/WdK6WhkQW9s0jaT7k=
|
github.com/edwingeng/deque/v2 v2.0.1 h1:yNEsA9tUImO0vyw2hmVGiK4nnkoxBQ8stMYpdVq2ZmQ=
|
||||||
github.com/beefsack/go-astar v0.0.0-20200827232313-4ecf9e304482/go.mod h1:Cu3t5VeqE8kXjUBeNXWQprfuaP5UCIc5ggGjgMx9KFc=
|
github.com/edwingeng/deque/v2 v2.0.1/go.mod h1:HukI8CQe9KDmZCcURPZRYVYjH79Zy2tIjTF9sN3Bgb0=
|
||||||
|
Reference in New Issue
Block a user