Implement day 8 solver
This commit is contained in:
110
puzzles/8.py
Normal file
110
puzzles/8.py
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
from itertools import combinations
|
||||||
|
from typing import NamedTuple
|
||||||
|
|
||||||
|
test_input = """
|
||||||
|
............
|
||||||
|
........0...
|
||||||
|
.....0......
|
||||||
|
.......0....
|
||||||
|
....0.......
|
||||||
|
......A.....
|
||||||
|
............
|
||||||
|
............
|
||||||
|
........A...
|
||||||
|
.........A..
|
||||||
|
............
|
||||||
|
............
|
||||||
|
""".strip()
|
||||||
|
|
||||||
|
test_solution_p1 = 14
|
||||||
|
test_solution_p2 = 34
|
||||||
|
|
||||||
|
|
||||||
|
def solve_p1(puzzle_input: str) -> int:
|
||||||
|
grid = _parse_signal_grid(puzzle_input)
|
||||||
|
antinodes = _calculate_antinodes(grid)
|
||||||
|
# _print_grid(grid, antinodes)
|
||||||
|
return len(antinodes)
|
||||||
|
|
||||||
|
|
||||||
|
def solve_p2(puzzle_input: str) -> int:
|
||||||
|
grid = _parse_signal_grid(puzzle_input)
|
||||||
|
antinodes = _calculate_antinodes(grid, harmonic=True)
|
||||||
|
# _print_grid(grid, antinodes)
|
||||||
|
return len(antinodes)
|
||||||
|
|
||||||
|
|
||||||
|
class Point(NamedTuple):
|
||||||
|
x: int
|
||||||
|
y: int
|
||||||
|
|
||||||
|
|
||||||
|
class SignalGrid(NamedTuple):
|
||||||
|
width: int
|
||||||
|
height: int
|
||||||
|
antenna_arrays: dict[str, set[Point]]
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_signal_grid(puzzle_input: str) -> SignalGrid:
|
||||||
|
lines = puzzle_input.strip().split("\n")
|
||||||
|
antenna_arrays: dict[str, set[Point]] = {}
|
||||||
|
|
||||||
|
for x, line in enumerate(lines):
|
||||||
|
for y, char in enumerate(line):
|
||||||
|
if char.isalnum():
|
||||||
|
if char not in antenna_arrays:
|
||||||
|
antenna_arrays[char] = set()
|
||||||
|
antenna_arrays[char].add(Point(x, y))
|
||||||
|
|
||||||
|
return SignalGrid(len(lines[0]), len(lines), antenna_arrays)
|
||||||
|
|
||||||
|
|
||||||
|
def _calculate_antinodes(grid: SignalGrid, harmonic=False) -> set[Point]:
|
||||||
|
antinodes: set[Point] = set()
|
||||||
|
|
||||||
|
for antenna_array in grid.antenna_arrays.values():
|
||||||
|
for a, b in combinations(antenna_array, 2):
|
||||||
|
dx = b.x - a.x
|
||||||
|
dy = b.y - a.y
|
||||||
|
|
||||||
|
# Left antinodes
|
||||||
|
if harmonic:
|
||||||
|
antinode = a
|
||||||
|
while True:
|
||||||
|
antinodes.add(antinode)
|
||||||
|
antinode = Point(antinode.x - dx, antinode.y - dy)
|
||||||
|
if not _is_in_grid(grid, antinode):
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
antinode = Point(a.x - dx, a.y - dy)
|
||||||
|
if _is_in_grid(grid, antinode):
|
||||||
|
antinodes.add(antinode)
|
||||||
|
|
||||||
|
# Right antinodes
|
||||||
|
if harmonic:
|
||||||
|
antinode = b
|
||||||
|
while True:
|
||||||
|
antinodes.add(antinode)
|
||||||
|
antinode = Point(antinode.x + dx, antinode.y + dy)
|
||||||
|
if not _is_in_grid(grid, antinode):
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
antinode = Point(b.x + dx, b.y + dy)
|
||||||
|
if _is_in_grid(grid, antinode):
|
||||||
|
antinodes.add(antinode)
|
||||||
|
|
||||||
|
return antinodes
|
||||||
|
|
||||||
|
|
||||||
|
def _is_in_grid(grid: SignalGrid, point: Point) -> bool:
|
||||||
|
return 0 <= point.x < grid.height and 0 <= point.y < grid.width
|
||||||
|
|
||||||
|
|
||||||
|
def _print_grid(grid: SignalGrid, antinodes: set[Point]):
|
||||||
|
output = [["."] * grid.width for _ in range(grid.height)]
|
||||||
|
for antinode in antinodes:
|
||||||
|
output[antinode.x][antinode.y] = "#"
|
||||||
|
for frequency, antenna_array in grid.antenna_arrays.items():
|
||||||
|
for antenna in antenna_array:
|
||||||
|
output[antenna.x][antenna.y] = frequency
|
||||||
|
print("\n".join("".join(row) for row in output))
|
Reference in New Issue
Block a user