| @@ -4,6 +4,14 @@ helper = Helper(debug=True) | |||
| load_input = helper.load_input | |||
| debug = helper.debug | |||
| # FUTURE ME: HERE'S THE KEY | |||
| # You don't have to check every seed. | |||
| # Instead, create ranges, and use the later steps to cut those ranges | |||
| # into chunks. The smallest value in the smallest chunk after the last | |||
| # step is the solution. The brute-force solution takes an enormous amount | |||
| # of time; the chunk solution is still polynomial time, I think, but | |||
| # the n is much smaller (dozens rather than hundreds of millions). | |||
| def within(target, start, end): | |||
| if start == end: | |||
| return target == start | |||
| @@ -11,6 +19,40 @@ def within(target, start, end): | |||
| start, end = end, start | |||
| return target >= start and end >= target | |||
| def chunk_it(old_range, new_range): | |||
| o_start, o_end = old_range | |||
| n_start, n_end = new_range | |||
| debug(o_start, o_end, n_start, n_end) | |||
| # The new range is entirely outside the current range | |||
| if n_end < o_start or n_start > o_end: | |||
| debug("Entirely outside!") | |||
| return sorted([old_range, new_range], key=lambda n:n[0]) | |||
| # The new range is entirely within the current range | |||
| if n_start >= o_start and n_end <= o_end: | |||
| # We've already accounted for this range | |||
| debug("Already got it") | |||
| return [old_range] | |||
| # The new range starts lower and ends higher | |||
| if n_start <= o_start and n_end >= o_end: | |||
| # We can replace the old range with the new one | |||
| debug("Replacement!") | |||
| return [new_range] | |||
| # The new range starts lower but ends within | |||
| if n_start <= o_start and n_end >= o_start and n_end <= o_end: | |||
| debug("Starts before") | |||
| return [[n_start, o_end]] | |||
| # The new range starts within but ends higher | |||
| if n_end >= o_end and n_start >= o_start and n_start <= o_end: | |||
| debug("Ends after") | |||
| return [[o_start, n_end]] | |||
| raise Exception(f"What? What? Nothing matched: {old_range}, {new_range}") | |||
| def main(): | |||
| lines = load_input(5) | |||
| @@ -26,10 +68,29 @@ def main(): | |||
| } | |||
| maps_keys = list(maps_dict.keys()) | |||
| maps_dict["seeds"] = [int(n) for n in lines[0].split()[1:]] | |||
| debug(f"Starting with {len(maps_dict['seeds'])//2} ranges") | |||
| real_seeds = [] | |||
| i, j = 0, 1 | |||
| while j < len(maps_dict["seeds"]): | |||
| real_seeds.append((maps_dict["seeds"][i], maps_dict["seeds"][i] + maps_dict["seeds"][j])) | |||
| new_range = [maps_dict["seeds"][i], maps_dict["seeds"][i] + maps_dict["seeds"][j]] | |||
| if len(real_seeds) == 0: | |||
| real_seeds.append(new_range) | |||
| else: | |||
| new_real_seeds = [] | |||
| for range in real_seeds: | |||
| add_ranges = chunk_it(range, new_range) | |||
| debug(add_ranges) | |||
| for r in add_ranges: | |||
| if r not in new_real_seeds: | |||
| new_real_seeds.append(r) | |||
| real_seeds = list(new_real_seeds) | |||
| i += 2 | |||
| j += 2 | |||
| debug(real_seeds, i, j) | |||
| debug(f"Found {len(real_seeds)} actual ranges") | |||
| real_seeds.sort(key=lambda n: n[0]) | |||
| # debug(real_seeds) | |||
| return | |||
| # new_start = maps_dict["seeds"][i] | |||
| # new_end = new_start + maps_dict["seeds"][j] | |||
| # debug(f"Adding seeds in range {new_start}-{new_end}") | |||
| @@ -37,8 +98,6 @@ def main(): | |||
| # debug(new_seeds) | |||
| # real_seeds.append(new_seeds) | |||
| # debug(real_seeds) | |||
| i += 2 | |||
| j += 2 | |||
| seeds_list = [{"seed": seed[1]} for seed in real_seeds] | |||
| current_key = "" | |||