diff --git a/README.md b/README.md index 82b1825..2b02c58 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ - 2020 - ★★★★★ ★★★★★ ★★★★★ ☆ - 2021 - ★★★★★ ★★★★★ ★★★★★ ★★★ - 2022 - ★★★★★ ★★★★★ ★★★★★ ★☆ -- 2023 - ★★★★☆ ★☆★★☆ ★ +- 2023 - ★★★★☆ ★☆★★☆ ★☆ ## How to use diff --git a/src/year2023/day12a.py b/src/year2023/day12a.py new file mode 100644 index 0000000..874fe92 --- /dev/null +++ b/src/year2023/day12a.py @@ -0,0 +1,58 @@ +"""2023 - Day 12 Part 1: Hot Springs""" +from dataclasses import dataclass +from typing import Self + + +@dataclass +class Row: + springs: str + segments: list[int] + + @property + def arrangements(self) -> int: + n = len(self.segments) + + def dfs(i: int, j: int, filled: int) -> int: + if i == len(self.springs): + if (j == n and filled == 0) or ( + j == n - 1 and filled == self.segments[j] + ): + return 1 + else: + return 0 + + count = 0 + + if self.springs[i] in {".", "?"}: + if filled == 0: + count += dfs(i + 1, j, filled) + elif filled == self.segments[j]: + count += dfs(i + 1, j + 1, 0) + + if self.springs[i] in {"#", "?"}: + if j < n and filled < self.segments[j]: + count += dfs(i + 1, j, filled + 1) + + return count + + return dfs(0, 0, 0) + + @classmethod + def from_line(cls, line: str) -> Self: + springs, segments_part = line.split() + segments = [int(x) for x in segments_part.split(",")] + return cls(springs, segments) + + +@dataclass +class Field: + rows: list[Row] + + @classmethod + def from_task(cls, task: str) -> Self: + return cls([Row.from_line(line) for line in task.splitlines()]) + + +def solve(task: str) -> int: + field = Field.from_task(task) + return sum(r.arrangements for r in field.rows) diff --git a/tests/src/year2023/test_day12a.py b/tests/src/year2023/test_day12a.py new file mode 100644 index 0000000..0411f16 --- /dev/null +++ b/tests/src/year2023/test_day12a.py @@ -0,0 +1,36 @@ +"""2023 - Day 12 Part 1: Hot Springs""" +from textwrap import dedent + +import pytest + +from src.year2023.day12a import Row +from src.year2023.day12a import solve + + +@pytest.mark.parametrize( + "line,expected", + ( + ("???.### 1,1,3", 1), + (".??..??...?##. 1,1,3", 4), + ("?#?#?#?#?#?#?#? 1,3,1,6", 1), + ("????.#...#... 4,1,1", 1), + ("????.######..#####. 1,6,5", 4), + ("?###???????? 3,2,1", 10), + ), +) +def test_row(line, expected): + assert Row.from_line(line).arrangements == expected + + +def test_solve(): + task = dedent( + """ + ???.### 1,1,3 + .??..??...?##. 1,1,3 + ?#?#?#?#?#?#?#? 1,3,1,6 + ????.#...#... 4,1,1 + ????.######..#####. 1,6,5 + ?###???????? 3,2,1 + """ + ).strip() + assert solve(task) == 21