This repository has been archived by the owner on Jan 2, 2023. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
day18.nim
121 lines (104 loc) · 3.92 KB
/
day18.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
109
110
111
112
113
114
115
116
117
118
119
120
121
import sequtils, strutils
type
GroundType = enum
gtVoid, gtOpen, gtTrees, gtLumberYard
Ground = array[-1..50, array[-1..50, GroundType]]
iterator around(x, y: int): tuple[x,y: int] =
for i in -1..1:
for j in -1..1:
if i != 0 or j != 0:
yield (x + i, y + j)
func hasAtLeastThree(ground: Ground, groundType: GroundType, x,y: int): bool =
var count = 0
for point in around(x, y):
if ground[point.y][point.x] == groundType:
count.inc
if count == 3:
return true
return false
func resourceValue(ground: Ground): int =
# Multiplying the number of wooded acres by the number of lumberyards gives
# the total resource value
var trees, yards: int
for row in ground:
for cell in row:
if cell == gtLumberYard:
yards.inc
elif cell == gtTrees:
trees.inc
trees * yards
func turnToTrees(ground: Ground, x,y: int): bool =
# An open acre will become filled with trees if three or more adjacent
# acres contained trees.
ground.hasAtLeastThree(gtTrees, x, y)
func turnToLumberYard(ground: Ground, x,y: int): bool =
# An acre filled with trees will become a lumberyard if three or more
# adjacent acres were lumberyards.
ground.hasAtLeastThree(gtLumberYard, x, y)
func remainLumberYard(ground: Ground, x,y: int): bool =
# An acre containing a lumberyard will remain a lumberyard if it was
# adjacent to at least one other lumberyard and at least one acre
# containing trees.
var foundYard, foundTrees: bool
for point in around(x,y):
if ground[point.y][point.x] == gtLumberYard:
foundYard = true
if ground[point.y][point.x] == gtTrees:
foundTrees = true
if foundTrees and foundYard:
return true
return false
proc load(dest: var Ground) =
var y: int
for line in readFile("data/18.txt").strip.splitlines:
for x, c in line:
dest[y][x] = case c:
of '.': gtOpen
of '|': gtTrees
of '#': gtLumberYard
else: gtVoid
y.inc
proc step(source: Ground, dest: var Ground) =
for y, row in source:
for x, cell in row:
dest[y][x] = case cell:
of gtVoid: gtVoid
of gtOpen:
if source.turnToTrees(x, y):
gtTrees
else:
gtOpen
of gtTrees:
if source.turnToLumberYard(x, y):
gtLumberYard
else:
gtTrees
of gtLumberYard:
if source.remainLumberYard(x, y):
gtLumberYard
else:
gtOpen
const loops = 1_000_000_000
var
grounds: array[100, Ground] # Arbitrary guess for maximum loop size
activeGround, t: int
grounds[0].load
while t < loops:
t.inc
let nextGround = (activeGround + 1) mod grounds.len
grounds[activeGround].step(grounds[nextGround])
activeGround = nextGround
if t == 10:
echo grounds[activeGround].resourceValue
# This isn't solvable by brute force, so assume that at some point the
# state will repeat itself. If we find a loop, just jump the time ahead
# by as many loops as we can, and carry on evaluating for the last few.
for i, o in grounds:
if i != activeGround and o == grounds[activeGround]:
let
loopLength = (grounds.len + activeGround - i) mod grounds.len
remaining = (loops - t) mod loopLength
t = loops
activeGround = (i + remaining) mod grounds.len
break
echo grounds[activeGround].resourceValue