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.

day22.nim 3.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. import sequtils, strutils, tables
  2. type
  3. Point = tuple[x,y: int]
  4. Tool = enum tTorch, tClimbing, tNeither
  5. Node = tuple[pos: Point, tool: Tool]
  6. iterator moves(point: Point): Point =
  7. let (x, y) = point
  8. if y > 0: yield (x, y - 1)
  9. if x > 0: yield (x - 1, y)
  10. yield (x, y + 1)
  11. yield (x + 1, y)
  12. let
  13. input = readFile("data/22.txt").splitLines
  14. depth = input[0].strip.split(' ')[1].parseInt
  15. targetParts = input[1].strip.split(' ')[1].split(',').map(parseInt)
  16. target: Point = (targetParts[0], targetParts[1])
  17. targetNode: Node = (target, tTorch)
  18. tools = [[tClimbing, tTorch], [tClimbing, tNeither], [tTorch, tNeither]]
  19. var
  20. erosions = initTable[Point, int]()
  21. dangerSum = 0
  22. # Add arbitrary extension to allow for a bit of overshooting.
  23. for y in 0..target.y + 75:
  24. for x in 0..target.x + 75:
  25. let
  26. geoindex = if (x == 0 and y == 0) or (x, y) == target:
  27. 0
  28. elif y == 0:
  29. x * 16807
  30. elif x == 0:
  31. y * 48271
  32. else:
  33. erosions[(x-1, y)] * erosions[(x, y-1)]
  34. erosionlevel = (geoindex + depth) mod 20183
  35. erosions[(x, y)] = erosionlevel
  36. if x <= target.x and y <= target.y:
  37. dangerSum += erosionlevel mod 3
  38. # Custom stack of pending steps, kept in order from smallest to largest.
  39. # Insertion is O(n), removing the smallest is O(1). Performs approximately a
  40. # billionty times faster than I managed using a Seq/Deque/Table/CountingTable.
  41. type
  42. StackStep = ref object
  43. node: Node
  44. distance: int
  45. next: StackStep
  46. Stack = object
  47. head: StackStep
  48. proc insert(stack: var Stack, node: Node, distance: int) =
  49. var newNode = new(StackStep)
  50. newNode.node = node
  51. newNode.distance = distance
  52. if stack.head == nil:
  53. stack.head = newNode
  54. else:
  55. var target = stack.head
  56. while target.next != nil and target.next.distance < distance:
  57. target = target.next
  58. newNode.next = target.next
  59. target.next = newNode
  60. proc popSmallest(stack: var Stack): tuple[node: Node, distance: int] =
  61. result = (stack.head.node, stack.head.distance)
  62. stack.head = stack.head.next
  63. var
  64. distances = initTable[Node, int]()
  65. stack: Stack
  66. stack.insert(((0, 0), tTorch), 0)
  67. while not distances.hasKey(targetNode):
  68. let (node, distance) = stack.popSmallest
  69. # We can have duplicate steps in our stack, but if we've already
  70. # put the distance then that route was necessarily shorter. Just
  71. # skip it.
  72. if distances.hasKey(node):
  73. continue
  74. distances[node] = distance
  75. # Up to four possible moves from the current node, depending on
  76. # terrain and tools.
  77. for newPos in node.pos.moves:
  78. if erosions.hasKey(newPos) and not distances.hasKey((newPos, node.tool)):
  79. let targetTools = tools[erosions[newPos] mod 3]
  80. if node.tool in targetTools:
  81. # We have a tool that lets us pass, there's no point trying to
  82. # swap.
  83. stack.insert(((newPos, node.tool)), distance + 1)
  84. else:
  85. # See if we can switch to a compatible tool and treat it as
  86. # a single step.
  87. for newTool in targetTools:
  88. if newTool in tools[erosions[node.pos] mod 3]:
  89. stack.insert(((newPos, newTool)), distance + 8)
  90. echo dangerSum
  91. echo distances[targetNode]