From 8d4af7e6e9e21869bb460a34823326c235029032 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20Engl=C3=B6f=20Ytterstr=C3=B6m?= Date: Wed, 27 Nov 2024 23:36:36 +0100 Subject: [PATCH] Solve 2016:13 p1-2 "A Maze of Twisty Little Cubicles" BFS baby. --- 2016-python2/README.md | 50 +++++++++++++ 2016-python2/aoc.py | 125 ++++++++++++++++++++++++++++++++ 2016-python2/output/__init__.py | 98 +++++++++++++++++++++++++ 2016-python2/output/day_13.py | 55 ++++++++++++++ 4 files changed, 328 insertions(+) create mode 100644 2016-python2/README.md create mode 100644 2016-python2/aoc.py create mode 100644 2016-python2/output/__init__.py create mode 100644 2016-python2/output/day_13.py diff --git a/2016-python2/README.md b/2016-python2/README.md new file mode 100644 index 0000000..7576b38 --- /dev/null +++ b/2016-python2/README.md @@ -0,0 +1,50 @@ +# Advent of Code 2016 + +Solutions for #aoc2016 in Python 3 (3.12.7). + +## Setup + +Since I want to remember, this is what was used to solve +the puzzles. + +- Lenovo Thinkpad x260 laptop with Arch Linux. +- Hyprland with gBar. +- Editor: Zed. +- Terminal: Alacritty. + +## Help scripts + +Display all solved puzzles: + + python aoc.py + +To bootstrap a new puzzle (creates `input/.txt` and `output/day_.py`): + + python aoc.py + +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/2016-python2/aoc.py b/2016-python2/aoc.py new file mode 100644 index 0000000..517712d --- /dev/null +++ b/2016-python2/aoc.py @@ -0,0 +1,125 @@ +import sys +from pathlib import Path + + +def headline(n, title): + """Print day number and name, followed by a ruler. Used by the answer decorator""" + print(f"\n--- Day {n}: {title} ---\n") + + +year = 2016 + +try: + _, day_no, *name = sys.argv +except ValueError: + day_no = None + name = None + +print( + f"\nAdvent of Code {year}" "\n###################" "\n\nby Anders Englöf Ytterström" +) + +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) + print(f"\n- creating output/day_{padded_no}.py") + with open("output/day_{}.py".format(padded_no), "w") as s: + s.write( + f""" +from output import answer # , matrix, D, DD, ADJ, ints, mhd, mdbg, vdbg + +n = {day_no} +title = "{name}" + + +@answer(1, "Answer is {{}}") +def part_1(presolved): + return presolved[0] + + +@answer(2, "Actually, answer is {{}}") +def part_2(presolved): + return presolved[1] + + +def solve(data): + return 1, 2 + + +if __name__ == "__main__": + # 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 + inp = solve(inp) + + a = part_1(inp) + # b = part_2(inp) + + # uncomment and replace 0 with actual output to refactor code + # and ensure nonbreaking changes + # assert a == 0 + # assert b == 0 +""".strip() + + "\n" + ) + print( + f""" +Done! start coding. + +Puzzle link: +https://adventofcode.com/{year}/day/{day_no} + +Puzzle input (copy and paste to input/{day_no.zfill(2)}.txt): +https://adventofcode.com/{year}/day/{day_no}/input + """ + ) + exit(0) + + +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(), + ["n", "title", "part_1", "part_2"], + 0, + ) + with open(f"./input/{i}.txt", "r") as f: + data = f.read().strip() + headline(day.n, day.title) + try: + data = day.presolve(data) + except AttributeError: + pass + try: + data = day.solve(data) + except AttributeError: + pass + if day.part_1(data, decorate=True): + stars += 1 + if day.part_2(data, decorate=True): + 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/2016-python2/output/__init__.py b/2016-python2/output/__init__.py new file mode 100644 index 0000000..340e6cd --- /dev/null +++ b/2016-python2/output/__init__.py @@ -0,0 +1,98 @@ +import functools +import re + +# Directions/Adjacents for 2D matrices, in the order UP, RIGHT, DOWN, LEFT +D = [ + (-1, 0), + (0, 1), + (1, 0), + (0, -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), +} + +# 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 answer(part_index, fmt_string): + """Decorator to present a solution in a human readable format""" + + def decorator_aoc(func): + @functools.wraps(func) + def wrapper_aoc(*args, **kwargs): + decorate = kwargs.get("decorate", False) + if decorate: + del kwargs["decorate"] + answer = func(*args, **kwargs) + if not decorate: + print(answer) + else: + formatted = fmt_string.format(answer) + print(f" {part_index}) {formatted}") + return answer + + return wrapper_aoc + + return decorator_aoc + + +def ints(s): + """Extract all 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 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 diff --git a/2016-python2/output/day_13.py b/2016-python2/output/day_13.py new file mode 100644 index 0000000..64e9b08 --- /dev/null +++ b/2016-python2/output/day_13.py @@ -0,0 +1,55 @@ +from collections import Counter +from math import inf + +from output import D + + +def solve(data, E=(31, 39)): + S = (1, 1) + p1, p2 = bfs(S, E, int(data), inf, 0) + return p1, p2 + + +def bfs(S, E, z, p1, p2): + seen = set() + q = [(S, 0)] + while q: + m, w = q.pop(0) + if m in seen: + continue + seen.add(m) + + if m == E: + p1 = min(w, p1) + if w <= 50: + p2 = max(p2, len(seen)) + + x, y = m + g = [ + (x, y) + for x, y in [(x + c, y + r) for r, c in D] + if x >= 0 and y >= 0 and valid(x, y, z) + ] + + for s in g: + q.append((s, w + 1)) + return p1, p2 + + +def valid(x, y, z): + n = (x * x + 3 * x + 2 * x * y + y + y * y) + z + s = Counter(f"{n:b}")["1"] + return s % 2 == 0 + + +if __name__ == "__main__": + with open("./input/13.txt", "r") as f: + inp = f.read().strip() + + t, _ = solve("10", (7, 4)) + assert t == 11 + + p1, p2 = solve(inp) + + print(p1) + print(p2)