Skip to content

Commit

Permalink
Implement 2024 day 20
Browse files Browse the repository at this point in the history
  • Loading branch information
bertptrs committed Dec 20, 2024
1 parent fdaadfe commit 395463d
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 0 deletions.
82 changes: 82 additions & 0 deletions 2024/src/aoc/days/day20.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import itertools

import numpy

from . import SeparateRunner

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

CHEATS = [
(-2, 0),
(2, 0),
(0, -2),
(0, 2),
]


def parse_path(input: str) -> dict[tuple[int, int], int]:
grid = numpy.array(list(map(list, input.strip().split("\n"))))

ys, xs = numpy.nonzero(grid == "S")
sx, sy = int(xs[0]), int(ys[0])

nx, ny = sx, sy

path = {
(sx, sy): 0,
}

while grid[ny, nx] != "E":
x, y = nx, ny

for dx, dy in DIRECTIONS:
if grid[y + dy, x + dx] == "#" or (x + dx, y + dy) in path:
continue
nx = x + dx
ny = y + dy
break

path[nx, ny] = len(path)
return path


def get_savings(a: tuple[tuple[int, int], int], b: tuple[tuple[int, int], int]) -> int:
(ax, ay), ad = a
(bx, by), bd = b

dist = abs(bx - ax) + abs(by - ay)
if dist <= 20:
return bd - ad - dist
else:
return 0


class DayRunner(SeparateRunner):
@classmethod
def part1(cls, input: str, limit: int = 100) -> int:
path = parse_path(input)

total = 0

for (px, py), dist in path.items():
for dx, dy in CHEATS:
if (other := path.get((px + dx, py + dy))) is not None:
savings = dist - other - 2
if savings >= limit:
total += 1

return total

@classmethod
def part2(cls, input: str, limit: int = 100) -> int:
path = parse_path(input)

return sum(
get_savings(a, b) >= limit
for a, b in itertools.combinations(path.items(), 2)
)
15 changes: 15 additions & 0 deletions 2024/tests/samples/20.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
###############
#...#...#.....#
#.#.#.#.#.###.#
#S#...#.#.#...#
#######.#.#.###
#######.#.#...#
#######.#.###.#
###..E#...#...#
###.#######.###
#...###...#...#
#.#####.#.###.#
#.#...#.#.#...#
#.#.#.#.#.#.###
#...#...#...###
###############
11 changes: 11 additions & 0 deletions 2024/tests/test_day20.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from aoc.days.day20 import DayRunner

from . import get_data


def test_sample_part1() -> None:
assert DayRunner.part1(get_data(20), limit=1) == 44


def test_sample_part2() -> None:
assert DayRunner.part2(get_data(20), limit=50) == 285

0 comments on commit 395463d

Please sign in to comment.