97
day05/day05.py
Executable file
97
day05/day05.py
Executable file
@@ -0,0 +1,97 @@
|
||||
#!/usr/bin/env python3
|
||||
from collections import defaultdict
|
||||
import dataclasses
|
||||
import sys
|
||||
from typing import DefaultDict, Generator, Sequence, Tuple
|
||||
import re
|
||||
from itertools import repeat
|
||||
import pprint
|
||||
|
||||
|
||||
SEG_PAT = re.compile(r"(?P<x1>[\d]+),(?P<y1>[\d]+) -> (?P<x2>[\d]+),(?P<y2>[\d]+)")
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class Seg:
|
||||
x1: int
|
||||
y1: int
|
||||
x2: int
|
||||
y2: int
|
||||
|
||||
@staticmethod
|
||||
def from_str(s: str) -> "Seg":
|
||||
match = SEG_PAT.match(s)
|
||||
assert match
|
||||
return Seg(
|
||||
int(match["x1"]), int(match["y1"]), int(match["x2"]), int(match["y2"])
|
||||
)
|
||||
|
||||
def points(self, diag: bool) -> Generator[Tuple[int, int], None, None]:
|
||||
if self.x1 == self.x2:
|
||||
# We're going up and down
|
||||
if self.y1 > self.y2:
|
||||
lo = self.y2
|
||||
hi = self.y1
|
||||
else:
|
||||
lo = self.y1
|
||||
hi = self.y2
|
||||
yield from zip(repeat(self.x1), range(lo, hi + 1))
|
||||
elif self.y1 == self.y2:
|
||||
# We're going side to side
|
||||
if self.x1 > self.x2:
|
||||
lo = self.x2
|
||||
hi = self.x1
|
||||
else:
|
||||
lo = self.x1
|
||||
hi = self.x2
|
||||
yield from zip(range(lo, hi + 1), repeat(self.y1))
|
||||
elif diag:
|
||||
# Figure out the slope
|
||||
if self.x1 > self.x2:
|
||||
# right to left
|
||||
lox = self.x2
|
||||
hix = self.x1
|
||||
loy = self.y2
|
||||
hiy = self.y1
|
||||
else:
|
||||
lox = self.x1
|
||||
hix = self.x2
|
||||
loy = self.y1
|
||||
hiy = self.y2
|
||||
slope = float(hiy - loy) / float(hix - lox)
|
||||
yf = float(loy)
|
||||
for x in range(lox, hix + 1):
|
||||
yield (x, int(yf))
|
||||
yf += slope
|
||||
else:
|
||||
# No support for diagonals
|
||||
return None
|
||||
|
||||
|
||||
def part1(segments: Sequence[Seg]):
|
||||
# Create a counting dict, we can probably avoid the matrix stuff
|
||||
points: DefaultDict[Tuple[int, int], int] = defaultdict(lambda: 0)
|
||||
for seg in segments:
|
||||
for point in seg.points(diag=False):
|
||||
points[point] += 1
|
||||
# Figure out how many locations where at least 2 lines overlap
|
||||
count = len([overlap for overlap in points.values() if overlap > 1])
|
||||
print(f"{count} overlaps >1")
|
||||
|
||||
|
||||
def part2(segments: Sequence[Seg]):
|
||||
# Create a counting dict, we can probably avoid the matrix stuff
|
||||
points: DefaultDict[Tuple[int, int], int] = defaultdict(lambda: 0)
|
||||
for seg in segments:
|
||||
for point in seg.points(diag=True):
|
||||
points[point] += 1
|
||||
# Figure out how many locations where at least 2 lines overlap
|
||||
count = len([overlap for overlap in points.values() if overlap > 1])
|
||||
print(f"{count} overlaps >1")
|
||||
|
||||
|
||||
segments = [Seg.from_str(line) for line in sys.stdin]
|
||||
print("Part 1")
|
||||
part1(segments)
|
||||
print("Part 2")
|
||||
part2(segments)
|
||||
Reference in New Issue
Block a user