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.

day15.nim 5.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. import algorithm, deques, sequtils, strutils
  2. type
  3. Species = enum
  4. spElf, spGoblin
  5. Point = tuple[x,y: int]
  6. Creep = ref object
  7. pos: Point
  8. hp,ap: int
  9. species: Species
  10. proc cmpCreeps(x, y: Creep): int =
  11. if x.pos.y == y.pos.y:
  12. x.pos.x - y.pos.x
  13. else:
  14. x.pos.y - y.pos.y
  15. iterator moves(pos: Point): Point =
  16. yield (pos.x, pos.y-1)
  17. yield (pos.x-1, pos.y)
  18. yield (pos.x+1, pos.y)
  19. yield (pos.x, pos.y+1)
  20. proc highAt(bitmasks: seq[uint64], pos: Point): bool {.inline.} =
  21. let mask = 1'u64 shl pos.x
  22. (bitmasks[pos.y] and mask) == mask
  23. func setLowAt(bitmasks: var seq[uint64], pos: Point) {.inline.} =
  24. bitmasks[pos.y] = bitmasks[pos.y] and not (1'u64 shl pos.x)
  25. func setHighAt(bitmasks: var seq[uint64], pos: Point) {.inline.} =
  26. bitmasks[pos.y] = bitmasks[pos.y] or (1'u64 shl pos.x)
  27. proc inRange(targets: seq[uint64], pos: Point): bool =
  28. for move in pos.moves:
  29. if targets.highAt(move):
  30. return true
  31. proc allDead(targets: seq[uint64]): bool =
  32. for line in targets:
  33. if line != 0:
  34. return false
  35. return true
  36. proc totalHp(creeps: seq[Creep]): int =
  37. for creep in creeps:
  38. if creep.hp > 0:
  39. result += creep.hp
  40. proc step(targets: seq[uint64], passable: seq[uint64], pos: Point): tuple[valid: bool, step: Point] =
  41. type Step = tuple[firstStep, pos: Point]
  42. var
  43. queue = initDeque[Step]()
  44. allowed: seq[uint64]
  45. allowed.deepCopy(passable)
  46. for firstStep in pos.moves:
  47. if allowed.highAt(firstStep):
  48. queue.addLast((firstStep, firstStep))
  49. allowed.setLowAt(firstStep)
  50. while queue.len > 0:
  51. let lastStep = queue.popFirst()
  52. for nextStep in lastStep.pos.moves:
  53. if targets.highAt(nextStep):
  54. return (true, lastStep.firstStep)
  55. if allowed.highAt(nextStep):
  56. queue.addLast((lastStep.firstStep, nextStep))
  57. allowed.setLowAt(nextStep)
  58. let
  59. input = readFile("data/15.txt").strip.splitlines
  60. var
  61. maxx = int.low
  62. creeps: seq[Creep]
  63. passable: seq[uint64]
  64. goblins: seq[uint64]
  65. elves: seq[uint64]
  66. proc load(elfAttackPower: int = 3) =
  67. maxx = int.low
  68. creeps.setLen(0)
  69. passable.setLen(0)
  70. goblins.setLen(0)
  71. elves.setLen(0)
  72. for y, line in input:
  73. var linePassable, lineGoblins, lineElves: uint64
  74. for x, c in line:
  75. maxx = max(x, maxx)
  76. if c == 'E' or c == 'G':
  77. var creep = new(Creep)
  78. creep.pos = (x,y)
  79. creep.hp = 200
  80. if c == 'E':
  81. creep.species = spElf
  82. lineElves = lineElves or (1'u64 shl x)
  83. creep.ap = elfAttackPower
  84. else:
  85. creep.species = spGoblin
  86. lineGoblins = lineGoblins or (1'u64 shl x)
  87. creep.ap = 3
  88. creeps.add(creep)
  89. elif c == '.':
  90. linePassable = linePassable or (1'u64 shl x)
  91. passable.add(linePassable)
  92. goblins.add(lineGoblins)
  93. elves.add(lineElves)
  94. proc run(elfAttackPower: int = 3, allowElvesToDie: bool = true): int =
  95. load(elfAttackPower)
  96. var runs: int
  97. while true:
  98. creeps.sort(cmpCreeps)
  99. for creep in creeps:
  100. if creep.hp <= 0:
  101. continue
  102. let targets = if creep.species == spGoblin: elves else: goblins
  103. if targets.allDead:
  104. return runs * creeps.totalHp
  105. if not targets.inRange(creep.pos):
  106. let move = targets.step(passable, creep.pos)
  107. if move.valid:
  108. if creep.species == spGoblin:
  109. goblins.setLowAt(creep.pos)
  110. goblins.setHighAt(move.step)
  111. else:
  112. elves.setLowAt(creep.pos)
  113. elves.setHighAt(move.step)
  114. passable.setHighAt(creep.pos)
  115. passable.setLowAt(move.step)
  116. creep.pos = move.step
  117. if targets.inRange(creep.pos):
  118. let spaces = toSeq(creep.pos.moves)
  119. var
  120. lowestHp = int.high
  121. bestCreep: Creep
  122. for other in creeps:
  123. # Find an enemy that's alive
  124. if other.species != creep.species and other.hp > 0 and
  125. # ... That's in range
  126. other.pos in spaces and
  127. # ... With lower HP
  128. (other.hp < lowestHp or
  129. # ... Or the same HP and higher up
  130. (other.hp == lowestHp and (other.pos.y < bestCreep.pos.y or
  131. # ... Or the same HP and height but further left
  132. (other.pos.y == bestCreep.pos.y and other.pos.x < bestCreep.pos.x)))):
  133. lowestHp = other.hp
  134. bestCreep = other
  135. bestCreep.hp -= creep.ap
  136. if bestCreep.hp <= 0:
  137. if bestCreep.species == spGoblin:
  138. goblins.setLowAt(bestCreep.pos)
  139. else:
  140. elves.setLowAt(bestCreep.pos)
  141. if not allowElvesToDie:
  142. return -1
  143. passable.setHighAt(bestCreep.pos)
  144. runs.inc
  145. echo run()
  146. var ap = 4
  147. while true:
  148. let res = run(ap, false)
  149. if res > -1:
  150. echo res
  151. break
  152. ap.inc