import lib import math EXAMPLE = """ Sensor at x=2, y=18: closest beacon is at x=-2, y=15 Sensor at x=9, y=16: closest beacon is at x=10, y=16 Sensor at x=13, y=2: closest beacon is at x=15, y=3 Sensor at x=12, y=14: closest beacon is at x=10, y=16 Sensor at x=10, y=20: closest beacon is at x=10, y=16 Sensor at x=14, y=17: closest beacon is at x=10, y=16 Sensor at x=8, y=7: closest beacon is at x=2, y=10 Sensor at x=2, y=0: closest beacon is at x=2, y=10 Sensor at x=0, y=11: closest beacon is at x=2, y=10 Sensor at x=20, y=14: closest beacon is at x=25, y=17 Sensor at x=17, y=20: closest beacon is at x=21, y=22 Sensor at x=16, y=7: closest beacon is at x=15, y=3 Sensor at x=14, y=3: closest beacon is at x=15, y=3 Sensor at x=20, y=1: closest beacon is at x=15, y=3 """ def unify_ranges(ranges): # aaaaaaaaaaa # bbbb # # aaa # bbb # # aaa # bbb ranges = sorted(ranges) while True: for i in range(len(ranges) - 1): a, b = ranges[i], ranges[i + 1] if a == b: del ranges[i] break elif a[0] == b[0]: ranges[i] = (a[0], max(a[1], b[1])) del ranges[i + 1] break elif a[1] == b[1]: ranges[i] = (min(a[0], b[0]), b[1]) del ranges[i + 1] break elif a[0] < b[0] and a[1] > b[1]: del ranges[i + 1] break elif a[0] > b[0] and a[1] < b[1]: del ranges[i] break elif a[1] < b[0]: pass elif a[0] <= b[1] and a[1] >= b[0]: ranges[i] = (a[0], b[1]) del ranges[i + 1] break else: raise Exception("uhoh", a, b) else: return ranges return ranges def mdist(dx, dy): return abs(dx) + abs(dy) def xdist(dm, dy): x = dm - abs(dy) if x <= 0: return 0 return x def solve(lines: list[str], yt): sensors = [] bacons = set() for (i, line) in enumerate(lines): digits = lib.str_to_ints(line) sx, sy, bx, by = digits sm = mdist(bx - sx, by - sy) sensors.append([sx, sy, sm]) bacons.add((bx, by)) ranges = [] for (sx, sy, sm) in sensors: x_range = xdist(sm, yt - sy) if x_range == 0: continue r = (sx - x_range, sx + x_range) # print(f"{sx=} {sy=} {r=}") ranges.append(r) ranges = unify_ranges(ranges) r = 0 for (a, b) in ranges: r += b - a + 1 for (bx, by) in list(bacons): if by == yt and bx >= a and bx <= b: r -= 1 # 140:00 I don't know what a Manhattan distance is. return r def solve2(lines: list[str], xymax): sensors = [] bacons = set() for (i, line) in enumerate(lines): digits = lib.str_to_ints(line) sx, sy, bx, by = digits sm = mdist(bx - sx, by - sy) sensors.append([sx, sy, sm]) bacons.add((bx, by)) finds = [] for yt in range(0, xymax): ranges = [] for (sx, sy, sm) in sensors: x_range = xdist(sm, yt - sy) if x_range == 0: continue r = (sx - x_range, sx + x_range) ranges.append(r) ranges = unify_ranges(ranges) if ranges[0][0] > 0: raise Exception("Bacon at 0.") elif ranges[-1][-1] < xymax: raise Exception("Bacon at xymax.") for i in range(len(ranges) - 1): if ranges[i + 1][0] - ranges[i][1] > 1: finds.append((ranges[i][1] + 1, yt)) if len(finds) != 1: raise Exception("TOO MANY FINDS") else: x, y = finds[0] return x * 4000000 + y # 10:00 def main(): lines = lib.str_to_lines_no_empty(EXAMPLE) print("Example 1:", solve(lines, 10)) lines = lib.str_to_lines_no_empty(open("i15.txt").read()) print("Solution 1:", solve(lines, 2000000)) lines = lib.str_to_lines_no_empty(EXAMPLE) print("Example 2:", solve2(lines, 20)) lines = lib.str_to_lines_no_empty(open("i15.txt").read()) print("Solution 2:", solve2(lines, 4000000)) if __name__ == "__main__": main()