Skip to content

Commit

Permalink
AoC 2022 Day 15 Part 2
Browse files Browse the repository at this point in the history
  • Loading branch information
loociano committed Dec 17, 2022
1 parent e084321 commit 6f31dbb
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 26 deletions.
57 changes: 40 additions & 17 deletions aoc2022/src/day15/python/solution.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@ def get_manhattan_distance(position_a: Position, position_b: Position) -> int:


class Sensor:
def __init__(self, x: int, y: int, bx: int, by: int, row: int):
def __init__(self, x: int, y: int, bx: int, by: int):
self._sensor_pos = (x, y)
self._closest_beacon_pos = (bx, by)
self._distance = get_manhattan_distance(position_a=self._sensor_pos,
position_b=self._closest_beacon_pos)
self.intervals_at_row: tuple[Interval, ...] = self._calc_intervals_at_row(
row=row)
# Will be calculated later.
self.intervals_at_row: Optional[tuple[Interval, ...]] = None

def _calc_intervals_at_row(self, row: int) -> Optional[tuple[Interval, ...]]:
def calc_intervals_at_row(self, row: int) -> Optional[tuple[Interval, ...]]:
if (row < self._sensor_pos[1] - self._distance
or row > self._sensor_pos[1] + self._distance):
return None
Expand Down Expand Up @@ -62,19 +62,23 @@ def _calc_intervals_at_row(self, row: int) -> Optional[tuple[Interval, ...]]:
)


def _parse_sensors_data(sensors_data: Sequence[str], row: int) -> tuple[Sensor]:
def _parse_sensors_data(sensors_data: Sequence[str],
) -> tuple[tuple[Sensor, ...], set[Position]]:
sensors: list[Sensor] = []
beacon_positions = set()
for sensor_data in sensors_data:
matches = re.search(
r'Sensor at x=(-?\d+), y=(-?\d+): closest beacon is at x=(-?\d+), y=(-?\d+)',
sensor_data)
bx = int(matches.group(3))
by = int(matches.group(4))
sensors.append(Sensor(
x=int(matches.group(1)),
y=int(matches.group(2)),
bx=int(matches.group(3)),
by=int(matches.group(4)),
row=row))
return tuple(sensors)
bx=bx,
by=by))
beacon_positions.add((bx, by))
return tuple(sensors), beacon_positions


def _merge_intervals(intervals: tuple[Interval, ...]) -> tuple[Interval, ...]:
Expand All @@ -85,9 +89,9 @@ def _merge_intervals(intervals: tuple[Interval, ...]) -> tuple[Interval, ...]:
else:
merged.append([begin, end])
copy = []
for i in merged:
copy.append(tuple(i))
return tuple(merged)
for interval in merged:
copy.append((interval[0], interval[1]))
return tuple(copy)


def _count_positions(intervals: tuple[Interval, ...]) -> int:
Expand All @@ -97,13 +101,32 @@ def _count_positions(intervals: tuple[Interval, ...]) -> int:
return count


def count_not_beacon_positions(sensors_data: Sequence[str], row: int) -> int:
sensors: tuple[Sensor] = _parse_sensors_data(sensors_data, row)
print('Parsed all!')
def _calc_merged_intervals_at_row(
sensors: tuple[Sensor, ...]) -> tuple[Interval, ...]:
intervals = []
for sensor in sensors:
if sensor.intervals_at_row is not None:
for interval_at_row in sensor.intervals_at_row:
intervals.append(interval_at_row)
merged_intervals = _merge_intervals(tuple(intervals))
return _count_positions(merged_intervals)
return _merge_intervals(tuple(intervals))


def count_not_beacon_positions(sensors_data: Sequence[str], row: int) -> int:
sensors, _ = _parse_sensors_data(sensors_data)
for sensor in sensors:
sensor.intervals_at_row = sensor.calc_intervals_at_row(row)
return _count_positions(_calc_merged_intervals_at_row(sensors))


def find_tuning_frequency(sensors_data: Sequence[str],
known_range: Interval) -> int:
sensors, beacon_positions = _parse_sensors_data(sensors_data)
for row in range(known_range[0], known_range[1] + 1):
for sensor in sensors:
sensor.intervals_at_row = sensor.calc_intervals_at_row(row)
merged = _calc_merged_intervals_at_row(sensors)
if len(merged) > 1:
x = merged[0][1] + 1
if (x, row) not in beacon_positions:
return 4000000 * x + row
raise ValueError('Did not find frequency!')
36 changes: 27 additions & 9 deletions aoc2022/test/day15/python/test_solution.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
# limitations under the License.
import unittest

from aoc2022.src.day15.python.solution import get_manhattan_distance, count_not_beacon_positions
from aoc2022.src.day15.python.solution import get_manhattan_distance, \
count_not_beacon_positions, find_tuning_frequency
from common.python3.AdventOfCodeTestCase import AdventOfCodeTestCase


Expand All @@ -22,13 +23,20 @@ def __init__(self, *args, **kwargs):
super(TestDay15Solution, self).__init__(__file__, *args, **kwargs)

def test_manhattanDistance_success(self):
self.assertEqual(7, get_manhattan_distance(position_a=(2, 18), position_b=(-2, 15)))
self.assertEqual(1, get_manhattan_distance(position_a=(9, 16), position_b=(10, 16)))
self.assertEqual(3, get_manhattan_distance(position_a=(13, 2), position_b=(15, 3)))
self.assertEqual(4, get_manhattan_distance(position_a=(12, 14), position_b=(10, 16)))
self.assertEqual(4, get_manhattan_distance(position_a=(10, 20), position_b=(10, 16)))
self.assertEqual(5, get_manhattan_distance(position_a=(14, 17), position_b=(10, 16)))
self.assertEqual(9, get_manhattan_distance(position_a=(8, 7), position_b=(2, 10)))
self.assertEqual(7, get_manhattan_distance(position_a=(2, 18),
position_b=(-2, 15)))
self.assertEqual(1, get_manhattan_distance(position_a=(9, 16),
position_b=(10, 16)))
self.assertEqual(3, get_manhattan_distance(position_a=(13, 2),
position_b=(15, 3)))
self.assertEqual(4, get_manhattan_distance(position_a=(12, 14),
position_b=(10, 16)))
self.assertEqual(4, get_manhattan_distance(position_a=(10, 20),
position_b=(10, 16)))
self.assertEqual(5, get_manhattan_distance(position_a=(14, 17),
position_b=(10, 16)))
self.assertEqual(9, get_manhattan_distance(position_a=(8, 7),
position_b=(2, 10)))
# Also...
# 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
Expand All @@ -42,7 +50,17 @@ def test_part1_withExample_correctCount(self):
self.assertEqual(26, count_not_beacon_positions(self.examples[0], row=10))

def test_part2_withPuzzleInput_correctCount(self):
self.assertEqual(5112034, count_not_beacon_positions(self.input, row=2000000))
self.assertEqual(5112034,
count_not_beacon_positions(self.input, row=2000000))

def test_part2_withExample_success(self):
self.assertEqual(56000011, find_tuning_frequency(self.examples[0],
known_range=(0, 20)))

def test_part2_withPuzzleInput_success(self):
self.assertEqual(13172087230812,
find_tuning_frequency(self.input,
known_range=(0, 4000000)))


if __name__ == '__main__':
Expand Down

0 comments on commit 6f31dbb

Please sign in to comment.