aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-x2018/01/puzzle-1.awk4
-rwxr-xr-x2018/01/puzzle-2.awk14
-rwxr-xr-x2018/02/puzzle-1.pl19
-rwxr-xr-x2018/02/puzzle-2.py26
-rwxr-xr-x2024/25/puzzle-1.py30
-rwxr-xr-x2025/01/puzzle-1.awk19
-rwxr-xr-x2025/01/puzzle-2.awk23
-rw-r--r--2025/02/.gitignore1
-rw-r--r--2025/02/Makefile1
-rw-r--r--2025/02/puzzles.sh12
-rw-r--r--2025/03/.gitignore1
-rw-r--r--2025/03/Makefile1
-rw-r--r--2025/03/puzzles.c48
-rw-r--r--2025/04/.gitignore1
-rw-r--r--2025/04/Makefile1
-rw-r--r--2025/04/puzzles.py95
-rwxr-xr-x2025/05/puzzle-1.awk19
-rwxr-xr-x2025/05/puzzle-2.awk31
-rwxr-xr-x2025/06/puzzle-1.awk20
-rwxr-xr-x2025/06/puzzle-2.sh27
-rwxr-xr-x2025/07/puzzle-1.py23
-rwxr-xr-x2025/07/puzzle-2.py31
-rw-r--r--2025/08/.gitignore1
-rw-r--r--2025/08/Makefile1
-rw-r--r--2025/08/puzzles.py53
-rwxr-xr-x2025/09/puzzle-1.py29
-rwxr-xr-x2025/09/puzzle-2.py49
-rwxr-xr-x2025/10/puzzle-1.py49
-rwxr-xr-x2025/11/puzzle-1.awk19
-rwxr-xr-x2025/11/puzzle-2.py37
-rwxr-xr-xfetch-inputs6
31 files changed, 688 insertions, 3 deletions
diff --git a/2018/01/puzzle-1.awk b/2018/01/puzzle-1.awk
new file mode 100755
index 0000000..128d7e2
--- /dev/null
+++ b/2018/01/puzzle-1.awk
@@ -0,0 +1,4 @@
+#!/usr/bin/awk -f
+
+{ x += $1 }
+END { print x }
diff --git a/2018/01/puzzle-2.awk b/2018/01/puzzle-2.awk
new file mode 100755
index 0000000..2236499
--- /dev/null
+++ b/2018/01/puzzle-2.awk
@@ -0,0 +1,14 @@
+#!/usr/bin/awk -f
+
+{ xs[NR] = $1 }
+
+END {
+ for (;;) {
+ for (i = 1; i <= length(xs); i++) {
+ if (ys[x += xs[i]]++) {
+ print x
+ exit
+ }
+ }
+ }
+}
diff --git a/2018/02/puzzle-1.pl b/2018/02/puzzle-1.pl
new file mode 100755
index 0000000..85a533b
--- /dev/null
+++ b/2018/02/puzzle-1.pl
@@ -0,0 +1,19 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+my $file = "input";
+open my $in, "<", $file or die "Failed to open ‘$file’: $!";
+
+my ($x, $y) = (0) x 2;
+while (<$in>) {
+ chomp;
+ my %freq;
+ $freq{$_}++ for split //;
+ $x++ if grep { $_ == 2 } values %freq;
+ $y++ if grep { $_ == 3 } values %freq;
+}
+
+close $in;
+printf "%d\n", $x * $y;
diff --git a/2018/02/puzzle-2.py b/2018/02/puzzle-2.py
new file mode 100755
index 0000000..37ce920
--- /dev/null
+++ b/2018/02/puzzle-2.py
@@ -0,0 +1,26 @@
+#!/usr/bin/python3
+
+import itertools
+
+
+def main() -> None:
+ with open('input', 'r') as f:
+ xs = f.readlines()
+ print(next(
+ x for x in
+ itertools.starmap(common, itertools.combinations(xs, 2))
+ if x is not None
+ ), end='')
+
+
+def common(x: str, y: str) -> str | None:
+ it = (i for i, (a, b) in enumerate(zip(x, y)) if a != b)
+ i = next(it)
+ try:
+ next(it)
+ except StopIteration:
+ return x[:i] + x[i+1:]
+
+
+if __name__ == '__main__':
+ main()
diff --git a/2024/25/puzzle-1.py b/2024/25/puzzle-1.py
new file mode 100755
index 0000000..2ad6581
--- /dev/null
+++ b/2024/25/puzzle-1.py
@@ -0,0 +1,30 @@
+#!/usr/bin/python3
+
+import itertools
+
+
+def main() -> None:
+ keys: list[int] = []
+ locks: list[int] = []
+
+ with open("input", "r") as f:
+ schems = f.read().split("\n\n")
+
+ for schem in schems:
+ n = 0
+ for char in schem:
+ if char == '\n':
+ continue
+ n <<= 1
+ if char == '#':
+ n |= 1
+ (locks if n >= 0x7C0000000 else keys).append(n)
+
+ def nand(x: int, y: int) -> bool:
+ return not x & y
+
+ print(sum(itertools.starmap(nand, itertools.product(keys, locks))))
+
+
+if __name__ == "__main__":
+ main() \ No newline at end of file
diff --git a/2025/01/puzzle-1.awk b/2025/01/puzzle-1.awk
new file mode 100755
index 0000000..4ed1d7f
--- /dev/null
+++ b/2025/01/puzzle-1.awk
@@ -0,0 +1,19 @@
+#!/usr/bin/awk -f
+
+function mod(n, m)
+{
+ return ((n % m) + m) % m
+}
+
+BEGIN { dial = 50 }
+
+/^L/ { sign = -1 }
+/^R/ { sign = +1 }
+
+{
+ n = substr($0, 2)
+ if ((dial = mod(dial + n*sign, 100)) == 0)
+ acc++
+}
+
+END { print acc }
diff --git a/2025/01/puzzle-2.awk b/2025/01/puzzle-2.awk
new file mode 100755
index 0000000..b0516bf
--- /dev/null
+++ b/2025/01/puzzle-2.awk
@@ -0,0 +1,23 @@
+#!/usr/bin/awk -f
+
+function mod(n, m)
+{
+ return ((n % m) + m) % m
+}
+
+BEGIN { dial = 50 }
+
+{ n = substr($0, 2) }
+
+/^L/ {
+ sign = -1
+ acc += int(n / 100) + (n%100 >= dial && dial != 0)
+}
+/^R/ {
+ sign = +1
+ acc += int((dial + n) / 100)
+}
+
+{ dial = mod(dial + n*sign, 100) }
+
+END { print acc }
diff --git a/2025/02/.gitignore b/2025/02/.gitignore
new file mode 100644
index 0000000..62d9f48
--- /dev/null
+++ b/2025/02/.gitignore
@@ -0,0 +1 @@
+puzzle-[12].sh
diff --git a/2025/02/Makefile b/2025/02/Makefile
new file mode 100644
index 0000000..c1620aa
--- /dev/null
+++ b/2025/02/Makefile
@@ -0,0 +1 @@
+include ../../Makefiles/sh.mk
diff --git a/2025/02/puzzles.sh b/2025/02/puzzles.sh
new file mode 100644
index 0000000..0ad12cc
--- /dev/null
+++ b/2025/02/puzzles.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+# START PART 1
+readonly REGEXP='^([0-9]+)\1$'
+# END PART 1 START PART 2
+readonly REGEXP='^([0-9]+)\1+$'
+# END PART 2
+
+tr ',' '\n' <input | while IFS=- read -r x y
+do
+ seq -f %1.0f $x $y
+done | grep -E "$REGEXP" | awk '{ n += $1 } END { print n }'
diff --git a/2025/03/.gitignore b/2025/03/.gitignore
new file mode 100644
index 0000000..60d075d
--- /dev/null
+++ b/2025/03/.gitignore
@@ -0,0 +1 @@
+puzzle-[12]
diff --git a/2025/03/Makefile b/2025/03/Makefile
new file mode 100644
index 0000000..de3f940
--- /dev/null
+++ b/2025/03/Makefile
@@ -0,0 +1 @@
+include ../../Makefiles/c.mk
diff --git a/2025/03/puzzles.c b/2025/03/puzzles.c
new file mode 100644
index 0000000..9bd68ee
--- /dev/null
+++ b/2025/03/puzzles.c
@@ -0,0 +1,48 @@
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef PART1
+ #define DIGITS ((size_t)(2))
+#else
+ #define DIGITS ((size_t)(12))
+#endif
+
+int
+main(void)
+{
+ FILE *fp = fopen("input", "r");
+ if (fp == nullptr)
+ err(EXIT_FAILURE, "failed to open input");
+
+ char *buf = nullptr;
+ size_t acc, bufsz;
+ ssize_t n;
+
+ acc = bufsz = 0;
+
+ while ((n = getline(&buf, &bufsz, fp)) != -1) {
+ if (buf[n - 1] == '\n')
+ buf[--n] = 0;
+
+ char digits[DIGITS + 1];
+ memcpy(digits, buf, DIGITS);
+ digits[DIGITS] = 0;
+
+ for (size_t i = 1; i < n - DIGITS + 1; i++) {
+ for (size_t j = 0; j < DIGITS; j++) {
+ if (buf[i + j] > digits[j])
+ memcpy(digits + j, buf + i + j, DIGITS - j);
+ }
+ }
+
+ acc += (size_t)strtol(digits, nullptr, 10);
+ }
+ if (ferror(fp))
+ err(EXIT_FAILURE, "failed to read record");
+
+ fclose(fp);
+ printf("%zu\n", acc);
+ return EXIT_SUCCESS;
+}
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()
diff --git a/2025/05/puzzle-1.awk b/2025/05/puzzle-1.awk
new file mode 100755
index 0000000..acfe21b
--- /dev/null
+++ b/2025/05/puzzle-1.awk
@@ -0,0 +1,19 @@
+#!/usr/bin/awk -f
+
+BEGIN { FS = "-" }
+
+NF == 2 {
+ xs[NR, 1] = $1
+ xs[NR, 2] = $2
+}
+
+NF == 1 {
+ for (i = 1; i <= length(xs) / 2; i++) {
+ if ($1 >= xs[i, 1] && $1 <= xs[i, 2]) {
+ cnt++
+ break
+ }
+ }
+}
+
+END { print cnt }
diff --git a/2025/05/puzzle-2.awk b/2025/05/puzzle-2.awk
new file mode 100755
index 0000000..3dfc661
--- /dev/null
+++ b/2025/05/puzzle-2.awk
@@ -0,0 +1,31 @@
+#!/usr/bin/gawk -f
+
+function rangecmp(i1, v1, i2, v2)
+{
+ return v1[1] - v2[1]
+}
+
+function max(x, y)
+{
+ return x > y ? x : y
+}
+
+BEGIN { FS = "-" }
+
+NF == 2 {
+ xs[NR][1] = $1
+ xs[NR][2] = $2
+}
+
+END {
+ asort(xs, xs, "rangecmp")
+ for (i in xs) {
+ if (xs[i][2] <= m)
+ continue
+ cnt += xs[i][2] - max(xs[i][1], m)
+ if (xs[i][1] > m)
+ cnt++
+ m = xs[i][2]
+ }
+ print cnt
+}
diff --git a/2025/06/puzzle-1.awk b/2025/06/puzzle-1.awk
new file mode 100755
index 0000000..4bb4338
--- /dev/null
+++ b/2025/06/puzzle-1.awk
@@ -0,0 +1,20 @@
+#!/usr/bin/awk -f
+
+NR == 1 {
+ for (i = 1; i <= NF; i++)
+ xs[i, 2] = 1
+}
+
+!/[+*]/ {
+ for (i = 1; i <= NF; i++) {
+ xs[i, 1] += $i
+ xs[i, 2] *= $i
+ }
+}
+
+/[+*]/ {
+ for (i = 1; i <= NF; i++)
+ total += xs[i, $i == "+" ? 1 : 2]
+}
+
+END { print total }
diff --git a/2025/06/puzzle-2.sh b/2025/06/puzzle-2.sh
new file mode 100755
index 0000000..cba270f
--- /dev/null
+++ b/2025/06/puzzle-2.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+{
+ printf '('
+ for i in $(seq "$(head -n1 input | wc -c)")
+ do
+ num="$(cut -c $i input | paste -sd '')"
+ num="${num%${num##*[![:space:]]}}"
+
+ case "$num" in
+ *[+*])
+ test -n "$op" && printf ') + ('
+ op="${num#${num%?}}"
+ num="${num%?}"
+ printf '%d %s ' $num "$op"
+ ;;
+ '')
+ test "$op" = +
+ printf '%d' $?
+ ;;
+ *)
+ printf '%d %s ' $num "$op"
+ ;;
+ esac
+ done
+ printf ')\n'
+} | bc
diff --git a/2025/07/puzzle-1.py b/2025/07/puzzle-1.py
new file mode 100755
index 0000000..0c30dfa
--- /dev/null
+++ b/2025/07/puzzle-1.py
@@ -0,0 +1,23 @@
+#!/usr/bin/python3
+
+
+def main() -> None:
+ with open('input', 'r') as f:
+ grid = tuple(tuple(x.strip()) for x in f.readlines())
+
+ xs = set()
+ xs.add(grid[0].index('S'))
+ cnt = 0
+
+ for row in grid[1:]:
+ for i, ch in enumerate(row):
+ if ch == '^' and i in xs:
+ cnt += 1
+ xs.remove(i)
+ xs.add(i - 1)
+ xs.add(i + 1)
+
+ print(cnt)
+
+if __name__ == '__main__':
+ main()
diff --git a/2025/07/puzzle-2.py b/2025/07/puzzle-2.py
new file mode 100755
index 0000000..3695889
--- /dev/null
+++ b/2025/07/puzzle-2.py
@@ -0,0 +1,31 @@
+#!/usr/bin/python3
+
+import collections
+import functools
+
+
+Pos = collections.namedtuple('Pos', ['x', 'y'])
+
+
+def main() -> None:
+ with open('input', 'r') as f:
+ grid = tuple(tuple(x.strip()) for x in f.readlines())
+
+ pos = Pos(grid[0].index('S'), 0)
+ print(npaths(grid, pos))
+
+
+@functools.cache
+def npaths(grid: list[list[str]], pos: Pos) -> int:
+ if pos.y == len(grid):
+ return 1
+ if grid[pos.y][pos.x] == '^':
+ return (
+ npaths(grid, Pos(pos.x - 1, pos.y))
+ + npaths(grid, Pos(pos.x + 1, pos.y))
+ )
+ return npaths(grid, Pos(pos.x, pos.y + 1))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/2025/08/.gitignore b/2025/08/.gitignore
new file mode 100644
index 0000000..ffc46fe
--- /dev/null
+++ b/2025/08/.gitignore
@@ -0,0 +1 @@
+puzzle-[12].py
diff --git a/2025/08/Makefile b/2025/08/Makefile
new file mode 100644
index 0000000..0a4e980
--- /dev/null
+++ b/2025/08/Makefile
@@ -0,0 +1 @@
+include ../../Makefiles/py.mk
diff --git a/2025/08/puzzles.py b/2025/08/puzzles.py
new file mode 100644
index 0000000..086baca
--- /dev/null
+++ b/2025/08/puzzles.py
@@ -0,0 +1,53 @@
+#!/usr/bin/python3
+
+import functools
+import heapq
+import itertools
+import math
+import operator
+
+
+type Point = tuple[int, int, int]
+type Circuit = set[Point]
+
+
+def main() -> None:
+ with open('input', 'r') as f:
+ boxes = [tuple(map(int, l.split(','))) for l in f.readlines()]
+
+ circuits: list[Circuit] = []
+ pairs = itertools.combinations(boxes, 2)
+ pairs = iter(sorted(pairs, key=lambda p: math.dist(*p)))
+
+# START PART 1
+ for p, q in itertools.islice(pairs, 1000):
+ connect(circuits, p, q)
+
+ lens = heapq.nlargest(3, map(len, circuits))
+ print(functools.reduce(operator.mul, lens))
+# END PART 1 START PART 2
+ while len(circuits) != 1 or len(circuits[0]) != len(boxes):
+ p, q = next(pairs)
+ connect(circuits, p, q)
+ print(p[0] * q[0])
+# END PART 2
+
+
+def connect(circuits: list[Circuit], p: Point, q: Point) -> None:
+ pc = next((c for c in circuits if p in c), None)
+ qc = next((c for c in circuits if q in c), None)
+
+ match (pc, qc):
+ case (None, None):
+ circuits.append({p, q})
+ case (x, None):
+ pc.add(q)
+ case (None, y):
+ qc.add(p)
+ case (x, y) if x is not y:
+ circuits.remove(qc)
+ pc |= qc
+
+
+if __name__ == '__main__':
+ main()
diff --git a/2025/09/puzzle-1.py b/2025/09/puzzle-1.py
new file mode 100755
index 0000000..6d42141
--- /dev/null
+++ b/2025/09/puzzle-1.py
@@ -0,0 +1,29 @@
+#!/usr/bin/python3
+
+import itertools
+
+
+type Point = tuple[int, int]
+
+
+def main() -> None:
+ with open('input', 'r') as f:
+ xs = [tuple(map(int, l.split(','))) for l in f.readlines()]
+
+ it = itertools.combinations(xs, 2)
+ it = itertools.starmap(area, it)
+ print(max(it))
+
+
+def area(p: Point, q: Point) -> int:
+ a, b = minmax(p[0], q[0])
+ c, d = minmax(p[1], q[1])
+ return (b - a + 1) * (d - c + 1)
+
+
+def minmax(x: int, y: int) -> tuple[int, int]:
+ return (x, y) if x < y else (y, x)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/2025/09/puzzle-2.py b/2025/09/puzzle-2.py
new file mode 100755
index 0000000..f77c4c1
--- /dev/null
+++ b/2025/09/puzzle-2.py
@@ -0,0 +1,49 @@
+#!/usr/bin/python3
+
+import itertools
+
+
+type Point = tuple[int, int]
+type Box = tuple[int, int, int, int]
+
+
+def main() -> None:
+ with open('input', 'r') as f:
+ xs = [tuple(map(int, l.split(','))) for l in f.readlines()]
+
+ it = itertools.pairwise(xs)
+ it = itertools.starmap(tobox, it)
+ ys = list(it)
+
+ n = 0
+ it = itertools.combinations(xs, 2)
+ it = itertools.starmap(tobox, it)
+ for x in it:
+ if (_area := area(x)) <= n:
+ continue
+ a, b, c, d = x
+ for p, q, r, s in ys:
+ if a < r and b < s and c > p and d > q:
+ break
+ else:
+ n = _area
+
+ print(n)
+
+
+def tobox(p: Point, q: Point) -> Box:
+ a, b = minmax(p[0], q[0])
+ c, d = minmax(p[1], q[1])
+ return a, c, b, d
+
+
+def area(b: Box) -> int:
+ return (b[2] - b[0] + 1) * (b[3] - b[1] + 1)
+
+
+def minmax(x: int, y: int) -> tuple[int, int]:
+ return (x, y) if x < y else (y, x)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/2025/10/puzzle-1.py b/2025/10/puzzle-1.py
new file mode 100755
index 0000000..e3bd232
--- /dev/null
+++ b/2025/10/puzzle-1.py
@@ -0,0 +1,49 @@
+#!/usr/bin/python3
+
+import dataclasses
+import functools
+import itertools
+import operator
+from typing import Self
+
+
+@dataclasses.dataclass
+class Machine:
+ target: int
+ buttons: list[int]
+
+ @classmethod
+ def from_record(cls, s: str) -> Self:
+ buttons: list[int] = []
+ for token in s.split(' '):
+ match token[0]:
+ case '[':
+ token = token.translate({
+ ord('.'): '0',
+ ord('#'): '1',
+ })
+ target = int(token[len(token) - 2:0:-1], base=2)
+ case '(':
+ n = 0
+ for x in map(int, token[1:-1].split(',')):
+ n |= 1 << x
+ buttons.append(n)
+ return cls(target, buttons)
+
+
+def main() -> None:
+ with open('input', 'r') as f:
+ xs = [Machine.from_record(x) for x in f.readlines()]
+ print(sum(map(fewest_clicks, xs)))
+
+
+def fewest_clicks(mach: Machine) -> int:
+ for i in itertools.count(start=1):
+ for comb in itertools.combinations(mach.buttons, i):
+ if functools.reduce(operator.xor, comb) == mach.target:
+ return i
+ # NOTREACHED
+
+
+if __name__ == '__main__':
+ main()
diff --git a/2025/11/puzzle-1.awk b/2025/11/puzzle-1.awk
new file mode 100755
index 0000000..aa752e1
--- /dev/null
+++ b/2025/11/puzzle-1.awk
@@ -0,0 +1,19 @@
+#!/usr/bin/gawk -f
+
+function npaths(src, dst, n, i)
+{
+ if (src == dst)
+ return 1;
+ for (i in paths[src])
+ n += npaths(paths[src][i], dst)
+ return n
+}
+
+BEGIN { FS = ":? " }
+
+{
+ for (i = 2; i <= NF; i++)
+ paths[$1][i - 1] = $i
+}
+
+END { print npaths("you", "out") }
diff --git a/2025/11/puzzle-2.py b/2025/11/puzzle-2.py
new file mode 100755
index 0000000..629c10c
--- /dev/null
+++ b/2025/11/puzzle-2.py
@@ -0,0 +1,37 @@
+#!/usr/bin/python3
+
+import functools
+
+
+paths: dict[str, list[str]] = {}
+
+def main() -> None:
+ with open('input', 'r') as f:
+ for line in f.readlines():
+ x, *xs = line.split()
+ paths[x[:-1]] = xs
+
+ print(npaths('svr', 'out'))
+
+
+@functools.cache
+def npaths(
+ src: str,
+ dst: str,
+ dacp: bool = False,
+ fftp: bool = False,
+) -> bool | int:
+ return (
+ dacp and fftp
+ if src == dst else
+ sum(npaths(
+ nsrc,
+ dst,
+ dacp or src == 'dac',
+ fftp or src == 'fft',
+ ) for nsrc in paths[src])
+ )
+
+
+if __name__ == '__main__':
+ main()
diff --git a/fetch-inputs b/fetch-inputs
index 4c3b00e..f88583b 100755
--- a/fetch-inputs
+++ b/fetch-inputs
@@ -3,11 +3,11 @@
cd "$(dirname "$0")"
find . \
- -regextype egrep \
-mindepth 2 \
-maxdepth 2 \
-type d \
- -regex '\./[0-9]{4}/[0-9]{2}' \
+ -regex '\./[0-9]\{4\}/[0-9]\{2\}' \
+ | sort \
| while IFS=/ read _ y d
do
file=$y/$d/input
@@ -16,4 +16,4 @@ do
wget -q --load-cookies=.cookies -O "$file" \
"https://adventofcode.com/$y/day/${d#0}/input"
>&2 printf 'DONE\n' >&2
-done \ No newline at end of file
+done