123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121 |
- 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
|