Skip to content

Commit

Permalink
Merge pull request #10 from tyler-hoffman/day-06
Browse files Browse the repository at this point in the history
Day 06
  • Loading branch information
tyler-hoffman authored Dec 7, 2024
2 parents ca7ff41 + 6be0616 commit 7dade4a
Show file tree
Hide file tree
Showing 10 changed files with 220 additions and 0 deletions.
Empty file added aoc_2024/day_06/__init__.py
Empty file.
67 changes: 67 additions & 0 deletions aoc_2024/day_06/a.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from dataclasses import dataclass
from functools import cached_property
from aoc_2024.day_06.parser import Parser
from aoc_2024.utils.point import Point

DIRECTIONS = [Point(0, -1), Point(1, 0), Point(0, 1), Point(-1, 0)]


@dataclass
class Day06PartASolver:
grid: list[list[str]]

@property
def solution(self) -> int:
visited = set[Point]()
direction_index = 0
pos = self.start
while pos.x >= 0 and pos.y >= 0 and pos.x < self.width and pos.y < self.height:
visited.add(pos)
next_pos = pos.add(DIRECTIONS[direction_index])
if next_pos in self.obstacles:
direction_index = (direction_index + 1) % 4
else:
pos = next_pos
return len(visited)

@cached_property
def width(self) -> int:
return len(self.grid[0])

@cached_property
def height(self) -> int:
return len(self.grid)

@cached_property
def obstacles(self) -> set[Point]:
output = set[Point]()
for y, line in enumerate(self.grid):
for x, char in enumerate(line):
if char == "#":
output.add(Point(x, y))
return output

@cached_property
def start(self) -> Point:
for y, line in enumerate(self.grid):
for x, char in enumerate(line):
if char == "^":
return Point(x, y)
assert False, "How'd we get here?"


def solve(input: str) -> int:
data = Parser.parse(input)
solver = Day06PartASolver(data)

return solver.solution


def get_solution() -> int:
with open("aoc_2024/day_06/input.txt", "r") as f:
input = f.read()
return solve(input)


if __name__ == "__main__":
print(get_solution())
92 changes: 92 additions & 0 deletions aoc_2024/day_06/b.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
from dataclasses import dataclass
from functools import cached_property
from aoc_2024.day_06.parser import Parser
from aoc_2024.utils.point import Point

DIRECTIONS = [Point(0, -1), Point(1, 0), Point(0, 1), Point(-1, 0)]


@dataclass
class Day06PartBSolver:
grid: list[list[str]]

@property
def solution(self) -> int:
output = 0
for extra_obstacle in self.to_check:
if self.has_loop(self.obstacles | {extra_obstacle}):
output += 1
return output

def has_loop(self, obstacles: set[Point]) -> bool:
visited = set[tuple[Point, int]]()
direction_index = 0
pos = self.start
while pos.x >= 0 and pos.y >= 0 and pos.x < self.width and pos.y < self.height:
key = (pos, direction_index)
if key in visited:
return True
else:
visited.add(key)
next_pos = pos.add(DIRECTIONS[direction_index])
if next_pos in obstacles:
direction_index = (direction_index + 1) % 4
else:
pos = next_pos
return False

@cached_property
def to_check(self) -> set[Point]:
points = set[Point]()
direction_index = 0
pos = self.start
while pos.x >= 0 and pos.y >= 0 and pos.x < self.width and pos.y < self.height:
points.add(pos)
next_pos = pos.add(DIRECTIONS[direction_index])
if next_pos in self.obstacles:
direction_index = (direction_index + 1) % 4
else:
pos = next_pos
return points - {self.start}

@cached_property
def width(self) -> int:
return len(self.grid[0])

@cached_property
def height(self) -> int:
return len(self.grid)

@cached_property
def obstacles(self) -> set[Point]:
output = set[Point]()
for y, line in enumerate(self.grid):
for x, char in enumerate(line):
if char == "#":
output.add(Point(x, y))
return output

@cached_property
def start(self) -> Point:
for y, line in enumerate(self.grid):
for x, char in enumerate(line):
if char == "^":
return Point(x, y)
assert False, "How'd we get here?"


def solve(input: str) -> int:
data = Parser.parse(input)
solver = Day06PartBSolver(data)

return solver.solution


def get_solution() -> int:
with open("aoc_2024/day_06/input.txt", "r") as f:
input = f.read()
return solve(input)


if __name__ == "__main__":
print(get_solution())
Binary file added aoc_2024/day_06/from_prompt.py
Binary file not shown.
Binary file added aoc_2024/day_06/input.txt
Binary file not shown.
5 changes: 5 additions & 0 deletions aoc_2024/day_06/parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class Parser:
@staticmethod
def parse(input: str) -> list[list[str]]:
lines = input.strip().splitlines()
return [list(line) for line in lines]
36 changes: 36 additions & 0 deletions aoc_2024/utils/point.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from __future__ import annotations
from dataclasses import dataclass


@dataclass(frozen=True)
class Point:
x: int
y: int

@property
def unit(self) -> Point:
x = 0 if self.x == 0 else self.x // abs(self.x)
y = 0 if self.y == 0 else self.y // abs(self.y)

return Point(x, y)

def add(self, other: Point) -> Point:
return Point(self.x + other.x, self.y + other.y)

def subtract(self, other: Point) -> Point:
return Point(self.x - other.x, self.y - other.y)

def multiply(self, amt: int) -> Point:
return Point(self.x * amt, self.y * amt)

def dist(self, other: Point) -> int:
return abs(self.x - other.x) + abs(self.y - other.y)

@property
def neighbors(self) -> set[Point]:
return {
Point(self.x, self.y - 1),
Point(self.x + 1, self.y),
Point(self.x, self.y + 1),
Point(self.x - 1, self.y),
}
Empty file added tests/test_day_06/__init__.py
Empty file.
10 changes: 10 additions & 0 deletions tests/test_day_06/test_a.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from aoc_2024.day_06.a import get_solution, solve
from aoc_2024.day_06.from_prompt import SAMPLE_DATA, SAMPLE_SOLUTION_A, SOLUTION_A


def test_solve():
assert solve(SAMPLE_DATA) == SAMPLE_SOLUTION_A


def test_my_solution():
assert get_solution() == SOLUTION_A
10 changes: 10 additions & 0 deletions tests/test_day_06/test_b.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from aoc_2024.day_06.b import get_solution, solve
from aoc_2024.day_06.from_prompt import SAMPLE_DATA, SAMPLE_SOLUTION_B, SOLUTION_B


def test_solve():
assert solve(SAMPLE_DATA) == SAMPLE_SOLUTION_B


def test_my_solution():
assert get_solution() == SOLUTION_B

0 comments on commit 7dade4a

Please sign in to comment.