Skip to content

Commit

Permalink
Added Day 17 and 18
Browse files Browse the repository at this point in the history
  • Loading branch information
GeertLitjens committed Dec 18, 2023
1 parent f707fe9 commit 2b80d04
Show file tree
Hide file tree
Showing 8 changed files with 203 additions and 0 deletions.
5 changes: 5 additions & 0 deletions src/advent_of_code_2023/days/day17/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
### Part 1
<PART_1_TEXT>

### Part 2
<PART_2_TEXT>
Empty file.
49 changes: 49 additions & 0 deletions src/advent_of_code_2023/days/day17/solution.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"""
"""

import heapq

import numpy as np

from advent_of_code_2023.utils import Solution


class DaySolution(Solution):
def __init__(self: "DaySolution", day: int = 17, year: int = 2023) -> None:
super().__init__(day, year)

def _parse_data(self: "DaySolution", input_data: str) -> np.ndarray:
""" """
return np.array([[int(x) for x in line] for line in input_data.splitlines()])

def min_heat_loss(
self: "DaySolution", edges: np.ndarray, least: int = 1, most: int = 3
) -> int:
queue = [(0, 0, 0, 0, 0)]
seen = set()
while queue:
heat, x, y, px, py = heapq.heappop(queue)
if (x, y) == (edges.shape[0] - 1, edges.shape[1] - 1):
return heat
if (x, y, px, py) in seen:
continue
seen.add((x, y, px, py))
# calculate turns only
for dx, dy in {(1, 0), (0, 1), (-1, 0), (0, -1)} - {(px, py), (-px, -py)}:
a, b, h = x, y, heat
# enter 4-10 moves in the chosen direction
for i in range(1, most + 1):
a, b = a + dx, b + dy
if 0 <= a < edges.shape[0] and 0 <= b < edges.shape[1]:
h += edges[a, b]
if i >= least:
heapq.heappush(queue, (h, a, b, dx, dy))
return 0

def _solve_part1(self: "DaySolution", parsed_data: np.ndarray) -> str:
""" """
return str(self.min_heat_loss(parsed_data))

def _solve_part2(self: "DaySolution", parsed_data: np.ndarray) -> str:
""" """
return str(self.min_heat_loss(parsed_data, 4, 10))
5 changes: 5 additions & 0 deletions src/advent_of_code_2023/days/day18/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
### Part 1
<PART_1_TEXT>

### Part 2
<PART_2_TEXT>
Empty file.
71 changes: 71 additions & 0 deletions src/advent_of_code_2023/days/day18/solution.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
"""
"""

import numpy as np

from advent_of_code_2023.utils import Solution


def shoelace(x_y: list[tuple[int, int]]) -> float:
x_y_arr = np.array(x_y)
x_y_arr = x_y_arr.reshape(-1, 2)

x = x_y_arr[:, 0]
y = x_y_arr[:, 1]

S1 = np.sum(x * np.roll(y, -1))
S2 = np.sum(y * np.roll(x, -1))

area = 0.5 * np.absolute(S1 - S2)

return area


class DaySolution(Solution):
def __init__(self: "DaySolution", day: int = 18, year: int = 2023) -> None:
super().__init__(day, year)

def _parse_data(self: "DaySolution", input_data: str) -> list[list[str]]:
""" """
return [
line.replace("(", "").replace(")", "").split(" ", 3)
for line in input_data.splitlines()
]

def _solve_part1(self: "DaySolution", parsed_data: list[list[str]]) -> str:
""" """
polygon = [(0, 0)]
bound_length = 0
for line in reversed(parsed_data):
dr, lngth, _ = line
bound_length += int(lngth)
match dr:
case "R":
polygon.append((polygon[-1][0], polygon[-1][1] + int(lngth)))
case "U":
polygon.append((polygon[-1][0] - int(lngth), polygon[-1][1]))
case "D":
polygon.append((polygon[-1][0] + int(lngth), polygon[-1][1]))
case "L":
polygon.append((polygon[-1][0], polygon[-1][1] - int(lngth)))
return str(int(shoelace(polygon) + bound_length // 2 + 1))

def _solve_part2(self: "DaySolution", parsed_data: list[list[str]]) -> str:
""" """
polygon = [(0, 0)]
bound_length = 0
nr_to_dr = {"0": "R", "1": "D", "2": "L", "3": "U"}
for line in reversed(parsed_data):
_, _, code = line
lngth = int(code[1:-1], 16)
bound_length += int(lngth)
match nr_to_dr[code[-1]]:
case "R":
polygon.append((polygon[-1][0], polygon[-1][1] + int(lngth)))
case "U":
polygon.append((polygon[-1][0] - int(lngth), polygon[-1][1]))
case "D":
polygon.append((polygon[-1][0] + int(lngth), polygon[-1][1]))
case "L":
polygon.append((polygon[-1][0], polygon[-1][1] - int(lngth)))
return str(int(shoelace(polygon) + bound_length // 2 + 1))
36 changes: 36 additions & 0 deletions tests/test_day17.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import pytest

from advent_of_code_2023.days.day17.solution import DaySolution # type: ignore


@pytest.fixture
def day_testdata() -> str:
return """\
2413432311323
3215453535623
3255245654254
3446585845452
4546657867536
1438598798454
4457876987766
3637877979653
4654967986887
4564679986453
1224686865563
2546548887735
4322674655533\
"""


def test_part1(day_testdata: str) -> None:
sol = DaySolution()
parsed_data = sol._parse_data(day_testdata)
result = sol._solve_part1(parsed_data)
assert result == "102"


def test_part2(day_testdata: str) -> None:
sol = DaySolution()
parsed_data = sol._parse_data(day_testdata)
result = sol._solve_part2(parsed_data)
assert result == "94"
37 changes: 37 additions & 0 deletions tests/test_day18.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import pytest

from advent_of_code_2023.days.day18.solution import DaySolution # type: ignore


@pytest.fixture
def day_testdata() -> str:
return """\
R 6 (#70c710)
D 5 (#0dc571)
L 2 (#5713f0)
D 2 (#d2c081)
R 2 (#59c680)
D 2 (#411b91)
L 5 (#8ceee2)
U 2 (#caa173)
L 1 (#1b58a2)
U 2 (#caa171)
R 2 (#7807d2)
U 3 (#a77fa3)
L 2 (#015232)
U 2 (#7a21e3)\
"""


def test_part1(day_testdata: str) -> None:
sol = DaySolution()
parsed_data = sol._parse_data(day_testdata)
result = sol._solve_part1(parsed_data)
assert result == "62"


def test_part2(day_testdata: str) -> None:
sol = DaySolution()
parsed_data = sol._parse_data(day_testdata)
result = sol._solve_part2(parsed_data)
assert result == "952408144115"

0 comments on commit 2b80d04

Please sign in to comment.