day15 finished
This commit is contained in:
parent
334debccc6
commit
d5edd049ce
6 changed files with 214 additions and 0 deletions
|
|
@ -8,6 +8,7 @@ I use python 3.11 without any libraries beyond the standard.
|
||||||
|
|
||||||
| Day | Time | Rank | Score | Time | Rank | Score |
|
| Day | Time | Rank | Score | Time | Rank | Score |
|
||||||
| --- | --------- | ----- | ----- | -------- | ----- | ----- |
|
| --- | --------- | ----- | ----- | -------- | ----- | ----- |
|
||||||
|
| 15 | 00:58:10 | 3963 | 0 | 02:26:22 | 4011 | 0 |
|
||||||
| 14 | 00:58:39 | 4431 | 0 | 01:18:15 | 4620 | 0 |
|
| 14 | 00:58:39 | 4431 | 0 | 01:18:15 | 4620 | 0 |
|
||||||
| 13 | 01:23:44 | 5522 | 0 | 01:45:59 | 5610 | 0 |
|
| 13 | 01:23:44 | 5522 | 0 | 01:45:59 | 5610 | 0 |
|
||||||
| 12 | 01:43:14 | 6571 | 0 | 01:51:15 | 6246 | 0 |
|
| 12 | 01:43:14 | 6571 | 0 | 01:51:15 | 6246 | 0 |
|
||||||
|
|
|
||||||
0
advent/days/day15/__init__.py
Normal file
0
advent/days/day15/__init__.py
Normal file
32
advent/days/day15/data/input.txt
Normal file
32
advent/days/day15/data/input.txt
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
2000000/4000000
|
||||||
|
Sensor at x=3859432, y=2304903: closest beacon is at x=3677247, y=3140958
|
||||||
|
Sensor at x=2488890, y=2695345: closest beacon is at x=1934788, y=2667279
|
||||||
|
Sensor at x=3901948, y=701878: closest beacon is at x=4095477, y=368031
|
||||||
|
Sensor at x=2422190, y=1775708: closest beacon is at x=1765036, y=2000000
|
||||||
|
Sensor at x=2703846, y=3282799: closest beacon is at x=2121069, y=3230302
|
||||||
|
Sensor at x=172003, y=2579074: closest beacon is at x=-77667, y=3197309
|
||||||
|
Sensor at x=1813149, y=1311283: closest beacon is at x=1765036, y=2000000
|
||||||
|
Sensor at x=1704453, y=2468117: closest beacon is at x=1934788, y=2667279
|
||||||
|
Sensor at x=1927725, y=2976002: closest beacon is at x=1934788, y=2667279
|
||||||
|
Sensor at x=3176646, y=1254463: closest beacon is at x=2946873, y=2167634
|
||||||
|
Sensor at x=2149510, y=3722117: closest beacon is at x=2121069, y=3230302
|
||||||
|
Sensor at x=3804434, y=251015: closest beacon is at x=4095477, y=368031
|
||||||
|
Sensor at x=2613561, y=3932220: closest beacon is at x=2121069, y=3230302
|
||||||
|
Sensor at x=3997794, y=3291220: closest beacon is at x=3677247, y=3140958
|
||||||
|
Sensor at x=98328, y=3675176: closest beacon is at x=-77667, y=3197309
|
||||||
|
Sensor at x=2006541, y=2259601: closest beacon is at x=1934788, y=2667279
|
||||||
|
Sensor at x=663904, y=122919: closest beacon is at x=1618552, y=-433244
|
||||||
|
Sensor at x=1116472, y=3349728: closest beacon is at x=2121069, y=3230302
|
||||||
|
Sensor at x=2810797, y=2300748: closest beacon is at x=2946873, y=2167634
|
||||||
|
Sensor at x=1760767, y=2024355: closest beacon is at x=1765036, y=2000000
|
||||||
|
Sensor at x=3098487, y=2529092: closest beacon is at x=2946873, y=2167634
|
||||||
|
Sensor at x=1716839, y=634872: closest beacon is at x=1618552, y=-433244
|
||||||
|
Sensor at x=9323, y=979154: closest beacon is at x=-245599, y=778791
|
||||||
|
Sensor at x=1737623, y=2032367: closest beacon is at x=1765036, y=2000000
|
||||||
|
Sensor at x=26695, y=3049071: closest beacon is at x=-77667, y=3197309
|
||||||
|
Sensor at x=3691492, y=3766350: closest beacon is at x=3677247, y=3140958
|
||||||
|
Sensor at x=730556, y=1657010: closest beacon is at x=1765036, y=2000000
|
||||||
|
Sensor at x=506169, y=3958647: closest beacon is at x=-77667, y=3197309
|
||||||
|
Sensor at x=2728744, y=23398: closest beacon is at x=1618552, y=-433244
|
||||||
|
Sensor at x=3215227, y=3077078: closest beacon is at x=3677247, y=3140958
|
||||||
|
Sensor at x=2209379, y=3030851: closest beacon is at x=2121069, y=3230302
|
||||||
15
advent/days/day15/data/test01.txt
Normal file
15
advent/days/day15/data/test01.txt
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
10/20
|
||||||
|
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
|
||||||
117
advent/days/day15/solution.py
Normal file
117
advent/days/day15/solution.py
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
from typing import Iterator, Self
|
||||||
|
|
||||||
|
day_num = 15
|
||||||
|
|
||||||
|
|
||||||
|
def part1(lines: Iterator[str]) -> int:
|
||||||
|
row, _ = next(lines).split('/')
|
||||||
|
sensor_map = SensorMap.parse(lines)
|
||||||
|
return sensor_map.count_impossible(int(row))
|
||||||
|
|
||||||
|
|
||||||
|
def part2(lines: Iterator[str]) -> int:
|
||||||
|
_, max_range = next(lines).split('/')
|
||||||
|
sensor_map = SensorMap.parse(lines)
|
||||||
|
return sensor_map.get_possible_frequency(int(max_range))
|
||||||
|
|
||||||
|
|
||||||
|
Position = tuple[int, int]
|
||||||
|
ColRange = tuple[int, int]
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(slots=True, frozen=True)
|
||||||
|
class Sensor:
|
||||||
|
sensor: Position
|
||||||
|
distance: int
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def parse(cls, line: str) -> tuple[Self, Position]:
|
||||||
|
parts = line.split('=')
|
||||||
|
sensor = int(parts[1].split(',')[0].strip()), int(parts[2].split(':')[0].strip())
|
||||||
|
beacon = int(parts[3].split(',')[0].strip()), int(parts[4].strip())
|
||||||
|
return cls(sensor, Sensor.manhatten(sensor, beacon)), beacon
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def manhatten(cls, first: Position, other: Position) -> int:
|
||||||
|
return abs(first[0] - other[0]) + abs(first[1] - other[1])
|
||||||
|
|
||||||
|
def col_range_at_row(self, row: int) -> ColRange | None:
|
||||||
|
col_distance = self.distance - abs(self.sensor[1] - row)
|
||||||
|
if col_distance < 0:
|
||||||
|
return None
|
||||||
|
|
||||||
|
from_x = self.sensor[0] - col_distance
|
||||||
|
to_x = self.sensor[0] + col_distance
|
||||||
|
|
||||||
|
return from_x, to_x
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(slots=True, frozen=True)
|
||||||
|
class SensorMap:
|
||||||
|
sensors: list[Sensor]
|
||||||
|
beacons: set[Position]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def parse(cls, lines: Iterator[str]) -> SensorMap:
|
||||||
|
sensors: list[Sensor] = []
|
||||||
|
beacons: set[Position] = set()
|
||||||
|
for line in lines:
|
||||||
|
sensor, beacon = Sensor.parse(line)
|
||||||
|
sensors.append(sensor)
|
||||||
|
beacons.add(beacon)
|
||||||
|
return cls(sensors, beacons)
|
||||||
|
|
||||||
|
def get_impossible(self, row: int) -> list[ColRange]:
|
||||||
|
col_ranges: list[ColRange] = []
|
||||||
|
|
||||||
|
for sensor in self.sensors:
|
||||||
|
x_range = sensor.col_range_at_row(row)
|
||||||
|
if x_range is None:
|
||||||
|
continue
|
||||||
|
from_x, to_x = x_range
|
||||||
|
col_ranges.append((from_x, to_x))
|
||||||
|
|
||||||
|
return col_ranges
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def merged_col_ranges(cls, col_ranges: list[ColRange]) -> Iterator[ColRange]:
|
||||||
|
col_ranges = sorted(col_ranges)
|
||||||
|
current = col_ranges[0]
|
||||||
|
for col_range in col_ranges:
|
||||||
|
if current[1] < col_range[1]:
|
||||||
|
if current[1] < col_range[0]:
|
||||||
|
yield current
|
||||||
|
current = col_range
|
||||||
|
else:
|
||||||
|
current = current[0], col_range[1]
|
||||||
|
yield current
|
||||||
|
|
||||||
|
def count_impossible(self, row: int) -> int:
|
||||||
|
col_ranges = self.get_impossible(row)
|
||||||
|
|
||||||
|
seen = sum(rng[1] - rng[0] + 1 for rng in SensorMap.merged_col_ranges(col_ranges))
|
||||||
|
beacons = len({beacon[0] for beacon in self.beacons if beacon[1] == row})
|
||||||
|
|
||||||
|
return seen - beacons
|
||||||
|
|
||||||
|
def get_possible(self, max_range: int) -> Position:
|
||||||
|
for row in range(max_range):
|
||||||
|
col_ranges = sorted(self.get_impossible(row))
|
||||||
|
|
||||||
|
curr1 = col_ranges[0][1]
|
||||||
|
for one0, one1 in col_ranges:
|
||||||
|
if curr1 < one1:
|
||||||
|
if curr1 < one0:
|
||||||
|
return curr1 + 1, row
|
||||||
|
if one1 > max_range:
|
||||||
|
break
|
||||||
|
curr1 = one1
|
||||||
|
|
||||||
|
raise Exception("No best spot found")
|
||||||
|
|
||||||
|
def get_possible_frequency(self, max_range: int) -> int:
|
||||||
|
freq_x, freq_y = self.get_possible(max_range)
|
||||||
|
return freq_x * 4_000_000 + freq_y
|
||||||
49
advent/days/day15/test_solution.py
Normal file
49
advent/days/day15/test_solution.py
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
from advent.common import input
|
||||||
|
|
||||||
|
from .solution import Sensor, SensorMap, day_num, part1, part2
|
||||||
|
|
||||||
|
|
||||||
|
def test_part1():
|
||||||
|
lines = input.read_lines(day_num, 'test01.txt')
|
||||||
|
expected = 26
|
||||||
|
result = part1(lines)
|
||||||
|
assert result == expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_part2():
|
||||||
|
lines = input.read_lines(day_num, 'test01.txt')
|
||||||
|
expected = 56000011
|
||||||
|
result = part2(lines)
|
||||||
|
assert result == expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse():
|
||||||
|
input = "Sensor at x=2, y=18: closest beacon is at x=-2, y=15"
|
||||||
|
expected = Sensor((2, 18), 7), (-2, 15)
|
||||||
|
result = Sensor.parse(input)
|
||||||
|
assert result == expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_x_range():
|
||||||
|
input = "Sensor at x=8, y=7: closest beacon is at x=2, y=10"
|
||||||
|
sensor, _ = Sensor.parse(input)
|
||||||
|
assert sensor.col_range_at_row(10) == (2, 14)
|
||||||
|
assert sensor.col_range_at_row(11) == (3, 13)
|
||||||
|
|
||||||
|
|
||||||
|
def test_impossible():
|
||||||
|
lines = input.read_lines(day_num, 'test01.txt')
|
||||||
|
next(lines)
|
||||||
|
sensor_map = SensorMap.parse(lines)
|
||||||
|
expected = 26
|
||||||
|
result = sensor_map.count_impossible(10)
|
||||||
|
assert result == expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_possible():
|
||||||
|
lines = input.read_lines(day_num, 'test01.txt')
|
||||||
|
next(lines)
|
||||||
|
sensor_map = SensorMap.parse(lines)
|
||||||
|
expected = 56000011
|
||||||
|
result = sensor_map.get_possible_frequency(20)
|
||||||
|
assert result == expected
|
||||||
Loading…
Add table
Add a link
Reference in a new issue