Skip to content

Commit

Permalink
day 21
Browse files Browse the repository at this point in the history
  • Loading branch information
Tom-the-Bomb committed Dec 25, 2023
1 parent b028791 commit 876ff3c
Show file tree
Hide file tree
Showing 7 changed files with 233 additions and 25 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ Solutions in `Python 3` :snake: and `Rust` :crab:
- `./run [day]` - Executes __python__ solutions
- if `day` is not provided, it executes **all** solutions
- `./runrs [day]` - Executes __rust__ solutions
- if `day` is not provided, it executes **all** solutions
- if `day` is not provided, it executes **all** solutions

Only the `python` solutions are well documented
2 changes: 2 additions & 0 deletions aoc-py/solutions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
from .day18 import Day18
from .day19 import Day19
from .day20 import Day20
from .day21 import Day21

from ..solution import Solution

Expand All @@ -49,6 +50,7 @@
Day6, Day7, Day8, Day9, Day10,
Day11, Day12, Day13, Day14, Day15,
Day16, Day17, Day18, Day19, Day20,
Day21,
)

del Solution
1 change: 1 addition & 0 deletions aoc-py/solutions/day10.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class Day10(Solution):
}

def _get_starting_pos(self, grid: list[str]) -> tuple[int, int]:
"""Finds and returns the coordinates (indices) of the starting position, the character 'S'"""
for i, row in enumerate(grid):
for j, char in enumerate(row):
if char == 'S':
Expand Down
111 changes: 91 additions & 20 deletions aoc-py/solutions/day21.py
Original file line number Diff line number Diff line change
@@ -1,37 +1,108 @@
"""
Day 1: Trebuchet?!
Day 21: Step Counter
https://adventofcode.com/2023/day/1
https://adventofcode.com/2023/day/21
"""
__all__ = ('Day1',)
__all__ = ('Day21',)

from typing import ClassVar

from ..solution import Solution

class Day1(Solution):
NAME: ClassVar[str] = 'Trebuchet?!'
NUM_MAP: ClassVar[dict[str, str]] = {
'one': '1',
'two': '2',
'three': '3',
'four': '4',
'five': '5',
'six': '6',
'seven': '7',
'eight': '8',
'nine': '9'
}
class Day21(Solution):
NAME: ClassVar[str] = 'Step Counter'

def _get_starting_pos(self, grid: list[str]) -> tuple[int, int]:
"""Finds and returns the coordinates (indices) of the starting position, the character 'S'"""
for i, row in enumerate(grid):
for j, char in enumerate(row):
if char == 'S':
return i, j
raise ValueError("No 'S' character found in grid")

def traverse(
self,
grid: list[str],
start: tuple[int, int],
*,
steps: int
) -> int:
initial_wrap = (int(), int())

to_check = [(start, initial_wrap, steps)]
traversed = {(start, initial_wrap)}
n_reached = 0

n_rows = len(grid)
n_cols = len(grid[0])

while to_check:
(row, col), (n_row_wraps, n_col_wraps), steps_left = to_check.pop(0)

if steps_left % 2 == 0:
n_reached += 1

if steps_left > 0:
directions = (
(row, col + 1),
(row, col - 1),
(row + 1, col),
(row - 1, col),
)

for new_row, new_col in directions:
# gets the amount of times we wrap around the grid's repetition cycles
# also the "n-th cycle" of the grid content
#
# i.e. when we are in th initial, given grid:
# `n_row_wraps` and `n_col_wraps` are both `0`
#
# quotient of dividing the current `index` by the number of rows/cols
# needs to be stored in `traversed` to allow the set to distinguish
# between coordinates that are the same, but are on different cycles/wraps of the grid
new_row_wraps, new_row = divmod(new_row, n_rows)
new_col_wraps, new_col = divmod(new_col, n_cols)
new_row_wraps += n_row_wraps
new_col_wraps += n_col_wraps

if (
new_row in range(n_rows)
and new_col in range(n_cols)
and grid[new_row][new_col] != '#'
and (pos := ((new_row, new_col), (new_row_wraps, new_col_wraps))) not in traversed
):
to_check.append(pos + (steps_left - 1,))
traversed.add(pos)
return n_reached

def part_one(self, inp: str) -> int:
...
grid = inp.splitlines()
start = self._get_starting_pos(grid)

return self.traverse(grid, start, steps=64)

def part_two(self, inp: str) -> int:
...
grid = inp.splitlines()

n_rows = len(grid)
n = 26501365 // n_rows
start = self._get_starting_pos(grid)
start_x, _ = start

t1 = self.traverse(grid, start, steps=start_x)
t2 = self.traverse(grid, start, steps=start_x + n_rows)
t3 = self.traverse(grid, start, steps=start_x + n_rows + n_rows)

return (
(n ** 2 - n)
* ((t3 + t1) // 2 - t2)
+ n * (t2 - t1)
+ t1
)

def run(self, inp: str) -> None:
print('Part 1:', p1 := self.part_one(inp))
print('Part 2:', p2 := self.part_two(inp))

assert p1 == 53651
assert p2 == 53894
assert p1 == 3743
assert p2 == 618261433219147
Loading

0 comments on commit 876ff3c

Please sign in to comment.