2022-15 py
Signed-off-by: AKP <tom@tdpain.net>
This commit is contained in:
parent
63cff023cc
commit
0b38b0f7fd
8 changed files with 233 additions and 12 deletions
|
@ -35,14 +35,14 @@ def parse(instr: str) -> Tuple[Scan, Vector, Vector]:
|
||||||
f = lambda x, y: x + y
|
f = lambda x, y: x + y
|
||||||
if dy < 0:
|
if dy < 0:
|
||||||
f = lambda x, y: x - y
|
f = lambda x, y: x - y
|
||||||
|
|
||||||
for j in range(abs(dy) + 1):
|
for j in range(abs(dy) + 1):
|
||||||
res[(points[i][0], f(points[i][1], j))] = State.WALL
|
res[(points[i][0], f(points[i][1], j))] = State.WALL
|
||||||
else:
|
else:
|
||||||
f = lambda x, y: x + y
|
f = lambda x, y: x + y
|
||||||
if dx < 0:
|
if dx < 0:
|
||||||
f = lambda x, y: x - y
|
f = lambda x, y: x - y
|
||||||
|
|
||||||
for j in range(abs(dx) + 1):
|
for j in range(abs(dx) + 1):
|
||||||
res[(f(points[i][0], j), points[i][1])] = State.WALL
|
res[(f(points[i][0], j), points[i][1])] = State.WALL
|
||||||
|
|
||||||
|
@ -51,16 +51,11 @@ def parse(instr: str) -> Tuple[Scan, Vector, Vector]:
|
||||||
max_x = max(keys, key=lambda x: x[0])[0]
|
max_x = max(keys, key=lambda x: x[0])[0]
|
||||||
min_y = min(keys, key=lambda x: x[1])[1]
|
min_y = min(keys, key=lambda x: x[1])[1]
|
||||||
max_y = max(keys, key=lambda x: x[1])[1]
|
max_y = max(keys, key=lambda x: x[1])[1]
|
||||||
|
|
||||||
return res, (min_x, min_y), (max_x, max_y)
|
return res, (min_x, min_y), (max_x, max_y)
|
||||||
|
|
||||||
|
|
||||||
def is_out_of_bounds(v: Vector, min_v: Vector, max_v: Vector) -> bool:
|
|
||||||
return not ((min_v[0] <= v[0] <= max_v[0]) and (0 <= v[1] <= max_v[1]))
|
|
||||||
|
|
||||||
|
|
||||||
class Challenge(BaseChallenge):
|
class Challenge(BaseChallenge):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def one(instr: str) -> int:
|
def one(instr: str) -> int:
|
||||||
inp, min_pos, max_pos = parse(instr)
|
inp, min_pos, max_pos = parse(instr)
|
||||||
|
@ -68,7 +63,9 @@ class Challenge(BaseChallenge):
|
||||||
cursor = (500, 0)
|
cursor = (500, 0)
|
||||||
grains = 0
|
grains = 0
|
||||||
|
|
||||||
while not is_out_of_bounds(cursor, min_pos, max_pos):
|
while (min_pos[0] <= cursor[0] <= max_pos[0]) and (
|
||||||
|
0 <= cursor[1] <= max_pos[1]
|
||||||
|
):
|
||||||
x, y = cursor
|
x, y = cursor
|
||||||
|
|
||||||
if inp.get((x, y + 1)) is None:
|
if inp.get((x, y + 1)) is None:
|
||||||
|
@ -91,7 +88,7 @@ class Challenge(BaseChallenge):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def two(instr: str) -> int:
|
def two(instr: str) -> int:
|
||||||
inp, min_pos, max_pos = parse(instr)
|
inp, _, max_pos = parse(instr)
|
||||||
|
|
||||||
max_pos = (max_pos[0], max_pos[1] + 2)
|
max_pos = (max_pos[0], max_pos[1] + 2)
|
||||||
|
|
||||||
|
|
1
challenges/2022/15-beaconExclusionZone/README.md
Normal file
1
challenges/2022/15-beaconExclusionZone/README.md
Normal file
|
@ -0,0 +1 @@
|
||||||
|
# [Day 15: Beacon Exclusion Zone](https://adventofcode.com/2022/day/15)
|
15
challenges/2022/15-beaconExclusionZone/benchmark.json
Normal file
15
challenges/2022/15-beaconExclusionZone/benchmark.json
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"day": 15,
|
||||||
|
"dir": "challenges/2022/15-beaconExclusionZone",
|
||||||
|
"implementations": {
|
||||||
|
"Python": {
|
||||||
|
"part.1.avg": 2.488755464553833,
|
||||||
|
"part.1.max": 2.488755464553833,
|
||||||
|
"part.1.min": 2.488755464553833,
|
||||||
|
"part.2.avg": 295.4520,
|
||||||
|
"part.2.max": 295.4520,
|
||||||
|
"part.2.min": 295.4520
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"numRuns": 1
|
||||||
|
}
|
17
challenges/2022/15-beaconExclusionZone/info.json
Normal file
17
challenges/2022/15-beaconExclusionZone/info.json
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"inputFile": "input.txt",
|
||||||
|
"testCases": {
|
||||||
|
"one": [
|
||||||
|
{
|
||||||
|
"input": "Sensor at x=2, y=18: closest beacon is at x=-2, y=15\nSensor at x=9, y=16: closest beacon is at x=10, y=16\nSensor at x=13, y=2: closest beacon is at x=15, y=3\nSensor at x=12, y=14: closest beacon is at x=10, y=16\nSensor at x=10, y=20: closest beacon is at x=10, y=16\nSensor at x=14, y=17: closest beacon is at x=10, y=16\nSensor at x=8, y=7: closest beacon is at x=2, y=10\nSensor at x=2, y=0: closest beacon is at x=2, y=10\nSensor at x=0, y=11: closest beacon is at x=2, y=10\nSensor at x=20, y=14: closest beacon is at x=25, y=17\nSensor at x=17, y=20: closest beacon is at x=21, y=22\nSensor at x=16, y=7: closest beacon is at x=15, y=3\nSensor at x=14, y=3: closest beacon is at x=15, y=3\nSensor at x=20, y=1: closest beacon is at x=15, y=3\n",
|
||||||
|
"expected": "26"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"two": [
|
||||||
|
{
|
||||||
|
"input": "Sensor at x=2, y=18: closest beacon is at x=-2, y=15\nSensor at x=9, y=16: closest beacon is at x=10, y=16\nSensor at x=13, y=2: closest beacon is at x=15, y=3\nSensor at x=12, y=14: closest beacon is at x=10, y=16\nSensor at x=10, y=20: closest beacon is at x=10, y=16\nSensor at x=14, y=17: closest beacon is at x=10, y=16\nSensor at x=8, y=7: closest beacon is at x=2, y=10\nSensor at x=2, y=0: closest beacon is at x=2, y=10\nSensor at x=0, y=11: closest beacon is at x=2, y=10\nSensor at x=20, y=14: closest beacon is at x=25, y=17\nSensor at x=17, y=20: closest beacon is at x=21, y=22\nSensor at x=16, y=7: closest beacon is at x=15, y=3\nSensor at x=14, y=3: closest beacon is at x=15, y=3\nSensor at x=20, y=1: closest beacon is at x=15, y=3\n",
|
||||||
|
"expected": "56000011"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
143
challenges/2022/15-beaconExclusionZone/py/__init__.py
Normal file
143
challenges/2022/15-beaconExclusionZone/py/__init__.py
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
import sys
|
||||||
|
from typing import *
|
||||||
|
from aocpy import BaseChallenge, Vector, foldl
|
||||||
|
import re
|
||||||
|
from dataclasses import dataclass
|
||||||
|
import portion as P
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Line:
|
||||||
|
m: int
|
||||||
|
c: int
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_points(a: Vector, b: Vector) -> Line:
|
||||||
|
calc_m = (b.y - a.y) / (b.x - a.x)
|
||||||
|
calc_c = a.y - (calc_m * a.x)
|
||||||
|
return Line(int(calc_m), int(calc_c))
|
||||||
|
|
||||||
|
|
||||||
|
parse_re = re.compile(
|
||||||
|
r"Sensor at x=(-?\d+), y=(-?\d+): closest beacon is at x=(-?\d+), y=(-?\d+)"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def parse(instr: str) -> List[Tuple[Vector, Vector]]:
|
||||||
|
res = []
|
||||||
|
for line in instr.strip().splitlines():
|
||||||
|
sensor_x, sensor_y, beacon_x, beacon_y = map(int, parse_re.match(line).groups())
|
||||||
|
res.append(
|
||||||
|
(
|
||||||
|
Vector(sensor_x, sensor_y),
|
||||||
|
Vector(beacon_x, beacon_y),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def postprocess(
|
||||||
|
inp: List[Tuple[Vector, Vector]]
|
||||||
|
) -> Tuple[
|
||||||
|
Dict[Vector, Tuple[Vector, Vector, Vector, Vector]],
|
||||||
|
Dict[Vector, Tuple[Line, Line, Line, Line]],
|
||||||
|
]:
|
||||||
|
# 0 top, 1 left, 2 right, 3 bottom
|
||||||
|
sensor_points: Dict[Vector, Tuple[Vector, Vector, Vector, Vector]] = {}
|
||||||
|
sensor_lines: Dict[Vector, Tuple[Line, Line, Line, Line]] = {}
|
||||||
|
|
||||||
|
for (sensor, beacon) in inp:
|
||||||
|
mh = sensor.manhattan_distance(beacon)
|
||||||
|
points = (
|
||||||
|
Vector(sensor.x, sensor.y - mh),
|
||||||
|
Vector(sensor.x - mh, sensor.y),
|
||||||
|
Vector(sensor.x + mh, sensor.y),
|
||||||
|
Vector(sensor.x, sensor.y + mh),
|
||||||
|
)
|
||||||
|
sensor_points[sensor] = points
|
||||||
|
|
||||||
|
lines = (
|
||||||
|
Line.from_points(points[1], points[0]),
|
||||||
|
Line.from_points(points[3], points[1]),
|
||||||
|
Line.from_points(points[2], points[0]),
|
||||||
|
Line.from_points(points[2], points[3]),
|
||||||
|
)
|
||||||
|
sensor_lines[sensor] = lines
|
||||||
|
|
||||||
|
return sensor_points, sensor_lines
|
||||||
|
|
||||||
|
|
||||||
|
class Challenge(BaseChallenge):
|
||||||
|
@staticmethod
|
||||||
|
def one(instr: str) -> int:
|
||||||
|
inp = parse(instr)
|
||||||
|
sensor_points, sensor_lines = postprocess(inp)
|
||||||
|
|
||||||
|
target_line = 10 if len(inp) == 14 else 2000000
|
||||||
|
ranges: List[Tuple[int, int]] = []
|
||||||
|
|
||||||
|
for (sensor, _) in inp:
|
||||||
|
points = sensor_points[sensor]
|
||||||
|
# print("polygon(", (", ".join(map(str, (points[0], points[1], points[3], points[2])))), ")")
|
||||||
|
lines = sensor_lines[sensor]
|
||||||
|
if points[0].y <= target_line <= points[3].y:
|
||||||
|
if target_line == points[1].y:
|
||||||
|
ranges.append((points[2].x, points[1].x))
|
||||||
|
elif points[0].y <= target_line < points[1].y:
|
||||||
|
ranges.append(
|
||||||
|
(
|
||||||
|
int((target_line - lines[2].c) / lines[2].m),
|
||||||
|
int((target_line - lines[0].c) / lines[0].m),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
ranges.append(
|
||||||
|
(
|
||||||
|
int((target_line - lines[3].c) / lines[3].m),
|
||||||
|
int((target_line - lines[1].c) / lines[1].m),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
s: Set[int] = set()
|
||||||
|
for x in ranges:
|
||||||
|
s = s.union(set(range(x[1], x[0] + 1)))
|
||||||
|
|
||||||
|
for (_, beacon) in inp:
|
||||||
|
if beacon.y == target_line:
|
||||||
|
s.discard(beacon.x)
|
||||||
|
|
||||||
|
return len(s)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def two(instr: str) -> int:
|
||||||
|
inp = parse(instr)
|
||||||
|
sensor_points, sensor_lines = postprocess(inp)
|
||||||
|
|
||||||
|
max_coord = 20 if len(inp) == 14 else 4000000
|
||||||
|
line_ranges: List[P.Interval] = [P.empty() for _ in range(max_coord + 1)]
|
||||||
|
|
||||||
|
for (sensor, _) in inp:
|
||||||
|
p0, p1, p2, p3 = sensor_points[sensor]
|
||||||
|
l0, l1, l2, l3 = sensor_lines[sensor]
|
||||||
|
|
||||||
|
for y in range(max(p0.y, 0), min(p3.y, max_coord) + 1):
|
||||||
|
a: int
|
||||||
|
b: int
|
||||||
|
if y == sensor.y:
|
||||||
|
a, b = p1.x, p2.x
|
||||||
|
elif p0.y <= y < p1.y:
|
||||||
|
a, b = int((y - l0.c) / l0.m), int((y - l2.c) / l2.m)
|
||||||
|
else:
|
||||||
|
a, b = int((y - l1.c) / l1.m), int((y - l3.c) / l3.m)
|
||||||
|
|
||||||
|
line_ranges[y] = line_ranges[y] | P.closed(
|
||||||
|
max(a, 0), min(b, max_coord) + 1
|
||||||
|
)
|
||||||
|
|
||||||
|
for y in range(len(line_ranges)):
|
||||||
|
res = line_ranges[y]
|
||||||
|
if not res.atomic:
|
||||||
|
return (4000000 * res[0].upper) + y
|
||||||
|
|
||||||
|
return 0
|
|
@ -28,4 +28,4 @@ Solutions to the [2022 Advent of Code](https://adventofcode.com/2022).
|
||||||
| 12 - Hill Climbing Algorithm | ☆ ☆ | | |
|
| 12 - Hill Climbing Algorithm | ☆ ☆ | | |
|
||||||
| 13 - Distress Signal | ☆ ☆ | | |
|
| 13 - Distress Signal | ☆ ☆ | | |
|
||||||
| 14 - Regolith Reservoir | ★ ★ | [Python](14-regolithReservoir/py) | Simulating falling sand |
|
| 14 - Regolith Reservoir | ★ ★ | [Python](14-regolithReservoir/py) | Simulating falling sand |
|
||||||
|
| 15 - Beacon Exclusion Zone | ★ ★ | [Python](15-beaconExclusionZone/py) | Searching through a 4000000^2 size grid for a literal single empty spot |
|
Binary file not shown.
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 51 KiB |
|
@ -1,3 +1,4 @@
|
||||||
|
from __future__ import annotations
|
||||||
from typing import Any, TypeVar, Callable, Iterable
|
from typing import Any, TypeVar, Callable, Iterable
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,4 +27,51 @@ def foldl(p: Callable[[U, T], U], i: Iterable[T], start: U) -> U:
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def foldr(p: Callable[[U, T], U], i: Iterable[T], start: U) -> U:
|
def foldr(p: Callable[[U, T], U], i: Iterable[T], start: U) -> U:
|
||||||
return foldl(p, reversed(i), start)
|
return foldl(p, reversed(i), start)
|
||||||
|
|
||||||
|
|
||||||
|
class Vector:
|
||||||
|
x: int
|
||||||
|
y: int
|
||||||
|
|
||||||
|
def __init__(self, x: int, y: int):
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _is_vector_tuple(o: Any) -> bool:
|
||||||
|
return type(o) == tuple and len(o) == 2 and type(o[0]) == int and type(o[1]) == int
|
||||||
|
|
||||||
|
def manhattan_distance(self, o: Vector) -> int:
|
||||||
|
return abs(self.x - o.x) + abs(self.y - o.y)
|
||||||
|
|
||||||
|
def __add__(self, o: Any) -> Vector:
|
||||||
|
if Vector._is_vector_tuple(o):
|
||||||
|
return Vector(self.x + o[0], self.y + o[1])
|
||||||
|
elif type(o) == Vector:
|
||||||
|
return Vector(self.x + o.x, self.y + o.y)
|
||||||
|
else:
|
||||||
|
raise ValueError(f"cannot add Vector and {type(o)}")
|
||||||
|
|
||||||
|
def __sub__(self, o: Any) -> Vector:
|
||||||
|
if Vector._is_vector_tuple(o):
|
||||||
|
return Vector(self.x - o[0], self.y - o[1])
|
||||||
|
elif type(o) == Vector:
|
||||||
|
return Vector(self.x - o.x, self.y - o.y)
|
||||||
|
else:
|
||||||
|
raise ValueError(f"cannot subtract Vector and {type(o)}")
|
||||||
|
|
||||||
|
def __eq__(self, o: Any) -> bool:
|
||||||
|
if Vector._is_vector_tuple(o):
|
||||||
|
return self.x == o[0] and self.y == o[1]
|
||||||
|
elif type(o) == Vector:
|
||||||
|
return self.x == o.x and self.y == o.y
|
||||||
|
else:
|
||||||
|
raise ValueError(f"cannot equate Vector and {type(o)}")
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
# return f"Vector(x={self.x}, y={self.y})"
|
||||||
|
return f"({self.x}, {self.y})"
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return hash(f"{self.x},{self.y}")
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue