Skip to content

Commit

Permalink
Day 14 (#22)
Browse files Browse the repository at this point in the history
* Get parser working

* Solve 14.a

* Solve 14.b
  • Loading branch information
tyler-hoffman authored Dec 14, 2023
1 parent 54e07ec commit 2a26f9f
Show file tree
Hide file tree
Showing 9 changed files with 231 additions and 0 deletions.
Empty file added aoc_2023/day_14/__init__.py
Empty file.
64 changes: 64 additions & 0 deletions aoc_2023/day_14/a.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from dataclasses import dataclass
from functools import cached_property
from aoc_2023.day_14.parser import Parser


@dataclass
class Day14PartASolver:
panel: list[list[str]]

@property
def solution(self) -> int:
panel = self.tilt_north(self.panel)
return self.count_north_load(panel)

def count_north_load(self, panel: list[list[str]]) -> int:
output = 0
for x in range(self.width):
for y in range(self.height):
if panel[y][x] == "O":
output += self.height - y
return output

def tilt_north(self, panel: list[list[str]]) -> list[list[str]]:
output = [["." for _ in range(self.width)] for _ in range(self.height)]
for x in range(self.width):
dest = 0
for y in range(self.height):
match panel[y][x]:
case ".":
...
case "#":
output[y][x] = "#"
dest = y + 1
case "O":
output[dest][x] = "O"
dest += 1
case _:
assert False
return output

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

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


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

return solver.solution


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


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


@dataclass
class Day14PartBSolver:
panel: list[list[str]]
cycles: int = 1000000000

@property
def solution(self) -> int:
seen_at = dict[tuple[tuple[str, ...], ...], int]()

panel = self.panel
for i in range(self.cycles):
panel = self.do_cycle(panel)
as_tuple = tuple([tuple([x for x in line]) for line in panel])
if as_tuple in seen_at:
first_sighted = seen_at[as_tuple]
cycle_length = i - first_sighted
cycles_left = self.cycles - i - 1
offset = cycles_left % cycle_length
index = first_sighted + offset
for p, j in seen_at.items():
if j == index:
return self.count_north_load([[x for x in line] for line in p])
else:
seen_at[as_tuple] = i
return self.count_north_load(panel)

def do_cycle(self, panel: list[list[str]]) -> list[list[str]]:
panel = self.tilt_north(panel)
panel = self.tilt_west(panel)
panel = self.tilt_south(panel)
panel = self.tilt_east(panel)
return panel

def count_north_load(self, panel: list[list[str]]) -> int:
output = 0
for x in range(self.width):
for y in range(self.height):
if panel[y][x] == "O":
output += self.height - y
return output

def tilt_north(self, panel: list[list[str]]) -> list[list[str]]:
output = [["." for _ in range(self.width)] for _ in range(self.height)]
for x in range(self.width):
dest = 0
for y in range(self.height):
match panel[y][x]:
case ".":
...
case "#":
output[y][x] = "#"
dest = y + 1
case "O":
output[dest][x] = "O"
dest += 1
case _:
assert False
return output

def tilt_south(self, panel: list[list[str]]) -> list[list[str]]:
output = [["." for _ in range(self.width)] for _ in range(self.height)]
for x in range(self.width):
dest = self.height - 1
for y in range(self.height - 1, -1, -1):
match panel[y][x]:
case ".":
...
case "#":
output[y][x] = "#"
dest = y - 1
case "O":
output[dest][x] = "O"
dest -= 1
case _:
assert False
return output

def tilt_west(self, panel: list[list[str]]) -> list[list[str]]:
output = [["." for _ in range(self.width)] for _ in range(self.height)]
for y in range(self.height):
dest = 0
for x in range(self.width):
match panel[y][x]:
case ".":
...
case "#":
output[y][x] = "#"
dest = x + 1
case "O":
output[y][dest] = "O"
dest += 1
case _:
assert False
return output

def tilt_east(self, panel: list[list[str]]) -> list[list[str]]:
output = [["." for _ in range(self.width)] for _ in range(self.height)]
for y in range(self.height):
dest = self.width - 1
for x in range(self.width - 1, -1, -1):
match panel[y][x]:
case ".":
...
case "#":
output[y][x] = "#"
dest = x - 1
case "O":
output[y][dest] = "O"
dest -= 1
case _:
assert False
return output

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

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


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

return solver.solution


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


if __name__ == "__main__":
print(get_solution())
Binary file added aoc_2023/day_14/from_prompt.py
Binary file not shown.
Binary file added aoc_2023/day_14/input.txt
Binary file not shown.
5 changes: 5 additions & 0 deletions aoc_2023/day_14/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]
Empty file added tests/test_day_14/__init__.py
Empty file.
10 changes: 10 additions & 0 deletions tests/test_day_14/test_a.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from aoc_2023.day_14.a import get_solution, solve
from aoc_2023.day_14.from_prompt import SAMPLE_DATA, SAMPLE_SOLUTION_A


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


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


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


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

0 comments on commit 2a26f9f

Please sign in to comment.