My solutions to 2018's advent of code
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
Ce dépôt est archivé. Vous pouvez voir les fichiers et le cloner, mais vous ne pouvez pas pousser ni ouvrir de ticket/demande d'ajout.

day20.nim 4.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. import deques, sequtils, sets, strutils, tables
  2. type
  3. Point = tuple[x,y: int]
  4. Direction = enum
  5. N, E, S, W
  6. Sequence = ref object
  7. forks: bool
  8. nodes: seq[Sequence]
  9. content: seq[Direction]
  10. proc `+` (a, b: Point): Point =
  11. result.x = a.x + b.x
  12. result.y = a.y + b.y
  13. # Parses the regular expression into a 'Sequence' data structure, by pushing
  14. # sequences onto a stack each time a nested expression is encountered.
  15. #
  16. # Three types of sequences are found:
  17. # 1) "Content-only", e.g. NNNEEE
  18. # 2) "Forks", e.g. (subsequence|subsequence|subsequence)
  19. # 3) "Concatenations", e.g. NN(subsequence)EE
  20. #
  21. func process(regex: string): Sequence =
  22. var
  23. stack = initDeque[Sequence]()
  24. span: seq[Direction]
  25. result = new(Sequence)
  26. stack.addLast(result)
  27. for c in regex:
  28. if c == '^':
  29. continue
  30. if c in ['(', ')', '|', '$']:
  31. if span.len > 0:
  32. # If we've got bare directions, add them as a content element.
  33. var newSeq = new(Sequence)
  34. newSeq.content = span
  35. stack.peekLast.nodes.add(newSeq)
  36. span.setLen(0)
  37. if c == '(':
  38. # To start a new fork block we add two sequences: one that will
  39. # contain all the forked elements, and the first child to hold
  40. # the content of the first fork.
  41. var forkSeq = new(Sequence)
  42. forkSeq.forks = true
  43. stack.addLast(forkSeq)
  44. stack.addLast(new(Sequence))
  45. elif c == '|':
  46. # Pop the previous child off and add it to the parent fork block,
  47. # then add a new one for the next branch.
  48. var child = stack.popLast
  49. stack.peekLast.nodes.add(child)
  50. stack.addLast(new(Sequence))
  51. elif c == ')':
  52. # Pop both the child and the parent fork block off the stack.
  53. var child = stack.popLast
  54. stack.peekLast.nodes.add(child)
  55. var forkSeq = stack.popLast
  56. stack.peekLast.nodes.add(forkSeq)
  57. else:
  58. span.add(parseEnum[Direction]($c))
  59. stack.popLast
  60. let
  61. directions = {
  62. N: ( 0, 1),
  63. E: ( 1, 0),
  64. S: ( 0, -1),
  65. W: (-1, 0),
  66. }.toTable
  67. opposites = { N: S, E: W, S: N, W: E, }.toTable
  68. input = readFile("data/20.txt").strip
  69. proc addDirection(table: var Table[Point, HashSet[Direction]], point: Point, direction: Direction) =
  70. if not table.hasKey(point):
  71. table[point] = initSet[Direction]()
  72. table[point].incl(direction)
  73. proc addDirections(table: var Table[Point, HashSet[Direction]], point: Point, direction: Direction): Point =
  74. let otherPoint = point + directions[direction]
  75. table.addDirection(point, direction)
  76. table.addDirection(otherPoint, opposites[direction])
  77. otherPoint
  78. # Recurses through the given sequence and builds up a map of which points are
  79. # traversable in which directions.
  80. #
  81. # The points parameter tracks which points could have been reached by the
  82. # sequence thus far. The current sequence must be evaluated for all of those
  83. # points. Initially this is just {(0,0)} but every time a fork is encountered
  84. # the number of points will expand.
  85. proc map(sequence: Sequence, grid: var Table[Point, HashSet[Direction]], points: HashSet[Point]): HashSet[Point] =
  86. if sequence.content.len > 0:
  87. result = initSet[Point]()
  88. for point in points:
  89. var newPoint = point
  90. for d in sequence.content:
  91. newPoint = grid.addDirections(newPoint, d)
  92. result.incl(newPoint)
  93. elif sequence.forks:
  94. result = initSet[Point]()
  95. for child in sequence.nodes:
  96. result.incl(child.map(grid, points))
  97. else:
  98. result = points
  99. for child in sequence.nodes:
  100. result = child.map(grid, result)
  101. # Performs a breadth-first search of the navigable area and assigns a
  102. # distance to each point.
  103. proc score(dirs: Table[Point, HashSet[Direction]]): Table[Point, int] =
  104. result = initTable[Point, int]()
  105. var stack = initDeque[tuple[pos: Point, length: int]]()
  106. stack.addLast(((0, 0), 0))
  107. while stack.len > 0:
  108. let step = stack.popFirst
  109. if not result.hasKey(step.pos) or result[step.pos] > step.length:
  110. result[step.pos] = step.length
  111. for direction in dirs[step.pos]:
  112. stack.addLast((step.pos + directions[direction], step.length + 1))
  113. proc distances(input: string): seq[int] =
  114. var grid = initTable[Point, HashSet[Direction]]()
  115. let points = toSet([(0, 0)])
  116. discard input.process.map(grid, points)
  117. toSeq(grid.score.values)
  118. let distanceValues = input.distances
  119. echo distanceValues.max
  120. echo distanceValues.filter(proc(i: int): bool = i >= 1000).len