My solutions to 2018's advent of code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.

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]