From 65ef6c9debe652e87dce6f1906cfd020577de61e Mon Sep 17 00:00:00 2001 From: Pavel Karateev Date: Sat, 16 Dec 2023 13:51:32 +0100 Subject: [PATCH] 2023 day 10 part 1 --- README.md | 2 +- src/year2023/day10a.py | 87 +++++++++++++++++++++++++++++++ tests/src/year2023/test_day10a.py | 30 +++++++++++ 3 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 src/year2023/day10a.py create mode 100644 tests/src/year2023/test_day10a.py diff --git a/README.md b/README.md index 437d8aa..ae164f0 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ - 2020 - ★★★★★ ★★★★★ ★★★★★ ☆ - 2021 - ★★★★★ ★★★★★ ★★★★★ ★★★ - 2022 - ★★★★★ ★★★★★ ★★★★★ ★☆ -- 2023 - ★★★★☆ ★☆★★ +- 2023 - ★★★★☆ ★☆★★☆ ## How to use diff --git a/src/year2023/day10a.py b/src/year2023/day10a.py new file mode 100644 index 0000000..40c073c --- /dev/null +++ b/src/year2023/day10a.py @@ -0,0 +1,87 @@ +"""2023 - Day 10 Part 1: Pipe Maze""" +from collections import defaultdict +from typing import TypeAlias + +C: TypeAlias = tuple[int, int] +Map: TypeAlias = dict[C, set[C]] +Land: TypeAlias = list[list[str]] + +# | - L J 7 F . S +N_CONN = {"S", "|", "L", "J"} +E_CONN = {"S", "-", "L", "F"} +S_CONN = {"S", "|", "7", "F"} +W_CONN = {"S", "-", "7", "J"} + + +def construct_map(land: Land) -> Map: + rows = len(land) + cols = len(land[0]) + + m: dict[C, set[C]] = defaultdict(set) + + for r in range(rows): + for c in range(cols): + # N + if r > 0 and land[r][c] in N_CONN and land[r - 1][c] in S_CONN: + m[(r, c)].add((r - 1, c)) + + # E + if ( + c < cols - 1 + and land[r][c] in E_CONN + and land[r][c + 1] in W_CONN + ): + m[(r, c)].add((r, c + 1)) + + # S + if ( + r < rows - 1 + and land[r][c] in S_CONN + and land[r + 1][c] in N_CONN + ): + m[(r, c)].add((r + 1, c)) + + # W + if c > 0 and land[r][c] in W_CONN and land[r][c - 1] in E_CONN: + m[(r, c)].add((r, c - 1)) + + return m + + +def process_data(data: str) -> Land: + return [list(x) for x in data.splitlines()] + + +def get_path(prev: C, curr: C, m: Map, land: Land) -> int: + path = 0 + + while land[curr[0]][curr[1]] != "S": + nxt_set = m[curr] - {prev} + + if len(nxt_set) == 0: + return -1 # dead end + + prev = curr + (curr,) = nxt_set + path += 1 + + return path + + +def find_start(land: Land) -> C: + rows = len(land) + cols = len(land[0]) + + for r in range(rows): + for c in range(cols): + if land[r][c] == "S": + return r, c + + raise ValueError("start position wasn't found") + + +def solve(task: str) -> int: + land = process_data(task) + m = construct_map(land) + start = find_start(land) + return max(get_path(start, x, m, land) for x in m[start]) // 2 + 1 diff --git a/tests/src/year2023/test_day10a.py b/tests/src/year2023/test_day10a.py new file mode 100644 index 0000000..b226945 --- /dev/null +++ b/tests/src/year2023/test_day10a.py @@ -0,0 +1,30 @@ +"""2023 - Day 10 Part 1: Pipe Maze""" +from textwrap import dedent + +from src.year2023.day10a import solve + + +def test_solve_0(): + task = dedent( + """ + ..... + .S-7. + .|.|. + .L-J. + ..... + """ + ).strip() + assert solve(task) == 4 + + +def test_solve_1(): + task = dedent( + """ + ..F7. + .FJ|. + SJ.L7 + |F--J + LJ... + """ + ).strip() + assert solve(task) == 8