19.py 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. from queue import PriorityQueue
  2. lines = [line.split(".") for line in open("19.input")]
  3. blueprints = [list(map(int,[line[0].split(" ")[6], line[1].split(" ")[5], line[2].split(" ")[5], line[2].split(" ")[8], line[3].split(" ")[5], line[3].split(" ")[8]])) for line in lines]
  4. def add(a, b):
  5. return tuple(a_ + b_ for (a_, b_) in zip(list(a), list(b)))
  6. def sub(a, b):
  7. return tuple(a_ - b_ for (a_, b_) in zip(list(a), list(b)))
  8. def lte(a, b):
  9. return all(a_ <= b_ for (a_, b_) in zip(list(a), list(b)))
  10. def possible_purchases(res, robo, rem, costs, max_costs):
  11. """Returns set of possible purchase tuples"""
  12. result = set()
  13. for robo_idx, cost in enumerate(costs):
  14. if lte(cost, res):
  15. result.add(robo_idx)
  16. if 3 in result:
  17. return {3}
  18. for i in range(3):
  19. if res[i] + robo[i] * rem >= max_costs[i] * rem:
  20. result -= {i}
  21. # For part 2, this can be changed to < instead of <= for more speed. ¯\_(ツ)_/¯
  22. # I guess I was just lucky with my input...
  23. if len(result) <= 2:
  24. result.add(-1)
  25. return result
  26. def find_max(bp_idx, blueprint, steps):
  27. c_ore, c_clay, c_ob_ore, c_ob_clay, c_geo_ore, c_geo_ob = blueprint
  28. q = PriorityQueue()
  29. states = {}
  30. results = {}
  31. purc_cache = {}
  32. res = (0, 0, 0, 0)
  33. robo = (1, 0, 0, 0)
  34. costs = [
  35. (c_ore, 0, 0, 0),
  36. (c_clay, 0, 0, 0),
  37. (c_ob_ore, c_ob_clay, 0, 0),
  38. (c_geo_ore, 0, c_geo_ob, 0),
  39. ]
  40. max_costs = [max(c[i] for c in costs) for i in range(4)]
  41. start = (res, robo, steps)
  42. q.put(start)
  43. i = 0
  44. while not q.empty():
  45. current = q.get()
  46. #print(len(states), current)
  47. res, robo, rem = current
  48. if res[0] > i:
  49. i = res[0]
  50. print(i)
  51. if robo[0] >= c_geo_ore and robo[2] >= c_geo_ob:
  52. print("Early exit")
  53. states[current] = res[3] + robo[3] * rem + rem * rem // 2
  54. continue
  55. if rem == 0:
  56. continue
  57. for robo_idx in possible_purchases(res, robo, rem, costs, max_costs):
  58. if robo_idx != -1:
  59. new_purc = [0, 0, 0, 0]
  60. new_purc[robo_idx] = 1
  61. next_st = (add(sub(res, costs[robo_idx]), robo), add(robo, tuple(new_purc)), rem - 1)
  62. else:
  63. next_st = (add(res, robo), robo, rem - 1)
  64. if next_st not in states:
  65. states[next_st] = next_st[0][3]
  66. q.put(next_st)
  67. return max(s for s in states.values())
  68. def part1():
  69. total = 0
  70. for i, bp in enumerate(blueprints):
  71. score = find_max(i, bp, 24)
  72. print(i + 1, score)
  73. total += (i + 1) * score
  74. print("Part 1:", total)
  75. part1()
  76. def part2():
  77. product = 1
  78. for i, bp in enumerate(blueprints[:3]):
  79. score = find_max(i, bp, 32)
  80. print(i + 1, score)
  81. product *= score
  82. print("Part 2:", product)
  83. part2()