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.

day09.nim 3.0KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. import strscans
  2. # For performance, we manually allocate a contiguous region of memory to store
  3. # all of our marbles in, and manually work out pointer offsets for them within
  4. # that region based on the marble ID. This is probably a very bad idea for
  5. # anything other than a performance-obsessed, short-lived program.
  6. type
  7. Marble = ptr object
  8. next: Marble
  9. const
  10. MarbleSize = sizeof(pointer)
  11. proc addressOf(memory: pointer, marbleNumber: int): Marble {.inline.} =
  12. cast[Marble](cast[uint](memory) + cast[uint](marbleNumber * MarbleSize))
  13. proc valueOf(memory: pointer, marble: Marble): int {.inline.} =
  14. cast[int](cast[uint](marble) - cast[uint](memory)) div MarbleSize
  15. proc insertAfter(node: Marble, memory: pointer, value: int): Marble {.inline.} =
  16. var newNode = memory.addressOf(value)
  17. newNode.next = node.next
  18. node.next = newNode
  19. newNode
  20. proc removeNext(node: Marble) {.inline.} =
  21. node.next = node.next.next
  22. proc newSingleNode(memory: pointer, value: int): Marble =
  23. result = memory.addressOf(value)
  24. result.next = result
  25. var
  26. input = readFile("data/09.txt")
  27. players: int
  28. marbles: int
  29. if not input.scanf("$i players; last marble is worth $i points", players, marbles):
  30. raise newException(Defect, "Invalid input line: " & input)
  31. # Instead of using a doubly-linked list, we keep a current pointer and a
  32. # separate one trailing behind it. The trail will expand to 8 marbles and is
  33. # used when a multiple of 23 is played (so we can remove the N-7th marble). The
  34. # trail then catches up to the current pointer and drifts back to 8 again over
  35. # the next few moves. This saves a 64 bit memory allocation per marble, which
  36. # gives a small but noticable speed bump.
  37. let
  38. hundredMarbles = marbles * 100
  39. memory = alloc(MarbleSize * hundredMarbles)
  40. var
  41. player = 0
  42. scores = newSeq[int](players)
  43. current = newSingleNode(memory, 0)
  44. currentTrail = current
  45. currentTrailDrift = 0
  46. specialCountdown = 23
  47. for i in 1 .. hundredMarbles:
  48. # Instead of testing each marble number to see if it's a multiple of 23, we
  49. # keep a counter that we just loop over and over again.
  50. specialCountdown.dec
  51. if specialCountdown == 0:
  52. # The current player is only relevant when a 23nth marble is played, so
  53. # we can just update the player here instead of every turn.
  54. player = (player + 23) mod players
  55. scores[player] += i + memory.valueOf(currentTrail.next)
  56. currentTrail.removeNext
  57. current = currentTrail.next
  58. currentTrail = current
  59. currentTrailDrift = 0
  60. specialCountdown = 23
  61. else:
  62. current = current.next.insertAfter(memory, i)
  63. if i > 8:
  64. if currentTrailDrift == 8:
  65. currentTrail = currentTrail.next.next
  66. else:
  67. currentTrailDrift += 2
  68. else:
  69. currentTrail = current
  70. if i == marbles or i == hundredMarbles:
  71. echo scores.max