mirror of
https://github.com/Threnklyn/advent-of-code-go.git
synced 2026-06-05 19:59:25 +02:00
2018-day22: ugly nested memo calculator, heap datastrcture for djikstra queue
This commit is contained in:
@@ -0,0 +1,285 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/alexchao26/advent-of-code-go/datastructures"
|
||||||
|
"github.com/alexchao26/advent-of-code-go/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var part int
|
||||||
|
flag.IntVar(&part, "part", 1, "part 1 or 2")
|
||||||
|
flag.Parse()
|
||||||
|
fmt.Println("Running part", part)
|
||||||
|
|
||||||
|
if part == 1 {
|
||||||
|
ans := part1(util.ReadFile("./input.txt"))
|
||||||
|
util.CopyToClipboard(fmt.Sprintf("%v", ans))
|
||||||
|
fmt.Println("Output:", ans)
|
||||||
|
} else {
|
||||||
|
ans := part2(util.ReadFile("./input.txt"))
|
||||||
|
util.CopyToClipboard(fmt.Sprintf("%v", ans))
|
||||||
|
fmt.Println("Output:", ans)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func part1(input string) int {
|
||||||
|
depth, targetX, targetY := parseInput(input)
|
||||||
|
|
||||||
|
regionTypeCalculator := memoRegionTypeCalculator(depth, targetX, targetY)
|
||||||
|
var ans int
|
||||||
|
|
||||||
|
for x := 0; x <= targetX; x++ {
|
||||||
|
for y := 0; y <= targetY; y++ {
|
||||||
|
riskLevel := int(regionTypeCalculator(x, y)) % 3
|
||||||
|
ans += riskLevel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ans
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseInput(input string) (int, int, int) {
|
||||||
|
lines := strings.Split(input, "\n")
|
||||||
|
var depth, targetX, targetY int
|
||||||
|
_, err := fmt.Sscanf(lines[0], "depth: %d", &depth)
|
||||||
|
if err != nil {
|
||||||
|
panic("parsing depth from input" + err.Error())
|
||||||
|
}
|
||||||
|
_, err = fmt.Sscanf(lines[1], "target: %d,%d", &targetX, &targetY)
|
||||||
|
if err != nil {
|
||||||
|
panic("parsing targetX and targetY from input" + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return depth, targetX, targetY
|
||||||
|
}
|
||||||
|
|
||||||
|
func memoErosionLevelCalculator(depth, targetX, targetY int) func(x, y int) int {
|
||||||
|
// map to memoize results and prevent branch recursion
|
||||||
|
xyToErosion := map[[2]int]int{}
|
||||||
|
|
||||||
|
var closureGetErosionFunc func(x, y int) int
|
||||||
|
closureGetErosionFunc = func(x, y int) int {
|
||||||
|
coords := [2]int{x, y}
|
||||||
|
if e, ok := xyToErosion[coords]; ok {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
var geologicIndex int
|
||||||
|
if coords == [2]int{0, 0} || coords == [2]int{targetX, targetY} {
|
||||||
|
geologicIndex = 0
|
||||||
|
} else if y == 0 {
|
||||||
|
geologicIndex = x * 16807
|
||||||
|
} else if x == 0 {
|
||||||
|
geologicIndex = y * 48271
|
||||||
|
} else {
|
||||||
|
geologicIndex = closureGetErosionFunc(x-1, y) * closureGetErosionFunc(x, y-1)
|
||||||
|
}
|
||||||
|
erosionLevel := (geologicIndex + depth) % 20183
|
||||||
|
|
||||||
|
xyToErosion[coords] = erosionLevel
|
||||||
|
return erosionLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
return closureGetErosionFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func memoRegionTypeCalculator(depth, targetX, targetY int) func(x, y int) regionType {
|
||||||
|
erosionCalculator := memoErosionLevelCalculator(depth, targetX, targetY)
|
||||||
|
|
||||||
|
xyToRegionType := map[[2]int]regionType{}
|
||||||
|
var closureRegionFunc func(x, y int) regionType
|
||||||
|
closureRegionFunc = func(x, y int) regionType {
|
||||||
|
if x < 0 || y < 0 {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
coords := [2]int{x, y}
|
||||||
|
if rt, ok := xyToRegionType[coords]; ok {
|
||||||
|
return rt
|
||||||
|
}
|
||||||
|
erosion := erosionCalculator(x, y)
|
||||||
|
|
||||||
|
rt := regionType(erosion % 3)
|
||||||
|
xyToRegionType[coords] = rt
|
||||||
|
return rt
|
||||||
|
}
|
||||||
|
|
||||||
|
return closureRegionFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func part2(input string) int {
|
||||||
|
depth, targetX, targetY := parseInput(input)
|
||||||
|
regionCalculator := memoRegionTypeCalculator(depth, targetX, targetY)
|
||||||
|
|
||||||
|
heap := datastructures.NewMinHeap()
|
||||||
|
firstNode := node{
|
||||||
|
coords: [2]int{0, 0},
|
||||||
|
regionType: regionCalculator(0, 0),
|
||||||
|
equipped: torch,
|
||||||
|
totalTime: 0,
|
||||||
|
}
|
||||||
|
heap.Add(firstNode)
|
||||||
|
|
||||||
|
var eqCoordsToMinDist = map[equipmentType]map[[2]int]int{
|
||||||
|
neither: map[[2]int]int{},
|
||||||
|
climbingGear: map[[2]int]int{},
|
||||||
|
torch: map[[2]int]int{},
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentNode node
|
||||||
|
for !(currentNode.coords[0] == targetX && currentNode.coords[1] == targetY) {
|
||||||
|
currentNode = step(heap, eqCoordsToMinDist, regionCalculator)
|
||||||
|
}
|
||||||
|
|
||||||
|
if currentNode.regionType != rocky {
|
||||||
|
panic("target must be rocky")
|
||||||
|
}
|
||||||
|
|
||||||
|
if currentNode.equipped != torch {
|
||||||
|
finalTime := currentNode.totalTime + 7
|
||||||
|
return finalTime
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentNode.totalTime
|
||||||
|
}
|
||||||
|
|
||||||
|
type regionType int
|
||||||
|
type equipmentType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
rocky regionType = iota // climbing gear or torch
|
||||||
|
wet // neither or climbing gear
|
||||||
|
narrow // neither or torch
|
||||||
|
)
|
||||||
|
const (
|
||||||
|
neither equipmentType = iota
|
||||||
|
climbingGear
|
||||||
|
torch
|
||||||
|
)
|
||||||
|
|
||||||
|
type node struct {
|
||||||
|
coords [2]int
|
||||||
|
equipped equipmentType
|
||||||
|
regionType regionType
|
||||||
|
totalTime int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n node) Value() int {
|
||||||
|
return n.totalTime
|
||||||
|
}
|
||||||
|
|
||||||
|
func step(heap *datastructures.MinHeap, eqCoordsToMinDist map[equipmentType]map[[2]int]int, regionCalculator func(x, y int) regionType) node {
|
||||||
|
// remove node from heap, this will be returned at the end
|
||||||
|
minNodeInterface := heap.Remove()
|
||||||
|
if minNodeInterface == nil {
|
||||||
|
panic("Heap is empty, it shouldn't be empty...")
|
||||||
|
}
|
||||||
|
minNode, ok := minNodeInterface.(node)
|
||||||
|
if !ok {
|
||||||
|
panic("interface conversion error")
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we've already visited these coordinates with this equipment, check
|
||||||
|
// that the current time is
|
||||||
|
t, ok := eqCoordsToMinDist[minNode.equipped][minNode.coords]
|
||||||
|
if ok && t <= minNode.totalTime {
|
||||||
|
return node{}
|
||||||
|
}
|
||||||
|
eqCoordsToMinDist[minNode.equipped][minNode.coords] = minNode.totalTime
|
||||||
|
// fmt.Println("coords, region, equipped, time", minNode.coords, minNode.regionType, minNode.equipped, minNode.totalTime)
|
||||||
|
// fmt.Println(" Steps", minNode.steps)
|
||||||
|
// first check if movement is possible because this requires less time
|
||||||
|
// add those steps to the heap
|
||||||
|
nodesToTravelTo := getNextNodes(minNode, regionCalculator)
|
||||||
|
// fmt.Println("nodes to travel to", nodesToTravelTo)
|
||||||
|
for _, n := range nodesToTravelTo {
|
||||||
|
heap.Add(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// then try to change equipment if possible
|
||||||
|
newEq := getSwitchableEquipment(minNode)
|
||||||
|
|
||||||
|
// find travelable-to nodes with new equipment on & append if applicable
|
||||||
|
swappedEquipmentNode := node{
|
||||||
|
coords: minNode.coords,
|
||||||
|
equipped: newEq,
|
||||||
|
regionType: minNode.regionType,
|
||||||
|
totalTime: minNode.totalTime + 7, // swapping takes 7 minutes
|
||||||
|
}
|
||||||
|
// in scope of for loop, just to be safe...
|
||||||
|
nodesToTravelTo = getNextNodes(swappedEquipmentNode, regionCalculator)
|
||||||
|
|
||||||
|
for _, n := range nodesToTravelTo {
|
||||||
|
heap.Add(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
return minNode
|
||||||
|
}
|
||||||
|
|
||||||
|
var directions = [4][2]int{
|
||||||
|
{-1, 0},
|
||||||
|
{1, 0},
|
||||||
|
{0, -1},
|
||||||
|
{0, 1},
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns a slice of nodes that represent traveling in the four directions from
|
||||||
|
// the currentNode
|
||||||
|
// includes adding 1 to the time
|
||||||
|
func getNextNodes(currentNode node, regionCalculator func(x, y int) regionType) []node {
|
||||||
|
var nodesToTravelTo []node
|
||||||
|
for _, d := range directions {
|
||||||
|
nextX := currentNode.coords[0] + d[0]
|
||||||
|
nextY := currentNode.coords[1] + d[1]
|
||||||
|
nextCoord := [2]int{nextX, nextY}
|
||||||
|
nextRegionType := regionCalculator(nextX, nextY)
|
||||||
|
|
||||||
|
// ensure next region is not out of range
|
||||||
|
if nextRegionType != -1 {
|
||||||
|
// check if it can be traveled to
|
||||||
|
if canTravelTo(nextRegionType, currentNode.equipped) {
|
||||||
|
nodesToTravelTo = append(nodesToTravelTo, node{
|
||||||
|
coords: nextCoord,
|
||||||
|
equipped: currentNode.equipped,
|
||||||
|
regionType: nextRegionType,
|
||||||
|
totalTime: currentNode.totalTime + 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodesToTravelTo
|
||||||
|
}
|
||||||
|
|
||||||
|
var mapRegionsToToolsMap = map[regionType]map[equipmentType]bool{
|
||||||
|
rocky: map[equipmentType]bool{
|
||||||
|
climbingGear: true,
|
||||||
|
torch: true,
|
||||||
|
},
|
||||||
|
wet: map[equipmentType]bool{
|
||||||
|
climbingGear: true,
|
||||||
|
neither: true,
|
||||||
|
},
|
||||||
|
narrow: map[equipmentType]bool{
|
||||||
|
torch: true,
|
||||||
|
neither: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func canTravelTo(nextRegionType regionType, equippedTool equipmentType) bool {
|
||||||
|
return mapRegionsToToolsMap[nextRegionType][equippedTool]
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSwitchableEquipment(n node) equipmentType {
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
eq := equipmentType(i)
|
||||||
|
if eq != n.equipped && mapRegionsToToolsMap[n.regionType][eq] {
|
||||||
|
return eq
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
panic("must be one piece of equipment to switch to")
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/alexchao26/advent-of-code-go/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
var tests1 = []struct {
|
||||||
|
name string
|
||||||
|
want int
|
||||||
|
input string
|
||||||
|
}{
|
||||||
|
{"example", 114, "depth: 510\ntarget: 10,10"},
|
||||||
|
{"actual", 11972, util.ReadFile("input.txt")},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPart1(t *testing.T) {
|
||||||
|
for _, tt := range tests1 {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
startTime := time.Now()
|
||||||
|
if got := part1(tt.input); got != tt.want {
|
||||||
|
t.Errorf("part1() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
t.Logf("Run time: %v", time.Since(startTime))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var tests2 = []struct {
|
||||||
|
name string
|
||||||
|
want int
|
||||||
|
input string
|
||||||
|
}{
|
||||||
|
{"example", 45, "depth: 510\ntarget: 10,10"},
|
||||||
|
{"actual", 1092, util.ReadFile("input.txt")},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPart2(t *testing.T) {
|
||||||
|
for _, tt := range tests2 {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
startTime := time.Now()
|
||||||
|
if got := part2(tt.input); got != tt.want {
|
||||||
|
t.Errorf("part2() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
t.Logf("Run time: %v", time.Since(startTime))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_memoRegionTypeCalculator(t *testing.T) {
|
||||||
|
depth, targetX, targetY := 510, 10, 10
|
||||||
|
type args struct {
|
||||||
|
x int
|
||||||
|
y int
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want regionType
|
||||||
|
}{
|
||||||
|
{"0,0", args{0, 0}, rocky},
|
||||||
|
{"1,0", args{1, 0}, wet},
|
||||||
|
{"0,1", args{0, 1}, rocky},
|
||||||
|
{"1,1", args{1, 1}, narrow},
|
||||||
|
{"10,10", args{10, 10}, rocky},
|
||||||
|
{"11,10", args{11, 10}, wet},
|
||||||
|
{"0,6", args{0, 6}, narrow},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
memoFunc := memoRegionTypeCalculator(depth, targetX, targetY)
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := memoFunc(tt.args.x, tt.args.y); !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("memoRegionTypeCalculator() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable
+373
@@ -0,0 +1,373 @@
|
|||||||
|
|
||||||
|
--- Day 22: Mode Maze ---
|
||||||
|
This is it, your final stop: the year -483. It's snowing and dark outside; the only light you can see is coming from a small cottage in the distance. You make your way there and knock on the door.
|
||||||
|
|
||||||
|
|
||||||
|
A portly man with a large, white beard answers the door and invites you inside. For someone living near the North Pole in -483, he must not get many visitors, but he doesn't act surprised to see you. Instead, he offers you some milk and cookies.
|
||||||
|
|
||||||
|
|
||||||
|
After talking for a while, he asks a favor of you. His friend hasn't come back in a few hours, and he's not sure where he is. Scanning the region briefly, you discover one life signal in a cave system nearby; his friend must have taken shelter there. The man asks if you can go there to retrieve his friend.
|
||||||
|
|
||||||
|
|
||||||
|
The cave is divided into square regions which are either dominantly rocky, narrow, or wet (called its type). Each region occupies exactly one coordinate in X,Y format where X and Y are integers and zero or greater. (Adjacent regions can be the same type.)
|
||||||
|
|
||||||
|
|
||||||
|
The scan (your puzzle input) is not very detailed: it only reveals the depth of the cave system and the coordinates of the target. However, it does not reveal the type of each region. The mouth of the cave is at 0,0.
|
||||||
|
|
||||||
|
|
||||||
|
The man explains that due to the unusual geology in the area, there is a method to determine any region's type based on its erosion level. The erosion level of a region can be determined from its geologic index. The geologic index can be determined using the first rule that applies from the list below:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
The region at 0,0 (the mouth of the cave) has a geologic index of 0.
|
||||||
|
The region at the coordinates of the target has a geologic index of 0.
|
||||||
|
If the region's Y coordinate is 0, the geologic index is its X coordinate times 16807.
|
||||||
|
If the region's X coordinate is 0, the geologic index is its Y coordinate times 48271.
|
||||||
|
Otherwise, the region's geologic index is the result of multiplying the erosion levels of the regions at X-1,Y and X,Y-1.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
A region's erosion level is its geologic index plus the cave system's depth, all modulo 20183. Then:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
If the erosion level modulo 3 is 0, the region's type is rocky.
|
||||||
|
If the erosion level modulo 3 is 1, the region's type is wet.
|
||||||
|
If the erosion level modulo 3 is 2, the region's type is narrow.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
For example, suppose the cave system's depth is 510 and the target's coordinates are 10,10. Using % to represent the modulo operator, the cavern would look as follows:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
At 0,0, the geologic index is 0. The erosion level is (0 + 510) % 20183 = 510. The type is 510 % 3 = 0, rocky.
|
||||||
|
At 1,0, because the Y coordinate is 0, the geologic index is 1 * 16807 = 16807. The erosion level is (16807 + 510) % 20183 = 17317. The type is 17317 % 3 = 1, wet.
|
||||||
|
At 0,1, because the X coordinate is 0, the geologic index is 1 * 48271 = 48271. The erosion level is (48271 + 510) % 20183 = 8415. The type is 8415 % 3 = 0, rocky.
|
||||||
|
At 1,1, neither coordinate is 0 and it is not the coordinate of the target, so the geologic index is the erosion level of 0,1 (8415) times the erosion level of 1,0 (17317), 8415 * 17317 = 145722555. The erosion level is (145722555 + 510) % 20183 = 1805. The type is 1805 % 3 = 2, narrow.
|
||||||
|
At 10,10, because they are the target's coordinates, the geologic index is 0. The erosion level is (0 + 510) % 20183 = 510. The type is 510 % 3 = 0, rocky.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Drawing this same cave system with rocky as ., wet as =, narrow as |, the mouth as M, the target as T, with 0,0 in the top-left corner, X increasing to the right, and Y increasing downward, the top-left corner of the map looks like this:
|
||||||
|
|
||||||
|
|
||||||
|
M=.|=.|.|=.|=|=.
|
||||||
|
.|=|=|||..|.=...
|
||||||
|
.==|....||=..|==
|
||||||
|
=.|....|.==.|==.
|
||||||
|
=|..==...=.|==..
|
||||||
|
=||.=.=||=|=..|=
|
||||||
|
|.=.===|||..=..|
|
||||||
|
|..==||=.|==|===
|
||||||
|
.=..===..=|.|||.
|
||||||
|
.======|||=|=.|=
|
||||||
|
.===|=|===T===||
|
||||||
|
=|||...|==..|=.|
|
||||||
|
=.=|=.=..=.||==|
|
||||||
|
||=|=...|==.=|==
|
||||||
|
|=.=||===.|||===
|
||||||
|
||.|==.|.|.||=||
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Before you go in, you should determine the risk level of the area. For the rectangle that has a top-left corner of region 0,0 and a bottom-right corner of the region containing the target, add up the risk level of each individual region: 0 for rocky regions, 1 for wet regions, and 2 for narrow regions.
|
||||||
|
|
||||||
|
|
||||||
|
In the cave system above, because the mouth is at 0,0 and the target is at 10,10, adding up the risk level of all regions with an X coordinate from 0 to 10 and a Y coordinate from 0 to 10, this total is 114.
|
||||||
|
|
||||||
|
|
||||||
|
What is the total risk level for the smallest rectangle that includes 0,0 and the target's coordinates?
|
||||||
|
|
||||||
|
|
||||||
|
--- Part Two ---
|
||||||
|
Okay, it's time to go rescue the man's friend.
|
||||||
|
|
||||||
|
|
||||||
|
As you leave, he hands you some tools: a torch and some climbing gear. You can't equip both tools at once, but you can choose to use neither.
|
||||||
|
|
||||||
|
|
||||||
|
Tools can only be used in certain regions:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
In rocky regions, you can use the climbing gear or the torch. You cannot use neither (you'll likely slip and fall).
|
||||||
|
In wet regions, you can use the climbing gear or neither tool. You cannot use the torch (if it gets wet, you won't have a light source).
|
||||||
|
In narrow regions, you can use the torch or neither tool. You cannot use the climbing gear (it's too bulky to fit).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
You start at 0,0 (the mouth of the cave) with the torch equipped and must reach the target coordinates as quickly as possible. The regions with negative X or Y are solid rock and cannot be traversed. The fastest route might involve entering regions beyond the X or Y coordinate of the target.
|
||||||
|
|
||||||
|
|
||||||
|
You can move to an adjacent region (up, down, left, or right; never diagonally) if your currently equipped tool allows you to enter that region. Moving to an adjacent region takes one minute. (For example, if you have the torch equipped, you can move between rocky and narrow regions, but cannot enter wet regions.)
|
||||||
|
|
||||||
|
|
||||||
|
You can change your currently equipped tool or put both away if your new equipment would be valid for your current region. Switching to using the climbing gear, torch, or neither always takes seven minutes, regardless of which tools you start with. (For example, if you are in a rocky region, you can switch from the torch to the climbing gear, but you cannot switch to neither.)
|
||||||
|
|
||||||
|
|
||||||
|
Finally, once you reach the target, you need the torch equipped before you can find him in the dark. The target is always in a rocky region, so if you arrive there with climbing gear equipped, you will need to spend seven minutes switching to your torch.
|
||||||
|
|
||||||
|
|
||||||
|
For example, using the same cave system as above, starting in the top left corner (0,0) and moving to the bottom right corner (the target, 10,10) as quickly as possible, one possible route is as follows, with your current position marked X:
|
||||||
|
|
||||||
|
|
||||||
|
Initially:
|
||||||
|
X=.|=.|.|=.|=|=.
|
||||||
|
.|=|=|||..|.=...
|
||||||
|
.==|....||=..|==
|
||||||
|
=.|....|.==.|==.
|
||||||
|
=|..==...=.|==..
|
||||||
|
=||.=.=||=|=..|=
|
||||||
|
|.=.===|||..=..|
|
||||||
|
|..==||=.|==|===
|
||||||
|
.=..===..=|.|||.
|
||||||
|
.======|||=|=.|=
|
||||||
|
.===|=|===T===||
|
||||||
|
=|||...|==..|=.|
|
||||||
|
=.=|=.=..=.||==|
|
||||||
|
||=|=...|==.=|==
|
||||||
|
|=.=||===.|||===
|
||||||
|
||.|==.|.|.||=||
|
||||||
|
|
||||||
|
Down:
|
||||||
|
M=.|=.|.|=.|=|=.
|
||||||
|
X|=|=|||..|.=...
|
||||||
|
.==|....||=..|==
|
||||||
|
=.|....|.==.|==.
|
||||||
|
=|..==...=.|==..
|
||||||
|
=||.=.=||=|=..|=
|
||||||
|
|.=.===|||..=..|
|
||||||
|
|..==||=.|==|===
|
||||||
|
.=..===..=|.|||.
|
||||||
|
.======|||=|=.|=
|
||||||
|
.===|=|===T===||
|
||||||
|
=|||...|==..|=.|
|
||||||
|
=.=|=.=..=.||==|
|
||||||
|
||=|=...|==.=|==
|
||||||
|
|=.=||===.|||===
|
||||||
|
||.|==.|.|.||=||
|
||||||
|
|
||||||
|
Right:
|
||||||
|
M=.|=.|.|=.|=|=.
|
||||||
|
.X=|=|||..|.=...
|
||||||
|
.==|....||=..|==
|
||||||
|
=.|....|.==.|==.
|
||||||
|
=|..==...=.|==..
|
||||||
|
=||.=.=||=|=..|=
|
||||||
|
|.=.===|||..=..|
|
||||||
|
|..==||=.|==|===
|
||||||
|
.=..===..=|.|||.
|
||||||
|
.======|||=|=.|=
|
||||||
|
.===|=|===T===||
|
||||||
|
=|||...|==..|=.|
|
||||||
|
=.=|=.=..=.||==|
|
||||||
|
||=|=...|==.=|==
|
||||||
|
|=.=||===.|||===
|
||||||
|
||.|==.|.|.||=||
|
||||||
|
|
||||||
|
Switch from using the torch to neither tool:
|
||||||
|
M=.|=.|.|=.|=|=.
|
||||||
|
.X=|=|||..|.=...
|
||||||
|
.==|....||=..|==
|
||||||
|
=.|....|.==.|==.
|
||||||
|
=|..==...=.|==..
|
||||||
|
=||.=.=||=|=..|=
|
||||||
|
|.=.===|||..=..|
|
||||||
|
|..==||=.|==|===
|
||||||
|
.=..===..=|.|||.
|
||||||
|
.======|||=|=.|=
|
||||||
|
.===|=|===T===||
|
||||||
|
=|||...|==..|=.|
|
||||||
|
=.=|=.=..=.||==|
|
||||||
|
||=|=...|==.=|==
|
||||||
|
|=.=||===.|||===
|
||||||
|
||.|==.|.|.||=||
|
||||||
|
|
||||||
|
Right 3:
|
||||||
|
M=.|=.|.|=.|=|=.
|
||||||
|
.|=|X|||..|.=...
|
||||||
|
.==|....||=..|==
|
||||||
|
=.|....|.==.|==.
|
||||||
|
=|..==...=.|==..
|
||||||
|
=||.=.=||=|=..|=
|
||||||
|
|.=.===|||..=..|
|
||||||
|
|..==||=.|==|===
|
||||||
|
.=..===..=|.|||.
|
||||||
|
.======|||=|=.|=
|
||||||
|
.===|=|===T===||
|
||||||
|
=|||...|==..|=.|
|
||||||
|
=.=|=.=..=.||==|
|
||||||
|
||=|=...|==.=|==
|
||||||
|
|=.=||===.|||===
|
||||||
|
||.|==.|.|.||=||
|
||||||
|
|
||||||
|
Switch from using neither tool to the climbing gear:
|
||||||
|
M=.|=.|.|=.|=|=.
|
||||||
|
.|=|X|||..|.=...
|
||||||
|
.==|....||=..|==
|
||||||
|
=.|....|.==.|==.
|
||||||
|
=|..==...=.|==..
|
||||||
|
=||.=.=||=|=..|=
|
||||||
|
|.=.===|||..=..|
|
||||||
|
|..==||=.|==|===
|
||||||
|
.=..===..=|.|||.
|
||||||
|
.======|||=|=.|=
|
||||||
|
.===|=|===T===||
|
||||||
|
=|||...|==..|=.|
|
||||||
|
=.=|=.=..=.||==|
|
||||||
|
||=|=...|==.=|==
|
||||||
|
|=.=||===.|||===
|
||||||
|
||.|==.|.|.||=||
|
||||||
|
|
||||||
|
Down 7:
|
||||||
|
M=.|=.|.|=.|=|=.
|
||||||
|
.|=|=|||..|.=...
|
||||||
|
.==|....||=..|==
|
||||||
|
=.|....|.==.|==.
|
||||||
|
=|..==...=.|==..
|
||||||
|
=||.=.=||=|=..|=
|
||||||
|
|.=.===|||..=..|
|
||||||
|
|..==||=.|==|===
|
||||||
|
.=..X==..=|.|||.
|
||||||
|
.======|||=|=.|=
|
||||||
|
.===|=|===T===||
|
||||||
|
=|||...|==..|=.|
|
||||||
|
=.=|=.=..=.||==|
|
||||||
|
||=|=...|==.=|==
|
||||||
|
|=.=||===.|||===
|
||||||
|
||.|==.|.|.||=||
|
||||||
|
|
||||||
|
Right:
|
||||||
|
M=.|=.|.|=.|=|=.
|
||||||
|
.|=|=|||..|.=...
|
||||||
|
.==|....||=..|==
|
||||||
|
=.|....|.==.|==.
|
||||||
|
=|..==...=.|==..
|
||||||
|
=||.=.=||=|=..|=
|
||||||
|
|.=.===|||..=..|
|
||||||
|
|..==||=.|==|===
|
||||||
|
.=..=X=..=|.|||.
|
||||||
|
.======|||=|=.|=
|
||||||
|
.===|=|===T===||
|
||||||
|
=|||...|==..|=.|
|
||||||
|
=.=|=.=..=.||==|
|
||||||
|
||=|=...|==.=|==
|
||||||
|
|=.=||===.|||===
|
||||||
|
||.|==.|.|.||=||
|
||||||
|
|
||||||
|
Down 3:
|
||||||
|
M=.|=.|.|=.|=|=.
|
||||||
|
.|=|=|||..|.=...
|
||||||
|
.==|....||=..|==
|
||||||
|
=.|....|.==.|==.
|
||||||
|
=|..==...=.|==..
|
||||||
|
=||.=.=||=|=..|=
|
||||||
|
|.=.===|||..=..|
|
||||||
|
|..==||=.|==|===
|
||||||
|
.=..===..=|.|||.
|
||||||
|
.======|||=|=.|=
|
||||||
|
.===|=|===T===||
|
||||||
|
=|||.X.|==..|=.|
|
||||||
|
=.=|=.=..=.||==|
|
||||||
|
||=|=...|==.=|==
|
||||||
|
|=.=||===.|||===
|
||||||
|
||.|==.|.|.||=||
|
||||||
|
|
||||||
|
Right:
|
||||||
|
M=.|=.|.|=.|=|=.
|
||||||
|
.|=|=|||..|.=...
|
||||||
|
.==|....||=..|==
|
||||||
|
=.|....|.==.|==.
|
||||||
|
=|..==...=.|==..
|
||||||
|
=||.=.=||=|=..|=
|
||||||
|
|.=.===|||..=..|
|
||||||
|
|..==||=.|==|===
|
||||||
|
.=..===..=|.|||.
|
||||||
|
.======|||=|=.|=
|
||||||
|
.===|=|===T===||
|
||||||
|
=|||..X|==..|=.|
|
||||||
|
=.=|=.=..=.||==|
|
||||||
|
||=|=...|==.=|==
|
||||||
|
|=.=||===.|||===
|
||||||
|
||.|==.|.|.||=||
|
||||||
|
|
||||||
|
Down:
|
||||||
|
M=.|=.|.|=.|=|=.
|
||||||
|
.|=|=|||..|.=...
|
||||||
|
.==|....||=..|==
|
||||||
|
=.|....|.==.|==.
|
||||||
|
=|..==...=.|==..
|
||||||
|
=||.=.=||=|=..|=
|
||||||
|
|.=.===|||..=..|
|
||||||
|
|..==||=.|==|===
|
||||||
|
.=..===..=|.|||.
|
||||||
|
.======|||=|=.|=
|
||||||
|
.===|=|===T===||
|
||||||
|
=|||...|==..|=.|
|
||||||
|
=.=|=.X..=.||==|
|
||||||
|
||=|=...|==.=|==
|
||||||
|
|=.=||===.|||===
|
||||||
|
||.|==.|.|.||=||
|
||||||
|
|
||||||
|
Right 4:
|
||||||
|
M=.|=.|.|=.|=|=.
|
||||||
|
.|=|=|||..|.=...
|
||||||
|
.==|....||=..|==
|
||||||
|
=.|....|.==.|==.
|
||||||
|
=|..==...=.|==..
|
||||||
|
=||.=.=||=|=..|=
|
||||||
|
|.=.===|||..=..|
|
||||||
|
|..==||=.|==|===
|
||||||
|
.=..===..=|.|||.
|
||||||
|
.======|||=|=.|=
|
||||||
|
.===|=|===T===||
|
||||||
|
=|||...|==..|=.|
|
||||||
|
=.=|=.=..=X||==|
|
||||||
|
||=|=...|==.=|==
|
||||||
|
|=.=||===.|||===
|
||||||
|
||.|==.|.|.||=||
|
||||||
|
|
||||||
|
Up 2:
|
||||||
|
M=.|=.|.|=.|=|=.
|
||||||
|
.|=|=|||..|.=...
|
||||||
|
.==|....||=..|==
|
||||||
|
=.|....|.==.|==.
|
||||||
|
=|..==...=.|==..
|
||||||
|
=||.=.=||=|=..|=
|
||||||
|
|.=.===|||..=..|
|
||||||
|
|..==||=.|==|===
|
||||||
|
.=..===..=|.|||.
|
||||||
|
.======|||=|=.|=
|
||||||
|
.===|=|===X===||
|
||||||
|
=|||...|==..|=.|
|
||||||
|
=.=|=.=..=.||==|
|
||||||
|
||=|=...|==.=|==
|
||||||
|
|=.=||===.|||===
|
||||||
|
||.|==.|.|.||=||
|
||||||
|
|
||||||
|
Switch from using the climbing gear to the torch:
|
||||||
|
M=.|=.|.|=.|=|=.
|
||||||
|
.|=|=|||..|.=...
|
||||||
|
.==|....||=..|==
|
||||||
|
=.|....|.==.|==.
|
||||||
|
=|..==...=.|==..
|
||||||
|
=||.=.=||=|=..|=
|
||||||
|
|.=.===|||..=..|
|
||||||
|
|..==||=.|==|===
|
||||||
|
.=..===..=|.|||.
|
||||||
|
.======|||=|=.|=
|
||||||
|
.===|=|===X===||
|
||||||
|
=|||...|==..|=.|
|
||||||
|
=.=|=.=..=.||==|
|
||||||
|
||=|=...|==.=|==
|
||||||
|
|=.=||===.|||===
|
||||||
|
||.|==.|.|.||=||
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
This is tied with other routes as the fastest way to reach the target: 45 minutes. In it, 21 minutes are spent switching tools (three times, seven minutes each) and the remaining 24 minutes are spent moving.
|
||||||
|
|
||||||
|
|
||||||
|
What is the fewest number of minutes you can take to reach the target?
|
||||||
|
|
||||||
@@ -7,13 +7,13 @@ type MinHeap struct {
|
|||||||
|
|
||||||
// NewMinHeap initializes a heap with a closerToRootFunction that simply
|
// NewMinHeap initializes a heap with a closerToRootFunction that simply
|
||||||
// returns true if the first arg is smaller than the second
|
// returns true if the first arg is smaller than the second
|
||||||
func NewMinHeap() MinHeap {
|
func NewMinHeap() *MinHeap {
|
||||||
nestedHeap := heap{
|
nestedHeap := heap{
|
||||||
closerToRoot: func(val1, val2 int) bool {
|
closerToRoot: func(val1, val2 int) bool {
|
||||||
return val1 < val2
|
return val1 < val2
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return MinHeap{nestedHeap}
|
return &MinHeap{nestedHeap}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MaxHeap is an implementation of max heap
|
// MaxHeap is an implementation of max heap
|
||||||
@@ -23,13 +23,13 @@ type MaxHeap struct {
|
|||||||
|
|
||||||
// NewMaxHeap initializes a heap with a closerToRootFunction that simply
|
// NewMaxHeap initializes a heap with a closerToRootFunction that simply
|
||||||
// returns true if the first arg is larger than the second
|
// returns true if the first arg is larger than the second
|
||||||
func NewMaxHeap() MaxHeap {
|
func NewMaxHeap() *MaxHeap {
|
||||||
nestedHeap := heap{
|
nestedHeap := heap{
|
||||||
closerToRoot: func(val1, val2 int) bool {
|
closerToRoot: func(val1, val2 int) bool {
|
||||||
return val1 > val2
|
return val1 > val2
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return MaxHeap{nestedHeap}
|
return &MaxHeap{nestedHeap}
|
||||||
}
|
}
|
||||||
|
|
||||||
// heap contains a slice of heapNodes
|
// heap contains a slice of heapNodes
|
||||||
|
|||||||
Reference in New Issue
Block a user