2018 Day 22 Complete
This commit is contained in:
parent
7a00e2f9cc
commit
7c9742fb73
19
2018/day22/coord.go
Normal file
19
2018/day22/coord.go
Normal 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
|
||||||
|
}
|
@ -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
111
2018/day22/map.go
Normal 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))
|
||||||
|
}
|
||||||
|
}
|
48
2018/day22/priorityqueue.go
Normal file
48
2018/day22/priorityqueue.go
Normal 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
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user