From 36a4c8a21813c4d2e9c0d54b26ec8ddf6be0ea53 Mon Sep 17 00:00:00 2001 From: Daniel Lin Date: Fri, 22 Dec 2023 23:45:18 -0500 Subject: [PATCH] Day 22: Sand Slabs --- README.md | 2 +- py/aoc2023/day22.py | 88 +++++++++++++++++++++++++++++++++++++++++++++ py/pyproject.toml | 1 + 3 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 py/aoc2023/day22.py diff --git a/README.md b/README.md index 810fd69d..66e277ad 100644 --- a/README.md +++ b/README.md @@ -26,4 +26,4 @@ Development occurs in language-specific directories: |[Day19.hs](hs/src/Day19.hs)|[Day19.kt](kt/aoc2023-lib/src/commonMain/kotlin/com/github/ephemient/aoc2023/Day19.kt)|[day19.py](py/aoc2023/day19.py)|[day19.rs](rs/src/day19.rs)| |[Day20.hs](hs/src/Day20.hs)|[Day20.kt](kt/aoc2023-lib/src/commonMain/kotlin/com/github/ephemient/aoc2023/Day20.kt)|[day20.py](py/aoc2023/day20.py)|[day20.rs](rs/src/day20.rs)| |[Day21.hs](hs/src/Day21.hs)|[Day21.kt](kt/aoc2023-lib/src/commonMain/kotlin/com/github/ephemient/aoc2023/Day21.kt)|[day21.py](py/aoc2023/day21.py)|[day21.rs](rs/src/day21.rs)| -|[Day22.hs](hs/src/Day22.hs)|[Day22.kt](kt/aoc2023-lib/src/commonMain/kotlin/com/github/ephemient/aoc2023/Day22.kt)||| +|[Day22.hs](hs/src/Day22.hs)|[Day22.kt](kt/aoc2023-lib/src/commonMain/kotlin/com/github/ephemient/aoc2023/Day22.kt)|[day22.py](py/aoc2023/day22.py)|| diff --git a/py/aoc2023/day22.py b/py/aoc2023/day22.py new file mode 100644 index 00000000..964527e9 --- /dev/null +++ b/py/aoc2023/day22.py @@ -0,0 +1,88 @@ +""" +Day 22: Sand Slabs +""" + +from collections import defaultdict + +SAMPLE_INPUT = """ +1,0,1~1,2,1 +0,0,2~2,0,2 +0,2,3~2,2,3 +0,0,4~0,2,4 +2,0,5~2,2,5 +0,1,6~2,1,6 +1,1,8~1,1,9 +""" + + +def _parse(data): + bricks = [ + ([int(num) for num in first], [int(num) for num in second]) + for line in data.splitlines() + if len(parts := line.split("~", maxsplit=1)) == 2 + and len(first := parts[0].split(",", maxsplit=2)) == 3 + and len(second := parts[1].split(",", maxsplit=2)) == 3 + and all(num.isdecimal() for num in first + second) + ] + bricks.sort(key=lambda brick: brick[0][2]) + heights = defaultdict(int) + for first, second in bricks: + (x0, y0, z0), (x1, y1, z1) = first, second + dz = ( + max(heights[(x, y)] for x in range(x0, x1 + 1) for y in range(y0, y1 + 1)) + + 1 + - z0 + ) + first[2], second[2] = z0, z1 = z0 + dz, z1 + dz + for x in range(x0, x1 + 1): + for y in range(y0, y1 + 1): + heights[(x, y)] = z1 + bricks.sort(key=lambda brick: brick[0][2]) + rdeps, deps = defaultdict(set), defaultdict(set) + for i, ((x0, y0, _), (x1, y1, z1)) in enumerate(bricks): + for j, ((x2, y2, z2), (x3, y3, _)) in enumerate(bricks[i + 1 :]): + if z2 <= z1: + continue + if z2 > z1 + 1: + break + if x0 <= x3 and x2 <= x1 and y0 <= y3 and y2 <= y1: + rdeps[i].add(i + j + 1) + deps[i + j + 1].add(i) + j += 1 + return len(bricks), rdeps, deps + + +def part1(data): + """ + >>> part1(SAMPLE_INPUT) + 5 + """ + n, _, deps = _parse(data) + return n - len({next(iter(below)) for below in deps.values() if len(below) == 1}) + + +def part2(data): + """ + >>> part2(SAMPLE_INPUT) + 7 + """ + _, rdeps, deps = _parse(data) + acc = 0 + for item in rdeps.items(): + _deps = {above: set(belows) for above, belows in deps.items()} + stack = [item] + while stack: + below, aboves = stack.pop() + for above in aboves: + belows = _deps[above] + if below not in belows: + continue + belows.remove(below) + if belows: + continue + acc += 1 + stack.append((above, rdeps.get(above, ()))) + return acc + + +parts = (part1, part2) diff --git a/py/pyproject.toml b/py/pyproject.toml index 5a68ffee..e0c6321d 100644 --- a/py/pyproject.toml +++ b/py/pyproject.toml @@ -43,6 +43,7 @@ day18 = "aoc2023.day18:parts" day19 = "aoc2023.day19:parts" day20 = "aoc2023.day20:parts" day21 = "aoc2023.day21:parts" +day22 = "aoc2023.day22:parts" [tool.black] target_version = ["py312"]