aboutsummaryrefslogtreecommitdiff
path: root/2025
diff options
context:
space:
mode:
authorThomas Voss <mail@thomasvoss.com> 2025-12-07 06:57:27 +0100
committerThomas Voss <mail@thomasvoss.com> 2025-12-07 06:57:27 +0100
commit703c33211b15b714f00aaeefc880f3c5d594493b (patch)
treec535c4714cad362d58344fef7050df7e7712d869 /2025
parentdbaa30aa4413db70fd975054c676eb6084512293 (diff)
Add 2025 day 4 solutions
Diffstat (limited to '2025')
-rw-r--r--2025/04/.gitignore1
-rw-r--r--2025/04/Makefile1
-rw-r--r--2025/04/puzzles.py95
3 files changed, 97 insertions, 0 deletions
diff --git a/2025/04/.gitignore b/2025/04/.gitignore
new file mode 100644
index 0000000..ffc46fe
--- /dev/null
+++ b/2025/04/.gitignore
@@ -0,0 +1 @@
+puzzle-[12].py
diff --git a/2025/04/Makefile b/2025/04/Makefile
new file mode 100644
index 0000000..0a4e980
--- /dev/null
+++ b/2025/04/Makefile
@@ -0,0 +1 @@
+include ../../Makefiles/py.mk
diff --git a/2025/04/puzzles.py b/2025/04/puzzles.py
new file mode 100644
index 0000000..3558269
--- /dev/null
+++ b/2025/04/puzzles.py
@@ -0,0 +1,95 @@
+#!/usr/bin/python3
+
+import itertools
+from typing import Generator, Iterator, Self, TextIO
+
+
+class Grid:
+ @classmethod
+ def from_file(cls, f: TextIO) -> Self:
+ g = Grid()
+ g._grid = mkmap(
+ [[c for c in l if c != '\n'] for l in f.readlines()])
+ return g
+
+ def in_bounds_p(self, z: complex) -> bool:
+ return (
+ 0 <= z.real < len(self._grid[0])
+ and 0 <= z.imag < len(self._grid)
+ )
+
+ def accessablep(self, z: complex) -> bool:
+ return self[z] is not None and self[z] < 4
+
+ def indicies(self) -> Iterator[complex]:
+ return itertools.starmap(complex, itertools.product(
+ range(len(self._grid[0])),
+ range(len(self._grid)),
+ ))
+
+ def neighbors(self, z: complex) -> Generator[complex, None, None]:
+ for Δ in (
+ -1-1j, -1, -1+1j,
+ 0-1j, 0+1j,
+ +1-1j, +1, +1+1j,
+ ):
+ if self.in_bounds_p(z + Δ):
+ yield z + Δ
+
+ def __getitem__(self, z: complex) -> int | None:
+ return self._grid[int(z.real)][int(z.imag)]
+
+ def __setitem__(self, z: complex, n: int | None) -> None:
+ self._grid[int(z.real)][int(z.imag)] = n
+
+
+def mkmap(xs: list[list[str]]) -> list[list[int]]:
+ def get(pos: complex) -> str:
+ r, i = map(int, (pos.real, pos.imag))
+ if 0 <= r < len(xs[0]) and 0 <= i < len(xs):
+ return xs[r][i]
+ return '.'
+
+ return [
+ [
+ sum(get(complex(i, j) + Δ) == '@' for Δ in [
+ -1-1j, -1, -1+1j,
+ 0-1j, 0+1j,
+ +1-1j, +1, +1+1j,
+ ])
+ if get(complex(i, j)) == '@'
+ else None
+ for j in range(len(xs[i]))
+ ]
+ for i in range(len(xs))
+ ]
+
+
+def main() -> None:
+ with open('input', 'r') as f:
+ grid = Grid.from_file(f)
+
+ if PUZZLE_PART == 1:
+ acc = sum(grid.accessablep(z) for z in grid.indicies())
+ else:
+ acc = 0
+ while True:
+ breakp = True
+ for z in grid.indicies():
+ if not grid.accessablep(z):
+ continue
+ breakp = False
+ acc += 1
+ grid[z] = None
+ for p in grid.neighbors(z):
+ if grid[p] is not None:
+ grid[p] -= 1
+
+ if breakp:
+ break
+
+ print(acc)
+
+
+if __name__ == '__main__':
+ main()