Let's see how far I get this year.
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

11 місяці тому
11 місяці тому
11 місяці тому
11 місяці тому
11 місяці тому
11 місяці тому
11 місяці тому
11 місяці тому
11 місяці тому
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. from helpers import Helper
  2. helper = Helper(debug=True)
  3. load_input = helper.load_input
  4. debug = helper.debug
  5. # FUTURE ME: HERE'S THE KEY
  6. # You don't have to check every seed.
  7. # Instead, create ranges, and use the later steps to cut those ranges
  8. # into chunks. The smallest value in the smallest chunk after the last
  9. # step is the solution. The brute-force solution takes an enormous amount
  10. # of time; the chunk solution is still polynomial time, I think, but
  11. # the n is much smaller (dozens rather than hundreds of millions).
  12. def within(target, start, end):
  13. if start == end:
  14. return target == start
  15. if start > end:
  16. start, end = end, start
  17. return target >= start and end >= target
  18. def chunk_it(old_range, new_range):
  19. o_start, o_end = old_range
  20. n_start, n_end = new_range
  21. debug(o_start, o_end, n_start, n_end)
  22. # The new range is entirely outside the current range
  23. if n_end < o_start or n_start > o_end:
  24. debug("Entirely outside!")
  25. return sorted([old_range, new_range], key=lambda n:n[0])
  26. # The new range is entirely within the current range
  27. if n_start >= o_start and n_end <= o_end:
  28. # We've already accounted for this range
  29. debug("Already got it")
  30. return [old_range]
  31. # The new range starts lower and ends higher
  32. if n_start <= o_start and n_end >= o_end:
  33. # We can replace the old range with the new one
  34. debug("Replacement!")
  35. return [new_range]
  36. # The new range starts lower but ends within
  37. if n_start <= o_start and n_end >= o_start and n_end <= o_end:
  38. debug("Starts before")
  39. return [[n_start, o_end]]
  40. # The new range starts within but ends higher
  41. if n_end >= o_end and n_start >= o_start and n_start <= o_end:
  42. debug("Ends after")
  43. return [[o_start, n_end]]
  44. raise Exception(f"What? What? Nothing matched: {old_range}, {new_range}")
  45. def main():
  46. lines = load_input(5)
  47. maps_dict = {
  48. "seeds": [],
  49. "seed-to-soil": [],
  50. "soil-to-fertilizer": [],
  51. "fertilizer-to-water": [],
  52. "water-to-light": [],
  53. "light-to-temperature": [],
  54. "temperature-to-humidity": [],
  55. "humidity-to-location": [],
  56. }
  57. maps_keys = list(maps_dict.keys())
  58. maps_dict["seeds"] = [int(n) for n in lines[0].split()[1:]]
  59. debug(f"Starting with {len(maps_dict['seeds'])//2} ranges")
  60. real_seeds = []
  61. i, j = 0, 1
  62. while j < len(maps_dict["seeds"]):
  63. new_range = [maps_dict["seeds"][i], maps_dict["seeds"][i] + maps_dict["seeds"][j]]
  64. if len(real_seeds) == 0:
  65. real_seeds.append(new_range)
  66. else:
  67. new_real_seeds = []
  68. for range in real_seeds:
  69. add_ranges = chunk_it(range, new_range)
  70. debug(add_ranges)
  71. for r in add_ranges:
  72. if r not in new_real_seeds:
  73. new_real_seeds.append(r)
  74. real_seeds = list(new_real_seeds)
  75. i += 2
  76. j += 2
  77. debug(real_seeds, i, j)
  78. debug(f"Found {len(real_seeds)} actual ranges")
  79. real_seeds.sort(key=lambda n: n[0])
  80. # debug(real_seeds)
  81. return
  82. # new_start = maps_dict["seeds"][i]
  83. # new_end = new_start + maps_dict["seeds"][j]
  84. # debug(f"Adding seeds in range {new_start}-{new_end}")
  85. # new_seeds = range(new_start, new_end)
  86. # debug(new_seeds)
  87. # real_seeds.append(new_seeds)
  88. # debug(real_seeds)
  89. seeds_list = [{"seed": seed[1]} for seed in real_seeds]
  90. current_key = ""
  91. for line in lines[1:]:
  92. if line == "":
  93. continue
  94. split_line = line.split()
  95. if split_line[0] in maps_keys:
  96. current_key = split_line[0]
  97. else:
  98. maps_dict[current_key].append({
  99. x: int(a)
  100. for (x, a)
  101. in zip(["destination", "source", "length"], split_line)}
  102. )
  103. for seed in seeds_list:
  104. for key in maps_keys[1:]:
  105. source, _, destination = key.split("-")
  106. i = 0
  107. t_list = maps_dict[key]
  108. while i < len(t_list):
  109. s_map = t_list[i]
  110. # debug(f"Checking {seed[source]} against {s_map['source']}, {s_map['source'] + s_map['length']}")
  111. if within(seed[source], s_map["source"], (s_map["source"] + s_map["length"])):
  112. source_distance = seed[source] - s_map["source"]
  113. seed[destination] = s_map["destination"] + source_distance
  114. # debug("{}: {} found in {}+{}, {} is {} + {}".format(
  115. # source,
  116. # seed[source],
  117. # s_map["source"],
  118. # s_map["length"],
  119. # seed[destination],
  120. # s_map["destination"],
  121. # source_distance
  122. # ))
  123. i = len(t_list)
  124. else:
  125. # debug("{}: {} not found in {}+{}, using original".format(
  126. # source,
  127. # seed[source],
  128. # s_map["source"],
  129. # s_map["length"]
  130. # ))
  131. i += 1
  132. if not seed.get(destination, None):
  133. seed[destination] = seed[source]
  134. debug("\n".join([f"{k}: {v}" for k, v in seed.items() for seed in seeds_list]))
  135. print(min(seeds_list, key=lambda x: x["location"]))
  136. if __name__ == "__main__":
  137. main()