-
Notifications
You must be signed in to change notification settings - Fork 0
/
day22.nim
108 lines (84 loc) · 2.65 KB
/
day22.nim
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
import heapqueue
const
depth = 6084
xLim = 30 # manually
yLim = 710 # tweaked
type
Position = tuple[x, y: int]
RegionType = enum
rocky, wet, narrow
Tool = enum
# forbidden tool for each RegionType:
neither, torch, climbing
PosDetails = tuple
pos: Position
tool: Tool
time: int
Grid[T] = array[xLim, array[yLim, T]]
template `[]`(m: typed, p: Position): untyped = m[p.x][p.y]
template `[]=`(m: typed, p: Position, v: typed): untyped = m[p.x][p.y] = v
proc `<`(a, b: PosDetails): bool =
# Calculating priority for heapqueue.
# We're going mostly in y-direction, x can be ignored.
a.time - a.pos.y < b.time - b.pos.y
let target = (x: 14, y: 709)
var
eroMap: Grid[int]
regMap: Grid[RegionType]
proc erosionLevel(p: Position, geoInd: int): int =
(geoInd + depth) mod 20183
proc tileType(p: Position): RegionType =
RegionType(eroMap[p] mod 3)
proc geologicIndex(p: Position): int =
if p == (0, 0) or p == target: 0
else:
let (x, y) = p
if y == 0: 16807 * x
elif x == 0: 48271 * y
else: eroMap[x-1][y] * eroMap[x][y-1]
proc createMaps =
var pos = (x: 0, y: 0)
while pos.x < xLim:
pos.y = 0
while pos.y < yLim:
let geoInd = geologicIndex pos
eroMap[pos] = erosionLevel(pos, geoInd)
regMap[pos] = tileType pos
inc pos.y
inc pos.x
createMaps()
proc riskLevel: int =
for x in 0 .. target.x:
for y in 0 .. target.y:
result += ord regMap[x][y]
proc isValidCoord(p: Position): bool =
p.x >= 0 and p.x < xLim and p.y >= 0 and p.y < yLim
proc switchTool(current, forbidden: Tool): Tool =
for t in {neither, torch, climbing} - {current, forbidden}:
return t
iterator neighbours(p: Position): Position =
yield (p.x-1, p.y)
yield (p.x+1, p.y)
yield (p.x, p.y-1)
yield (p.x, p.y+1)
proc reachTarget: int =
var
minutes: array[Tool, Grid[int]]
queue = initHeapQueue[PosDetails]()
start = (x: 0, y: 0)
current = (pos: start, tool: torch, time: 0)
queue.push current
while queue.len > 0:
current = queue.pop
if current.pos == target and current.tool == torch:
return current.time
let otherTool = switchTool(current.tool, Tool(regMap[current.pos]))
queue.push (pos: current.pos, tool: otherTool, time: current.time+7)
for neighbour in current.pos.neighbours:
if neighbour.isValidCoord and current.tool != Tool(regMap[neighbour]):
let time = current.time + 1
if minutes[current.tool][neighbour] == 0 or time < minutes[current.tool][neighbour]:
minutes[current.tool][neighbour] = time
queue.push (pos: neighbour, tool: current.tool, time: time)
echo riskLevel()
echo reachTarget()