2018 Day 22 Complete
This commit is contained in:
@@ -2,141 +2,74 @@ package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"container/heap"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var depth int
|
||||
var target *Pos
|
||||
const bailFactor = 8
|
||||
|
||||
func main() {
|
||||
input := stdinToStringSlice()
|
||||
reg, err := regexp.Compile("[^0-9]+")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
depth = Atoi(reg.ReplaceAllString(input[0], ""))
|
||||
tgt := strings.Trim(input[1], "target: ")
|
||||
tgtPts := strings.Split(tgt, ",")
|
||||
target = NewPos(Atoi(tgtPts[0]), Atoi(tgtPts[1]), depth)
|
||||
|
||||
inp := StdinToStringSlice()
|
||||
fmt.Printf("# Part 1\nRisk level: %d\n", RiskLevel(inp))
|
||||
fmt.Printf("# Part 2\nRescue minutes: %d\n", Rescue(inp))
|
||||
}
|
||||
|
||||
func part1() {
|
||||
c := BuildCave(target)
|
||||
var risk int
|
||||
for y := 0; y <= target.y; y++ {
|
||||
for x := 0; x <= target.x; x++ {
|
||||
risk += c.risk(x, y, depth)
|
||||
func RiskLevel(input []string) int {
|
||||
m := NewMap(input)
|
||||
sum := 0
|
||||
for y := 0; y <= m.target.y; y++ {
|
||||
for x := 0; x <= m.target.x; x++ {
|
||||
sum += m.Type(x, y)
|
||||
}
|
||||
}
|
||||
fmt.Println("= Part 1 =")
|
||||
fmt.Println(risk)
|
||||
return sum
|
||||
}
|
||||
|
||||
func part2() {
|
||||
// Find shortest path form 0,0 to target
|
||||
// taking into account cost of changing gear
|
||||
// when necessary
|
||||
|
||||
}
|
||||
|
||||
type Cave struct {
|
||||
positions map[string]*Pos
|
||||
}
|
||||
|
||||
func BuildCave(target *Pos) *Cave {
|
||||
c := &Cave{
|
||||
positions: make(map[string]*Pos),
|
||||
func Rescue(input []string) int {
|
||||
m := NewMap(input)
|
||||
queue := PriorityQueue{
|
||||
&Item{pos: coord{0, 0}, time: 0, equip: ToolTorch},
|
||||
}
|
||||
c.addPos(target)
|
||||
return c
|
||||
}
|
||||
heap.Init(&queue)
|
||||
|
||||
func (c *Cave) addPos(p *Pos) {
|
||||
c.positions[p.string()] = p
|
||||
}
|
||||
|
||||
func (c *Cave) getPos(x, y, z int) *Pos {
|
||||
var p *Pos
|
||||
var ok bool
|
||||
if p, ok = c.positions[c.buildKey(x, y, z)]; !ok {
|
||||
p = NewPos(x, y, z)
|
||||
c.addPos(p)
|
||||
type step struct {
|
||||
coord coord
|
||||
equip int
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func (c *Cave) buildKey(x, y, z int) string {
|
||||
return fmt.Sprintf(
|
||||
"(%d,%d,%d)",
|
||||
x, y, z,
|
||||
)
|
||||
}
|
||||
distances := map[step]int{
|
||||
step{coord: coord{0, 0}, equip: ToolTorch}: 0,
|
||||
}
|
||||
|
||||
func (c *Cave) getGeoIndex(x, y, z int) int {
|
||||
p := c.getPos(x, y, z)
|
||||
if p.geo < 0 {
|
||||
if x == 0 && y == 0 {
|
||||
p.geo = 0
|
||||
} else if x == target.x && y == target.y {
|
||||
p.geo = 0
|
||||
} else if y == 0 {
|
||||
p.geo = x * 16807
|
||||
} else if x == 0 {
|
||||
p.geo = y * 48271
|
||||
} else {
|
||||
p.geo = c.erosion(x-1, y, z) * c.erosion(x, y-1, z)
|
||||
for len(queue) > 0 {
|
||||
item := (heap.Pop(&queue)).(*Item)
|
||||
if item.pos.x == m.target.x && item.pos.y == m.target.y && item.equip == ToolTorch {
|
||||
return item.time
|
||||
}
|
||||
|
||||
// Check if we're wandering too far away
|
||||
if item.pos.x > bailFactor*m.target.x || item.pos.y > bailFactor*m.target.y {
|
||||
continue
|
||||
}
|
||||
|
||||
if t, ok := distances[step{coord: item.pos, equip: item.equip}]; ok && t < item.time {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, n := range m.Neighbors(item.pos, item.equip) {
|
||||
d := step{coord: n.pos, equip: n.equip}
|
||||
if t, ok := distances[step{coord: n.pos, equip: n.equip}]; !ok || item.time+n.time < t {
|
||||
distances[d] = item.time + n.time
|
||||
heap.Push(&queue, &Item{pos: n.pos, time: item.time + n.time, equip: n.equip})
|
||||
}
|
||||
}
|
||||
}
|
||||
return p.geo
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *Cave) erosion(x, y, z int) int {
|
||||
p := c.getPos(x, y, z)
|
||||
if p.erosion < 0 {
|
||||
p.erosion = (c.getGeoIndex(x, y, z) + z) % 20183
|
||||
}
|
||||
return p.erosion
|
||||
}
|
||||
|
||||
func (c *Cave) risk(x, y, z int) int {
|
||||
return c.erosion(x, y, z) % 3
|
||||
}
|
||||
|
||||
type Pos struct {
|
||||
x, y, z int
|
||||
geo, erosion int
|
||||
}
|
||||
|
||||
func NewPos(x, y, z int) *Pos {
|
||||
r := &Pos{
|
||||
x: x,
|
||||
y: y,
|
||||
z: z,
|
||||
geo: -1,
|
||||
erosion: -1,
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (p *Pos) equals(n *Pos) bool {
|
||||
return p.x == n.x && p.y == n.y && p.z == n.z
|
||||
}
|
||||
|
||||
func (p *Pos) string() string {
|
||||
return fmt.Sprintf(
|
||||
"(%d,%d,%d)",
|
||||
p.x, p.y, p.z,
|
||||
)
|
||||
}
|
||||
|
||||
func stdinToStringSlice() []string {
|
||||
func StdinToStringSlice() []string {
|
||||
var input []string
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
for scanner.Scan() {
|
||||
@@ -144,12 +77,3 @@ func stdinToStringSlice() []string {
|
||||
}
|
||||
return input
|
||||
}
|
||||
|
||||
func Atoi(i string) int {
|
||||
var ret int
|
||||
var err error
|
||||
if ret, err = strconv.Atoi(i); err != nil {
|
||||
log.Fatal("Invalid Atoi: " + i)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user