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.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  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. # At each node we can switch tools once, with a cost of 7 minutes
  76. for tool in tools[erosions[node.pos] mod 3]:
  77. if tool != node.tool and not distances.hasKey((node.pos, tool)):
  78. stack.insert(((node.pos, tool)), distance + 7)
  79. # Up to four possible moves from the current node, depending on
  80. # terrain and tools.
  81. for newPos in node.pos.moves:
  82. if erosions.hasKey(newPos) and
  83. node.tool in tools[erosions[newPos] mod 3] and
  84. not distances.hasKey((newPos, node.tool)):
  85. stack.insert(((newPos, node.tool)), distance + 1)
  86. echo dangerSum
  87. echo distances[targetNode]