2018 Day 22 Complete

This commit is contained in:
Brian Buller 2019-11-07 11:53:26 -06:00
parent 7a00e2f9cc
commit 7c9742fb73
4 changed files with 225 additions and 123 deletions

19
2018/day22/coord.go Normal file
View File

@ -0,0 +1,19 @@
package main
type coord struct {
x, y int
}
func (c coord) neighbors() []coord {
n := []coord{
{c.x + 1, c.y},
{c.x, c.y + 1},
}
if c.x > 0 {
n = append(n, coord{c.x - 1, c.y})
}
if c.y > 0 {
n = append(n, coord{c.x, c.y - 1})
}
return n
}

View File

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

111
2018/day22/map.go Normal file
View File

@ -0,0 +1,111 @@
package main
import "fmt"
const (
geologicY = 16807
geologicX = 48271
caveModulo = 20183
)
const (
TypeRocky = 0
TypeWet = 1
TypeNarrow = 2
)
const (
ToolNone = 1 << iota
ToolTorch
ToolGear
)
type Map struct {
target coord
depth int
geologicIndicesCache map[int]map[int]int
erosionLevelsCache map[int]map[int]int
}
func NewMap(input []string) *Map {
m := Map{}
m.geologicIndicesCache = make(map[int]map[int]int)
m.erosionLevelsCache = make(map[int]map[int]int)
if len(input) != 2 {
panic("Invalid Input")
}
_, err := fmt.Sscanf(input[0], "depth: %d", &m.depth)
if err != nil {
panic(err)
}
_, err = fmt.Sscanf(input[1], "target: %d,%d", &m.target.x, &m.target.y)
if err != nil {
panic(err)
}
return &m
}
func (m *Map) GeologicIndex(x, y int) int {
if m.geologicIndicesCache[y] != nil {
if i, ok := m.geologicIndicesCache[y][x]; ok {
return i
}
} else {
m.geologicIndicesCache[y] = make(map[int]int)
}
switch {
case x == 0 && y == 0, x == m.target.x && y == m.target.y:
m.geologicIndicesCache[y][x] = 0
case y == 0:
m.geologicIndicesCache[y][x] = x * geologicY
case x == 0:
m.geologicIndicesCache[y][x] = y * geologicX
default:
m.geologicIndicesCache[y][x] = m.ErosionLevel(x-1, y) * m.ErosionLevel(x, y-1)
}
return m.geologicIndicesCache[y][x]
}
func (m *Map) ErosionLevel(x, y int) int {
if m.erosionLevelsCache[y] != nil {
if level, ok := m.erosionLevelsCache[y][x]; ok {
return level
}
} else {
m.erosionLevelsCache[y] = make(map[int]int)
}
m.erosionLevelsCache[y][x] = (m.GeologicIndex(x, y) + m.depth) % caveModulo
return m.erosionLevelsCache[y][x]
}
func (m Map) Type(x, y int) int {
return m.ErosionLevel(x, y) % 3
}
func (m Map) Neighbors(pos coord, equip int) []Item {
var n []Item
for _, c := range pos.neighbors() {
t := m.Type(c.x, c.y)
if equip&allowed(t) != 0 {
n = append(n, Item{pos: c, equip: equip, time: 1})
n = append(n, Item{pos: c, equip: equip ^ allowed(t), time: 8})
}
}
return n
}
func allowed(regionType int) int {
switch regionType {
case TypeRocky:
return ToolGear | ToolTorch
case TypeWet:
return ToolGear | ToolNone
case TypeNarrow:
return ToolTorch | ToolNone
default:
panic(fmt.Errorf("unknown region type: %d", regionType))
}
}

View File

@ -0,0 +1,48 @@
package main
type Item struct {
pos coord
equip int
time int
index int
}
type PriorityQueue []*Item
func (pq PriorityQueue) Len() int {
return len(pq)
}
func (pq PriorityQueue) Less(i, j int) bool {
return pq[i].time < pq[j].time
}
func (pq PriorityQueue) Swap(i, j int) {
pq[i], pq[j] = pq[j], pq[i]
pq[i].index = i
pq[j].index = j
}
func (pq *PriorityQueue) Push(x interface{}) {
item := x.(*Item)
item.index = len(*pq)
*pq = append(*pq, item)
}
func (pq *PriorityQueue) Pop() interface{} {
old := *pq
n := len(old)
item := old[n-1]
*pq = old[0 : n-1]
return item
}
func (i Item) Compare(o Item) int {
switch {
case i.time > o.time:
return 1
case i.time < o.time:
return -1
}
return 0
}