From 30fcda48fda6c543b09c9b27215e4eb24b421735 Mon Sep 17 00:00:00 2001 From: cupcakearmy Date: Wed, 21 Dec 2022 22:39:59 +0100 Subject: [PATCH] 15 --- 2022/15/README.md | 11 ++++ 2022/15/python/main.py | 119 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 2022/15/README.md create mode 100644 2022/15/python/main.py diff --git a/2022/15/README.md b/2022/15/README.md new file mode 100644 index 0000000..048660e --- /dev/null +++ b/2022/15/README.md @@ -0,0 +1,11 @@ +# 15 + +Ugly solution. The second part took me a good minute to compute. Each line is done quite efficiently, it could def. be improved by sorting out what lines we should consider in the first place. + +
+ Solutions +
    +
  1. 1
  2. +
  3. 13784551204480
  4. +
+
diff --git a/2022/15/python/main.py b/2022/15/python/main.py new file mode 100644 index 0000000..6675f5d --- /dev/null +++ b/2022/15/python/main.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python + +import sys +from dataclasses import dataclass +from functools import lru_cache +from os.path import dirname, join +from typing import Union + +# Day 15 + +# Common + + +def read_input(filename): + data = join(dirname(__file__), '..', filename) + with open(data) as f: + return f.read().strip() + + +test = read_input('test.txt') +data = read_input('input.txt') + + +@dataclass(unsafe_hash=True) +class Point: + x: int + y: int + + @lru_cache(maxsize=None) + def manhattan(self, other: 'Point'): + return abs(self.x - other.x) + abs(self.y - other.y) + + def tuning_freq(self) -> int: + return (self.x * 4000000) + self.y + + @staticmethod + def parse(s: str) -> 'Point': + return Point(*map(lambda s: int(s[2:]), s.split(', '))) # Format: x=8, y=7 + + +@dataclass +class Interval: + start: int + end: int + + def __contains__(self, other: Union[int, 'Interval']) -> bool: + if type(other) is int: + return self.start <= other and other <= self.end + elif isinstance(other, Interval): + return other.start in self or other.end in self + return False + + def __add__(self, other: 'Interval') -> 'Interval': + return Interval(min(self.start, other.start), max(self.end, other.end)) + + def __len__(self) -> int: + return abs(self.start - self.end) + 1 + + @staticmethod + def reduce(intervals: list['Interval']): + combined: list['Interval'] = [] + for interval in intervals: + for added in combined: + if added in interval or interval in added: + combined.remove(added) + combined.append(added + interval) + break + else: + combined.append(interval) + return combined if combined == intervals else Interval.reduce(combined) + + +@dataclass +class Map: + sensors: dict[Point, Point] + max_distance: int + + def analyse_line(self, line: int, minimum: int | float, maximum: int | float): + intervals: list[Interval] = [] + for sensor, beacon in self.sensors.items(): + radius = sensor.manhattan(beacon) # Radius of the sensor + dy = radius - abs(sensor.y - line) # Check if line is in the radius of the sensor + if dy >= 0: + # Add interval for scanned line + intervals.append(Interval(max(minimum, sensor.x-dy), min(maximum, sensor.x+dy))) + intervals = Interval.reduce(intervals) + return sum([len(i) for i in intervals]), intervals + + def flag_1(self, line: int): + score, _ = self.analyse_line(line, float('-inf'), float('inf')) + return score - 1 + + def flag_2(self, limit: int) -> int: + for line in range(limit): + score, intervals = self.analyse_line(line, 0, limit) + if score != limit + 1: + print(line, intervals) + return Point(intervals[0].end + 1, line).tuning_freq() + raise Exception("Not found") + + @ staticmethod + def parse(data: str) -> 'Map': + sensors: dict[Point, Point] = {} + max_distance = 0 + for line in data.splitlines(): + sensor, beacon = line.split(':') + s = Point.parse(sensor[10:]) + b = Point.parse(beacon[22:]) + sensors[s] = b + max_distance = max(max_distance, s.manhattan(b)) + return Map(sensors, max_distance) + + +# Running + +m = Map.parse(data) +print(m.flag_1(2000000)) +print(Point(3446137, 3204480).tuning_freq()) +# print(m.flag_2(4_000_000))