|
@@ -0,0 +1,121 @@
|
|
1
|
+import sequtils, strutils
|
|
2
|
+
|
|
3
|
+type
|
|
4
|
+ GroundType = enum
|
|
5
|
+ gtVoid, gtOpen, gtTrees, gtLumberYard
|
|
6
|
+ Ground = array[-1..50, array[-1..50, GroundType]]
|
|
7
|
+
|
|
8
|
+iterator around(x, y: int): tuple[x,y: int] =
|
|
9
|
+ for i in -1..1:
|
|
10
|
+ for j in -1..1:
|
|
11
|
+ if i != 0 or j != 0:
|
|
12
|
+ yield (x + i, y + j)
|
|
13
|
+
|
|
14
|
+func hasAtLeastThree(ground: Ground, groundType: GroundType, x,y: int): bool =
|
|
15
|
+ var count = 0
|
|
16
|
+ for point in around(x, y):
|
|
17
|
+ if ground[point.y][point.x] == groundType:
|
|
18
|
+ count.inc
|
|
19
|
+ if count == 3:
|
|
20
|
+ return true
|
|
21
|
+ return false
|
|
22
|
+
|
|
23
|
+func resourceValue(ground: Ground): int =
|
|
24
|
+ # Multiplying the number of wooded acres by the number of lumberyards gives
|
|
25
|
+ # the total resource value
|
|
26
|
+ var trees, yards: int
|
|
27
|
+ for row in ground:
|
|
28
|
+ for cell in row:
|
|
29
|
+ if cell == gtLumberYard:
|
|
30
|
+ yards.inc
|
|
31
|
+ elif cell == gtTrees:
|
|
32
|
+ trees.inc
|
|
33
|
+ trees * yards
|
|
34
|
+
|
|
35
|
+func turnToTrees(ground: Ground, x,y: int): bool =
|
|
36
|
+ # An open acre will become filled with trees if three or more adjacent
|
|
37
|
+ # acres contained trees.
|
|
38
|
+ ground.hasAtLeastThree(gtTrees, x, y)
|
|
39
|
+
|
|
40
|
+func turnToLumberYard(ground: Ground, x,y: int): bool =
|
|
41
|
+ # An acre filled with trees will become a lumberyard if three or more
|
|
42
|
+ # adjacent acres were lumberyards.
|
|
43
|
+ ground.hasAtLeastThree(gtLumberYard, x, y)
|
|
44
|
+
|
|
45
|
+func remainLumberYard(ground: Ground, x,y: int): bool =
|
|
46
|
+ # An acre containing a lumberyard will remain a lumberyard if it was
|
|
47
|
+ # adjacent to at least one other lumberyard and at least one acre
|
|
48
|
+ # containing trees.
|
|
49
|
+ var foundYard, foundTrees: bool
|
|
50
|
+ for point in around(x,y):
|
|
51
|
+ if ground[point.y][point.x] == gtLumberYard:
|
|
52
|
+ foundYard = true
|
|
53
|
+ if ground[point.y][point.x] == gtTrees:
|
|
54
|
+ foundTrees = true
|
|
55
|
+ if foundTrees and foundYard:
|
|
56
|
+ return true
|
|
57
|
+ return false
|
|
58
|
+
|
|
59
|
+proc load(dest: var Ground) =
|
|
60
|
+ var y: int
|
|
61
|
+ for line in readFile("data/18.txt").strip.splitlines:
|
|
62
|
+ for x, c in line:
|
|
63
|
+ dest[y][x] = case c:
|
|
64
|
+ of '.': gtOpen
|
|
65
|
+ of '|': gtTrees
|
|
66
|
+ of '#': gtLumberYard
|
|
67
|
+ else: gtVoid
|
|
68
|
+ y.inc
|
|
69
|
+
|
|
70
|
+proc step(source: Ground, dest: var Ground) =
|
|
71
|
+ for y, row in source:
|
|
72
|
+ for x, cell in row:
|
|
73
|
+ dest[y][x] = case cell:
|
|
74
|
+ of gtVoid: gtVoid
|
|
75
|
+ of gtOpen:
|
|
76
|
+ if source.turnToTrees(x, y):
|
|
77
|
+ gtTrees
|
|
78
|
+ else:
|
|
79
|
+ gtOpen
|
|
80
|
+ of gtTrees:
|
|
81
|
+ if source.turnToLumberYard(x, y):
|
|
82
|
+ gtLumberYard
|
|
83
|
+ else:
|
|
84
|
+ gtTrees
|
|
85
|
+ of gtLumberYard:
|
|
86
|
+ if source.remainLumberYard(x, y):
|
|
87
|
+ gtLumberYard
|
|
88
|
+ else:
|
|
89
|
+ gtOpen
|
|
90
|
+
|
|
91
|
+const loops = 1_000_000_000
|
|
92
|
+
|
|
93
|
+var
|
|
94
|
+ grounds: array[100, Ground] # Arbitrary guess for maximum loop size
|
|
95
|
+ activeGround, t: int
|
|
96
|
+
|
|
97
|
+grounds[0].load
|
|
98
|
+
|
|
99
|
+while t < loops:
|
|
100
|
+ t.inc
|
|
101
|
+
|
|
102
|
+ let nextGround = (activeGround + 1) mod grounds.len
|
|
103
|
+ grounds[activeGround].step(grounds[nextGround])
|
|
104
|
+ activeGround = nextGround
|
|
105
|
+
|
|
106
|
+ if t == 10:
|
|
107
|
+ echo grounds[activeGround].resourceValue
|
|
108
|
+
|
|
109
|
+ # This isn't solvable by brute force, so assume that at some point the
|
|
110
|
+ # state will repeat itself. If we find a loop, just jump the time ahead
|
|
111
|
+ # by as many loops as we can, and carry on evaluating for the last few.
|
|
112
|
+ for i, o in grounds:
|
|
113
|
+ if i != activeGround and o == grounds[activeGround]:
|
|
114
|
+ let
|
|
115
|
+ loopLength = (grounds.len + activeGround - i) mod grounds.len
|
|
116
|
+ remaining = (loops - t) mod loopLength
|
|
117
|
+ t = loops
|
|
118
|
+ activeGround = (i + remaining) mod grounds.len
|
|
119
|
+ break
|
|
120
|
+
|
|
121
|
+echo grounds[activeGround].resourceValue
|