From 7b5c9c393bffb98d662767d2a4199d836ad8dc4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20Engl=C3=B6f=20Ytterstr=C3=B6m?= Date: Sun, 23 Nov 2025 18:56:16 +0100 Subject: [PATCH 01/13] Inititate AOC 2022 in python --- 2022-python/README.md | 48 ++++++++ 2022-python/aoc.py | 118 ++++++++++++++++++++ 2022-python/output/__init__.py | 195 +++++++++++++++++++++++++++++++++ 3 files changed, 361 insertions(+) create mode 100644 2022-python/README.md create mode 100644 2022-python/aoc.py create mode 100644 2022-python/output/__init__.py diff --git a/2022-python/README.md b/2022-python/README.md new file mode 100644 index 0000000..2a5b22b --- /dev/null +++ b/2022-python/README.md @@ -0,0 +1,48 @@ +# Advent of Code 2022 + +Solutions for #aoc2022 in Python 3 (3.13.4). + +Programming setup: + +- Lenovo Thinkpad T14 +- OpenSUSE Tumbleweed with labwc +- Helix editor +- Vivaldi +- Foot + +## Help scripts + +Display all solved puzzles: + + python aoc.py + +To bootstrap a new puzzle (creates `input/.txt` and `output/day_.py`): + + python aoc.py new + +Manually copy the puzzle input from https://adventofcode.com and paste it in `input/.txt` +to start coding. + + wl-paste > input/.txt + +Solve separate puzzle (replace `XX` with the puzzle number): + + python -m output.day_XX + +Solve separate puzzle using stdin (replace `XX` with the puzzle number): + + wl-paste | python -m output.day_XX + cat tmpfile | python -m output.day_XX + +Execute separate puzzle on file save (replace `XX` with the puzzle number): + + ls output/*.py | entr -c -s 'wlpaste | python -m output.day_XX' + ls output/*.py | entr -c -s 'cat tmpfile | python -m output.day_XX' + ls output/*.py | entr -c -r python -m output.day_XX + +(requires `entr` and `wl-paste`, Mac users can instead use `pbpaste`. If you +prefer X at Linux, use `xclip -selection clipboard -o`). + +To lint files: + + ls output/*.py | entr -r -c flake8 output --ignore=E741,E501,E203 diff --git a/2022-python/aoc.py b/2022-python/aoc.py new file mode 100644 index 0000000..b606de2 --- /dev/null +++ b/2022-python/aoc.py @@ -0,0 +1,118 @@ +import sys +from pathlib import Path + + +def headline(n): + """Print day number and name, followed by a ruler. Used by the answer decorator""" + print(f"\nDay {int(n)} - https://adventofcode.com/{year}/day/{int(n)}\n") + + +year = 2022 +nostrip = [5] + +try: + _, day_no, *name = sys.argv +except ValueError: + day_no = None + name = None + +Path("./input").mkdir(parents=True, exist_ok=True) +Path("./output").mkdir(parents=True, exist_ok=True) + +if day_no and name: + name = " ".join(name) + padded_no = day_no.zfill(2) + with open("output/day_{}.py".format(padded_no), "w") as s: + s.write( + f""" +import re +from collections import deque, Counter, defaultdict +from heapq import heappop, heappush +from itertools import compress, combinations, chain, permutations + +from output import matrix, D, DD, ADJ, ints, mhd, mdbg, vdbg, cw, ccw, bk + + +def solve(data): + p1 = None + p2 = None + return p1, p2 + + +if __name__ == "__main__": + import os + + # use dummy data + inp = \"\"\" + replace me + \"\"\".strip() + + # uncomment to instead use stdin + # import sys; inp = sys.stdin.read().strip() + + # uncomment to use AoC provided puzzle input + # with open("./input/{padded_no}.txt", "r") as f: + # inp = f.read().strip() + + # uncomment to do initial data processing shared by part 1-2 + p1, p2 = solve(inp) + + print(p1) + os.system(f"echo {{p1}} | wl-copy") + # print(p2) + # os.system(f"echo {{p2}} | wl-copy") + + # uncomment and replace 0 with actual output to refactor code + # and ensure nonbreaking changes + # assert p1 == 0 + # assert p2 == 0 +""".strip() + + "\n" + ) + exit(0) + +print( + f"\n\033[95m\033[1mAdvent of Code {year}\033[0m" + "\n###################" + "\n\n\033[96mby Anders Englöf Ytterström\033[0m" +) + + +stars = 0 +for i in [str(n).zfill(2) for n in range(1, 26)]: + if not day_no or day_no.zfill(2) == i: + try: + day = __import__( + "output.day_{}".format(i), + globals(), + locals(), + ["solve"], + 0, + ) + with open(f"./input/{i}.txt", "r") as f: + data = f.read() + if int(i) not in nostrip: + data = data.strip() + headline(i) + try: + data = day.presolve(data) + except AttributeError: + pass + try: + p1, p2 = day.solve(data) + except AttributeError: + pass + if p1: + print(f" \033[92m1)\033[0m {p1}") + stars += 1 + if p2: + print(f" \033[92m2)\033[0m {p2}") + stars += 1 + except IOError: + pass + except ImportError: + pass +if not day_no: + print(f"\nStars: {stars}") + print("".join("*" if n < stars else "•" for n in range(50))) +print("") diff --git a/2022-python/output/__init__.py b/2022-python/output/__init__.py new file mode 100644 index 0000000..ba3186c --- /dev/null +++ b/2022-python/output/__init__.py @@ -0,0 +1,195 @@ +import re + +# Directions/Adjacents for 2D matrices, in the order UP, RIGHT, DOWN, LEFT +D = [ + (-1, 0), + (0, 1), + (1, 0), + (0, -1), +] + +Di = [ + (-1, -1), + (-1, 1), + (1, -1), + (1, 1), +] + +# Directions for 2D matrices, as a dict with keys U, R, D, L +DD = { + "U": (-1, 0), + "R": (0, 1), + "D": (1, 0), + "L": (0, -1), +} + +DDa = { + "^": (-1, 0), + ">": (0, 1), + "v": (1, 0), + "<": (0, -1), +} + +# Adjacent relative positions including diagonals for 2D matrices, in the order NW, N, NE, W, E, SW, S, SE +ADJ = [ + (-1, -1), + (-1, 0), + (1, -1), + (0, -1), + (0, 1), + (1, 1), + (1, 0), + (1, -1), +] + + +def ints(s): + """Extract all integers from a string""" + return [int(n) for n in re.findall(r"\d+", s)] + + +def sints(s): + """Extract all signed integers from a string""" + return [int(n) for n in re.findall(r"-?\d+", s)] + + +def mhd(a, b): + """Calculates the Manhattan distance between 2 positions in the format (y, x) or (x, y)""" + ar, ac = a + br, bc = b + return abs(ar - br) + abs(ac - bc) + + +def matrix(d): + """Transform a string into an iterable matrix. Returns the matrix, row count and col count""" + m = [tuple(r) for r in d.split()] + return m, len(m), len(m[0]) + + +def mdbg(m): + """Print-debug a matrix""" + for r in m: + print("".join(r)) + + +def vdbg(seen, h, w): + """Print-debug visited positions of a matrix""" + for r in range(h): + print("".join(["#" if (r, c) in seen else " " for c in range(w)])) + + +def cw(y, x): + """Flip a (y, x) direction counterwise: U->R, R->D, D->L, L->U. + + >>> cw(-1, 0) + (0, 1) + >>> cw(0, 1) + (1, 0) + >>> cw(1, 0) + (0, -1) + >>> cw(0, -1) + (-1, 0) + """ + return (x, y) if y == 0 else (x, -y) + + +def ccw(y, x): + """Flip a (y, x) direction counterwise: U->L, L->D, D->R, R->U. + + >>> ccw(-1, 0) + (0, -1) + >>> ccw(0, -1) + (1, 0) + >>> ccw(1, 0) + (0, 1) + >>> ccw(0, 1) + (-1, 0) + """ + return (x, y) if x == 0 else (-x, y) + + +def bfs(S, E=None): + """BFS algorithm, equal weighted nodes""" + seen = set() + q = [(S, 0)] + g = {} # graph, required to be provided at some point + while q: + m, w = q.pop(0) + if m in seen: + continue + seen.add(m) + # investigate here + for s in g[m]: + q.append((s, w + 1)) + # return insights + + +def mhd_search(r, c, R=20): + """returns all coords that are within R manhattan distance from (r,c)""" + p = set() + for d in range(1, R + 1): + p.add((r, c + d)) + p.add((r, c - d)) + p.add((r + d, c)) + p.add((r - d, c)) + for dd in range(d): + p.add((r - dd, c - d + dd)) + p.add((r + dd, c - d + dd)) + p.add((r - dd, c - dd + d)) + p.add((r + dd, c - dd + d)) + return p + + +def dijkstras(grid, start, target): + """ + 1. Create an array that holds the distance of each vertex from the starting + vertex. Initially, set this distance to infinity for all vertices except + the starting vertex which should be set to 0. + 2. Create a priority queue (heap) and insert the starting vertex with its + distance of 0. + 3. While there are still vertices left in the priority queue, select the vertex + with the smallest recorded distance from the starting vertex and visit its + neighboring vertices. + 4. For each neighboring vertex, check if it is visited already or not. If it + isn’t visited yet, calculate its tentative distance by adding its weight + to the smallest distance found so far for its parent/previous node + (starting vertex in case of first-level vertices). + 5. If this tentative distance is smaller than previously recorded value + (if any), update it in our ‘distances’ array. + 6. Finally, add this visited vertex with its updated distance to our priority + queue and repeat step-3 until we have reached our destination or exhausted + all nodes. + """ + import heapq + + target = max(grid) + seen = set() + queue = [(start, 0)] + while queue: + cost, pos, direction, steps = heapq.heappop(queue) + y, x = pos + dy, dx = direction + + if pos == target: + return cost + + if ((pos, "and stuff")) in seen: + continue + + seen.add((pos, "and stuff")) + + neighbors = [] + for n in neighbors: + heapq.heappush(queue, ("stuffs")) + + return -1 + + +def bk(graph, p, r=set(), x=set()): + """Bron-Kerbosch algoritm, no pivot: https://en.wikipedia.org/wiki/Bron%E2%80%93Kerbosch_algorithm""" + if not p and not x: + yield r + while p: + v = p.pop() + yield from bk(graph, p & set(graph[v]), r | {v}, x & graph[v]) + x.add(v) -- 2.45.3 From e791b19ab20f9d0263939e518a3ec8a4c2c4bc5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20Engl=C3=B6f=20Ytterstr=C3=B6m?= Date: Sat, 15 Nov 2025 15:05:09 +0100 Subject: [PATCH 02/13] Solve 2022 day 1 pt 1-2 --- 2022-python/output/day_01.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 2022-python/output/day_01.py diff --git a/2022-python/output/day_01.py b/2022-python/output/day_01.py new file mode 100644 index 0000000..f637ccf --- /dev/null +++ b/2022-python/output/day_01.py @@ -0,0 +1,26 @@ +from output import ints + + +def solve(data): + E = data.split("\n\n") + p1 = 0 + C = [] + for e in E: + e = sum(ints(e)) + p1 = max(p1, e) + C.append(e) + p2 = sum(sorted(C, reverse=True)[:3]) + return p1, p2 + + +if __name__ == "__main__": + with open("./input/01.txt", "r") as f: + inp = f.read().strip() + + p1, p2 = solve(inp) + + print(p1) + print(p2) + + assert p1 == 66616 + assert p2 == 199172 -- 2.45.3 From 597940f1b42adcd179b5533c070256296506296d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20Engl=C3=B6f=20Ytterstr=C3=B6m?= Date: Sun, 23 Nov 2025 15:11:23 +0100 Subject: [PATCH 03/13] Solve 2022 day 2 pt 1-2 --- 2022-python/output/day_02.py | 40 ++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 2022-python/output/day_02.py diff --git a/2022-python/output/day_02.py b/2022-python/output/day_02.py new file mode 100644 index 0000000..b58799e --- /dev/null +++ b/2022-python/output/day_02.py @@ -0,0 +1,40 @@ +def solve(data): + S1 = { + "A X": 1 + 3, + "A Y": 2 + 6, + "A Z": 3 + 0, + "B X": 1 + 0, + "B Y": 2 + 3, + "B Z": 3 + 6, + "C X": 1 + 6, + "C Y": 2 + 0, + "C Z": 3 + 3, + } + S2 = { + "A X": 3 + 0, + "A Y": 1 + 3, + "A Z": 2 + 6, + "B X": 1 + 0, + "B Y": 2 + 3, + "B Z": 3 + 6, + "C X": 2 + 0, + "C Y": 3 + 3, + "C Z": 1 + 6, + } + R = data.splitlines() + p1 = sum(S1[r] for r in R) + p2 = sum(S2[r] for r in R) + return p1, p2 + + +if __name__ == "__main__": + with open("./input/02.txt", "r") as f: + inp = f.read().strip() + + p1, p2 = solve(inp) + + print(p1) + print(p2) + + assert p1 == 12772 + assert p2 == 11618 -- 2.45.3 From 19790f089992d01b15b76a3c2b0f0a6b191033ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20Engl=C3=B6f=20Ytterstr=C3=B6m?= Date: Sun, 23 Nov 2025 15:51:48 +0100 Subject: [PATCH 04/13] Solve 2022 day 3 pt 1-2 --- 2022-python/output/day_03.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 2022-python/output/day_03.py diff --git a/2022-python/output/day_03.py b/2022-python/output/day_03.py new file mode 100644 index 0000000..05a3011 --- /dev/null +++ b/2022-python/output/day_03.py @@ -0,0 +1,28 @@ +def solve(data): + rs = data.split() + p1 = sum(map(_p1, rs)) + p2 = sum(map(_p2, zip(rs[::3], rs[1::3], rs[2::3]))) + return p1, p2 + + +def _p1(rs): + c = (set(rs[: len(rs) // 2]) & set(rs[len(rs) // 2 :])).pop() + return ord(c) - 96 if c.islower() else ord(c) - 38 + + +def _p2(rsg): + c = (set(rsg[0]) & set(rsg[1]) & set(rsg[2])).pop() + return ord(c) - 96 if c.islower() else ord(c) - 38 + + +if __name__ == "__main__": + with open("./input/03.txt", "r") as f: + inp = f.read().strip() + + p1, p2 = solve(inp) + + print(p1) + print(p2) + + assert p1 == 7817 + assert p2 == 2444 -- 2.45.3 From 7c25aeafd77d18a31c7577151ce24f65beb6eba7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20Engl=C3=B6f=20Ytterstr=C3=B6m?= Date: Sun, 23 Nov 2025 16:13:50 +0100 Subject: [PATCH 05/13] Solve 2022 day 4 pt 1-2 --- 2022-python/output/day_04.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 2022-python/output/day_04.py diff --git a/2022-python/output/day_04.py b/2022-python/output/day_04.py new file mode 100644 index 0000000..7420aa9 --- /dev/null +++ b/2022-python/output/day_04.py @@ -0,0 +1,31 @@ +from output import ints + + +def solve(data): + p1 = sum(_p1(line) for line in data.split()) + p2 = sum(_p2(line) for line in data.split()) + return p1, p2 + + +def _p1(data): + a1, a2, b1, b2 = ints(data) + return (a1 >= b1 and a2 <= b2) or (b1 >= a1 and b2 <= a2) + + +def _p2(data): + a1, a2, b1, b2 = ints(data) + + return len(set(range(a1, a2 + 1)) & set(range(b1, b2 + 1))) > 0 + + +if __name__ == "__main__": + with open("./input/04.txt", "r") as f: + inp = f.read().strip() + + p1, p2 = solve(inp) + + print(p1) + print(p2) + + assert p1 == 507 + assert p2 == 897 -- 2.45.3 From e800a005e81b9f7e1ec5f532207ef505491b0714 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20Engl=C3=B6f=20Ytterstr=C3=B6m?= Date: Sun, 23 Nov 2025 18:39:14 +0100 Subject: [PATCH 06/13] Solve 2022 day 5 pt 1-2 Important not to strip trailing whitespace from this one :) --- 2022-python/output/day_05.py | 45 ++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 2022-python/output/day_05.py diff --git a/2022-python/output/day_05.py b/2022-python/output/day_05.py new file mode 100644 index 0000000..cb33dd6 --- /dev/null +++ b/2022-python/output/day_05.py @@ -0,0 +1,45 @@ +from copy import deepcopy +from collections import defaultdict + +from output import ints + + +def solve(data): + state, program = data.split("\n\n") + labels, *state = state.splitlines()[::-1] + num = max(ints(labels)) + stacks = defaultdict(list) + for r, text in enumerate(state): + for c in range(4 * num): + if c % 4 == 1: + if text[c] != " ": + stacks[c // 4 + 1].append(text[c]) + p1 = _act(program, deepcopy(stacks)) + p2 = _act(program, deepcopy(stacks), r=9001) + return p1, p2 + + +def _act(program, stacks, r=9000): + for line in program.splitlines(): + count, old, new = ints(line) + match r: + case 9000: + for _ in range(count): + stacks[new].append(stacks[old].pop()) + case 9001: + stacks[new] += stacks[old][-count:] + stacks[old] = stacks[old][:-count] + return "".join(s[-1] for s in stacks.values()) + + +if __name__ == "__main__": + with open("./input/05.txt", "r") as f: + inp = f.read() + + p1, p2 = solve(inp) + + print(p1) + print(p2) + + assert p1 == "ZRLJGSCTR" + assert p2 == "PRTTGRFPB" -- 2.45.3 From a708d791c224d6d2578f2b86a2b711a58b715b58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20Engl=C3=B6f=20Ytterstr=C3=B6m?= Date: Sun, 23 Nov 2025 19:17:22 +0100 Subject: [PATCH 07/13] Solve 2022 day 6 pt 1-2 --- 2022-python/output/day_06.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 2022-python/output/day_06.py diff --git a/2022-python/output/day_06.py b/2022-python/output/day_06.py new file mode 100644 index 0000000..715ec09 --- /dev/null +++ b/2022-python/output/day_06.py @@ -0,0 +1,21 @@ +def solve(data): + for p1 in range(4, len(data)): + if len(set(data[p1 - 4 : p1])) == 4: + break + for p2 in range(14, len(data)): + if len(set(data[p2 - 14 : p2])) == 14: + break + return p1, p2 + + +if __name__ == "__main__": + with open("./input/06.txt", "r") as f: + inp = f.read().strip() + + p1, p2 = solve(inp) + + print(p1) + print(p2) + + assert p1 == 1238 + assert p2 == 3037 -- 2.45.3 From 90d5ab203d85beb0c4eb8979bef880351e6c8e3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20Engl=C3=B6f=20Ytterstr=C3=B6m?= Date: Sun, 23 Nov 2025 23:39:37 +0100 Subject: [PATCH 08/13] Solve 2022 day 7 pt 1-2 Funny that I did not mention the problem with recurring directory names in the first run of this puzzle in Elixir, back in 2022 and bb708a5e58. Most likely, the insight flew over my head that time. This time, I realized I could just add the size to the size of each parent. I struggled to find a graph traversal for it, until I realized I did not need the graph. The key rewrite was this: for p in path: fs[p] += size fs["/"] += size ... to this: p = "/" fs[p] += size for dir_name in path: p += f"/{dir_name}" fs[p] += size This solved my issues for 3 hours by acknowledge that - a directory name "a" can accour on multiple places, example: /a, /b/a, /c/a - a directory name "a" can have a parent directory named "a", example: /a/b/a, /a/c/d/a And as always, example input is the devil. Look at the reak input as soon as possible. --- 2022-python/output/day_07.py | 54 ++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 2022-python/output/day_07.py diff --git a/2022-python/output/day_07.py b/2022-python/output/day_07.py new file mode 100644 index 0000000..60f30c8 --- /dev/null +++ b/2022-python/output/day_07.py @@ -0,0 +1,54 @@ +from collections import defaultdict + +from output import ints + + +def solve(data): + fs = defaultdict(int) + pwd = [] + for cmd in data.splitlines(): + if cmd.startswith("$ ls") or cmd.startswith("dir "): + continue + if cmd.startswith("$ cd"): + d = cmd.split()[-1] + match d: + case "/": + pwd = [] + case "..": + pwd.pop() + case _: + pwd.append(d) + continue + a = "" + w = sum(ints(cmd)) + # in example input, all directories have distinct names. + # + # the puzzle input have repeated directory names, e.g. both + # /d/a and /c/b/a (a subfolder named "a") exists. + # + # Furthermore, puzzle input also has cases of /a/b/a, + # e.g. a recurring across directory path. + # + # Lost several hours due to this. + fs[a] += w + for p in pwd: + a += f"/{p}" + fs[a] += w + p1 = sum(i for i in fs.values() if i <= 100_000) + free = 70_000_000 - fs[""] + needed = 30_000_000 - free + p2 = min(i for i in fs.values() if i >= needed) + return p1, p2 + + +if __name__ == "__main__": + with open("./input/07.txt", "r") as f: + inp = f.read().strip() + + p1, p2 = solve(inp) + + print(p1) + print(p2) + + assert p1 == 1644735 + assert p2 == 1300850 -- 2.45.3 From 179203f7be9f8dbf1412d7e59b82912394696c08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20Engl=C3=B6f=20Ytterstr=C3=B6m?= Date: Wed, 26 Nov 2025 00:38:00 +0100 Subject: [PATCH 09/13] Solve 2022 day 8 pt 1-2 --- 2022-python/output/day_08.py | 60 ++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 2022-python/output/day_08.py diff --git a/2022-python/output/day_08.py b/2022-python/output/day_08.py new file mode 100644 index 0000000..d8906f8 --- /dev/null +++ b/2022-python/output/day_08.py @@ -0,0 +1,60 @@ +from math import prod + +from output import matrix + + +def solve(data): + M, R, C = matrix(data) + p1 = 2 * R + 2 * C - 4 + p2 = 0 + for row in range(1, R - 1): + for col in range(1, C - 1): + h = int(M[row][col]) + visible = 0 + score = [] + + for i, dy in enumerate(range(row - 1, -1, -1), start=1): + if h <= int(M[dy][col]): + break + else: + visible += 1 + score.append(i) + + for i, dx in enumerate(range(col + 1, C), start=1): + if h <= int(M[row][dx]): + break + else: + visible += 1 + score.append(i) + + for i, dy in enumerate(range(row + 1, R), start=1): + if h <= int(M[dy][col]): + break + else: + visible += 1 + score.append(i) + + for i, dx in enumerate(range(col - 1, -1, -1), start=1): + if h <= int(M[row][dx]): + break + else: + visible += 1 + score.append(i) + + if visible: + p1 += 1 + p2 = max(prod(score), p2) + return p1, p2 + + +if __name__ == "__main__": + with open("./input/08.txt", "r") as f: + inp = f.read().strip() + + p1, p2 = solve(inp) + + print(p1) + print(p2) + + assert p1 == 1812 + assert p2 == 315495 -- 2.45.3 From 657fe956f9079ff99bfc7c1777d7b8e76cc5bd3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20Engl=C3=B6f=20Ytterstr=C3=B6m?= Date: Thu, 27 Nov 2025 22:36:09 +0100 Subject: [PATCH 10/13] Solve 2022 day 9 pt 1-2 Pt 1 was just head and tail. For pt 2 I rewrote the code to instead build the rope (snake) from head to tail by always moving the head first and traverse the body of the snake. Yes, I think of it as a body of a snake - not knots on a rope. Child hood memories are to damn colorful :) For reference, this is my visually debugging code: def dbg(seen, snake): print(snake) for r in range(-4, 1): print("".join([v(r, c, snake) for c in range(0, 7)])) print("") def v(r, c, snake): if (r,c) == snake[0]: return "H" for i, n in enumerate(snake[1:-1], start=1): if (r,c) == n: return str(i) if (r,c) == snake[-1]: return "T" if (r,c) == (0,0): return "s" return "." --- 2022-python/output/day_09.py | 71 ++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 2022-python/output/day_09.py diff --git a/2022-python/output/day_09.py b/2022-python/output/day_09.py new file mode 100644 index 0000000..2c1e2b7 --- /dev/null +++ b/2022-python/output/day_09.py @@ -0,0 +1,71 @@ +from output import DD, ints + + +def solve(data): + p12 = [] + for sl in [2, 10]: + seen = set([(0, 0)]) + snake = [(0, 0)] * sl + for line in data.splitlines(): + d = line[0] + for i in range(ints(line)[0]): + ns = [] + hd, *tls = snake + r, c = hd + hd = r + DD[d][0], c + DD[d][1] + ns.append(hd) + for tl in tls: + r, c = tl + match (r - hd[0], c - hd[1]): + case (2, -1): + tl = r - 1, c + 1 + case (-2, -1): + tl = r + 1, c + 1 + case (-2, 1): + tl = r + 1, c - 1 + case (2, 1): + tl = r - 1, c - 1 + case (1, 2): + tl = r - 1, c - 1 + case (-1, 2): + tl = r + 1, c - 1 + case (-1, -2): + tl = r + 1, c + 1 + case (1, -2): + tl = r - 1, c + 1 + case (0, 2): + tl = r, c - 1 + case (0, -2): + tl = r, c + 1 + case (2, 0): + tl = r - 1, c + case (-2, 0): + tl = r + 1, c + case (2, -2): + tl = r - 1, c + 1 + case (-2, -2): + tl = r + 1, c + 1 + case (-2, 2): + tl = r + 1, c - 1 + case (2, 2): + tl = r - 1, c - 1 + ns.append(tl) + hd = tl + snake = ns + seen.add(tl) + p12.append(len(seen)) + p1, p2 = p12 + return p1, p2 + + +if __name__ == "__main__": + with open("./input/09.txt", "r") as f: + inp = f.read().strip() + + p1, p2 = solve(inp) + + print(p1) + print(p2) + + assert p1 == 6212 + assert p2 == 2522 -- 2.45.3 From 0f709f9f03cad90c0a2328ecb10eaa0a42dea068 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20Engl=C3=B6f=20Ytterstr=C3=B6m?= Date: Fri, 28 Nov 2025 00:24:56 +0100 Subject: [PATCH 11/13] Solve 2022 dat 10 pt 1-2 Not pretty, but it works :) --- 2022-python/output/day_10.py | 49 ++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 2022-python/output/day_10.py diff --git a/2022-python/output/day_10.py b/2022-python/output/day_10.py new file mode 100644 index 0000000..5ab6fce --- /dev/null +++ b/2022-python/output/day_10.py @@ -0,0 +1,49 @@ +from output import sints + + +def solve(data): + x = 1 + P = data.splitlines() + p1 = 0 + c = 0 + p = 0 + running = True + busy = False + pixels = [] + while running: + if not p < len(P) and not busy: + running = False + continue + line = P[min([p, len(P) - 1])] + if (c + 21) % 40 == 0: + p1 += (c + 1) * x + if c % 40 == 0: + pixels.append("\n") + pixels.append("■" if (x - 1) <= (c % 40) <= (x + 1) else " ") + if busy: + busy = False + x += int(sints(line)[0]) + p += 1 + c += 1 + continue + if line.startswith("noop"): + p += 1 + c += 1 + continue + busy = True + c += 1 + p2 = "".join(pixels) + return p1, p2 + + +if __name__ == "__main__": + with open("./input/10.txt", "r") as f: + inp = f.read().strip() + + p1, p2 = solve(inp) + + print(p1) + print(p2) + + assert p1 == 11820 + # assert p2 == "EPJBRKAH" -- 2.45.3 From 81c6bc4cd85f6fbe06bed6f7309a960e0d0b650c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20Engl=C3=B6f=20Ytterstr=C3=B6m?= Date: Sat, 29 Nov 2025 12:38:32 +0100 Subject: [PATCH 12/13] Solve 2022 Day 22 pt 1-2 This is how far I got the Dec 2022 run in Elixir. Had no idea what so ever for pt 2, so I had to solve it by the solutions thread in the subreddit. At first, I wondered if circular values would be the case. It was not. Apparently, modular arithmetics are what I lack knowledge about. Not entirely sure what to call the operator, it is either a greatest common divisor or a least common nominator. What it actually does is to reduce the numbers by modulo for pt 2, which will keep them low and faster to compute. My math is rusty. --- 2022-python/output/day_11.py | 74 ++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 2022-python/output/day_11.py diff --git a/2022-python/output/day_11.py b/2022-python/output/day_11.py new file mode 100644 index 0000000..197a2d5 --- /dev/null +++ b/2022-python/output/day_11.py @@ -0,0 +1,74 @@ +from copy import deepcopy +from collections import defaultdict +from output import ints + + +def solve(data): + M = [] + for md in data.split("\n\n"): + m = {} + starting_items, operation, divisor, if_true, if_false = md.splitlines()[1:] + m["items"] = ints(starting_items) + m["if_false"] = ints(if_false)[0] + m["if_true"] = ints(if_true)[0] + m["divisor"] = ints(divisor)[0] + m["operation_values"] = operation.split()[-3:] + M.append(m) + p12 = [] + # Part 2 resolver. + # + # Not sure what to call this, so I go for Greatest Common + # Divisor. Had to look for hints at subreddit to find it out. + # It basically fasten up the computation speed by keeping + # the numbers low. + # + # Apparently, this is common Modular arithmetic: + # https://en.wikipedia.org/wiki/Modular_arithmetic + gcd = 1 + for d in [m["divisor"] for m in M]: + gcd *= d + for rounds, wld in [(20, True), (10_000, False)]: + B = defaultdict(int) + MM = deepcopy(M) + for _ in range(rounds): + for i, m in enumerate(MM): + while m["items"]: + wl = m["items"].pop(0) + B[i] += 1 + x, y, z = m["operation_values"] + x = int(wl) if x == "old" else int(x) + z = int(wl) if z == "old" else int(z) + match y: + case "+": + wl = x + z + case "*": + wl = x * z + if wld: + wl = wl // 3 + else: + wl = wl % gcd + if wl % m["divisor"] == 0: + MM[m["if_true"]]["items"].append(wl) + else: + MM[m["if_false"]]["items"].append(wl) + a, b, *_ = sorted(B.values(), reverse=True) + p12.append(a * b) + p1, p2 = p12 + return p1, p2 + + +def _state(M): + return tuple([(tuple(m["items"])) for m in M]) + + +if __name__ == "__main__": + with open("./input/11.txt", "r") as f: + inp = f.read().strip() + + p1, p2 = solve(inp) + + print(p1) + print(p2) + + assert p1 == 50616 + assert p2 == 11309046332 -- 2.45.3 From 92df810943b637bd612abbf1cea5291c3249e326 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20Engl=C3=B6f=20Ytterstr=C3=B6m?= Date: Sun, 30 Nov 2025 18:48:52 +0100 Subject: [PATCH 13/13] Solve 2022 day 12 pt 1-2 BFS, baby. 2 things got me. * S position was not (0,0) in the puzzle input. Such embarrasment to loose time over something so stupid. * I asumed the elevation was fixed to to increase AND DECREASE by 1. From the problem description: > To avoid needing to get out your climbing gear, the elevation of the > destination square can be at most one higher than the elevation of > your current square; that is, if your current elevation is m, you > could step to elevation n, but not to elevation o. _(This also means > that the elevation of the destination square can be much lower than > the elevation of your current square.)_ This means elevation only can _increase_ by one, but _decrease_ with more. So my wrong code: 0 <= abs(n - e) <= 1 got fixed with: n - e <= 1 For pt 2, I reused the loop to find S to collect all "a" positions, and looped them as starting points. Execution time is not the best, but it works. --- 2022-python/output/__init__.py | 6 ++++ 2022-python/output/day_12.py | 58 ++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 2022-python/output/day_12.py diff --git a/2022-python/output/__init__.py b/2022-python/output/__init__.py index ba3186c..809c3f5 100644 --- a/2022-python/output/__init__.py +++ b/2022-python/output/__init__.py @@ -78,6 +78,12 @@ def vdbg(seen, h, w): print("".join(["#" if (r, c) in seen else " " for c in range(w)])) +def vvdbg(seen, h, w): + """Print-debug visited positions of a matrix, with values""" + for r in range(h): + print("".join([seen[(r, c)] if (r, c) in seen else "." for c in range(w)])) + + def cw(y, x): """Flip a (y, x) direction counterwise: U->R, R->D, D->L, L->U. diff --git a/2022-python/output/day_12.py b/2022-python/output/day_12.py new file mode 100644 index 0000000..cb6d27a --- /dev/null +++ b/2022-python/output/day_12.py @@ -0,0 +1,58 @@ +import re +from collections import deque, Counter, defaultdict +from heapq import heappop, heappush +from itertools import compress, combinations, chain, permutations + +from output import matrix, D, DDa, vvdbg, DD, ADJ, ints, mhd, mdbg, vdbg, cw, ccw, bk + + +def solve(data): + M, H, W = matrix(data) + E = S = None + p1 = p2 = H * W + SP = [] + for rr in range(H): + for cc in range(W): + if M[rr][cc] == "E": + E = (rr, cc) + if M[rr][cc] == "S": + S = (rr, cc) + SP.append((rr, cc)) + if M[rr][cc] == "a": + SP.append((rr, cc)) + M = [list(row) for row in M] + for rc, v in [(S, "a"), (E, "z")]: + r, c = rc + M[r][c] = v + for sp in SP: + Q = [(sp, ord("a"), 0)] + seen = set() + while Q: + yx, e, s = Q.pop(0) + if yx in seen: + continue + seen.add(yx) + if yx == E: + if sp == S: + p1 = s + p2 = min(p2, s) + y, x = yx + for dy, dx in D: + if 0 <= y + dy < H and 0 <= x + dx < W: + n = ord(M[y + dy][x + dx]) + if n - e <= 1: + Q.append(((y + dy, x + dx), n, s + 1)) + return p1, p2 + + +if __name__ == "__main__": + with open("./input/12.txt", "r") as f: + inp = f.read().strip() + + p1, p2 = solve(inp) + + print(p1) + print(p2) + + assert p1 == 517 + assert p2 == 512 -- 2.45.3