112 lines
2.3 KiB
Go
112 lines
2.3 KiB
Go
|
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))
|
||
|
}
|
||
|
}
|