diff --git a/2023-python/.gitignore b/2023-python/.gitignore deleted file mode 100644 index fa878cc..0000000 --- a/2023-python/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -__pycache__ -input \ No newline at end of file diff --git a/2023-python/README.md b/2023-python/README.md deleted file mode 100644 index 249aff0..0000000 --- a/2023-python/README.md +++ /dev/null @@ -1,42 +0,0 @@ -Advent of Code 2023 -=================== - -Solutions for #aoc2023 in Python 3 (3.11.5). - -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 'xclip -selection clipboard -o | 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/2023-python/aoc.py b/2023-python/aoc.py deleted file mode 100644 index 090129d..0000000 --- a/2023-python/aoc.py +++ /dev/null @@ -1,126 +0,0 @@ -import os -import sys - - -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 = 2023 - -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" -) - -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("- making sure input dir exists") - if not os.path.exists("input"): - os.makedirs("input") - - 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/2023-python/output/__init__.py b/2023-python/output/__init__.py deleted file mode 100644 index 3a034d6..0000000 --- a/2023-python/output/__init__.py +++ /dev/null @@ -1,82 +0,0 @@ -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)])) diff --git a/2023-python/output/day_01.py b/2023-python/output/day_01.py deleted file mode 100644 index f2eecfb..0000000 --- a/2023-python/output/day_01.py +++ /dev/null @@ -1,51 +0,0 @@ -import re -from output import answer - -n = 1 -title = "Trebuchet?!" - - -@answer(1, "Calibration values sum: {}, excluding spelled out digits") -def part_1(data): - def value(s): - s = [int(c) for c in s if c.isdigit()] - return s[0] * 10 + s[-1] - - return sum(value(line) for line in data.split()) - - -@answer(2, "Calibration values sum: {}, including spelled out digits") -def part_2(data): - mp = { - "one": 1, - "two": 2, - "three": 3, - "four": 4, - "five": 5, - "six": 6, - "seven": 7, - "eight": 8, - "nine": 9, - } - - def value(l): - s = [ - int(c) if c.isdigit() else mp[c] - for c in re.findall( - r"(?=(\d|one|two|three|four|five|six|seven|eight|nine))", l - ) - ] - return s[0] * 10 + s[-1] - - return sum(value(line) for line in data.split()) - - -if __name__ == "__main__": - with open("./input/01.txt", "r") as f: - inp = f.read().strip() - - a = part_1(inp) - b = part_2(inp) - - assert a == 54634 - assert b == 53855 diff --git a/2023-python/output/day_02.py b/2023-python/output/day_02.py deleted file mode 100644 index f5ac032..0000000 --- a/2023-python/output/day_02.py +++ /dev/null @@ -1,49 +0,0 @@ -import re -from math import prod -from collections import defaultdict -from output import answer - -n = 2 -title = "Cube Conundrum" - - -@answer(1, "Sum of all possible game IDs: {}") -def part_1(data): - return sum( - [ - id + 1 - for id, game in enumerate(data.splitlines()) - if not sum( - any( - [ - c == "red" and int(n) > 12, - c == "green" and int(n) > 13, - c == "blue" and int(n) > 14, - ] - ) - for n, c in re.findall(r"(\d+) (\w+)", game) - ) - ] - ) - - -@answer(2, "Sum of all cube set powers: {}") -def part_2(data): - def power(game): - seen = defaultdict(int) - for n, c in re.findall(r"(\d+) (\w+)", game): - seen[c] = max([seen[c], int(n)]) - return prod([seen["blue"], seen["red"], seen["green"]]) - - return sum(power(game) for game in data.splitlines()) - - -if __name__ == "__main__": - with open("./input/02.txt", "r") as f: - inp = f.read().strip() - - a = part_1(inp) - b = part_2(inp) - - assert a == 2439 - assert b == 63711 diff --git a/2023-python/output/day_03.py b/2023-python/output/day_03.py deleted file mode 100644 index 19df9ae..0000000 --- a/2023-python/output/day_03.py +++ /dev/null @@ -1,68 +0,0 @@ -from collections import deque -from output import answer - -n = 3 -title = "Gear Ratios" - - -@answer(1, "Sum of all part numbers is {} in the engine schematic") -def part_1(presolved): - s, _ = presolved - return s - - -@answer(2, "Gear ratio sums is {} in the engine schematic") -def part_2(presolved): - _, gr = presolved - return gr - - -def presolve(data): - data = data.split() - adj = (-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1) - w = len(data[0]) - h = len(data) - s = list() - gr = list() - for y in range(w): - for x in range(h): - if data[y][x] != "." and not data[y][x].isdigit(): - seen = set() - t = list() - for oy, ox in adj: - if (y + oy, x + ox) in seen: - continue - if data[y + oy][x + ox].isdigit(): - n = deque([data[y + oy][x + ox]]) - i = x + ox - 1 - while i in range(w) and data[y + oy][i].isdigit(): - n.append(data[y + oy][i]) - seen.add((y + oy, i)) - i -= 1 - i = x + ox + 1 - while i in range(w) and data[y + oy][i].isdigit(): - n.appendleft(data[y + oy][i]) - seen.add((y + oy, i)) - i += 1 - t.append(sum(10**m * int(d) for m, d in enumerate(n))) - # part 1 - s.append(sum(t)) - # part 2 - if data[y][x] == "*" and len(t) == 2: - a, b = t - gr.append(a * b) - - return sum(s), sum(gr) - - -if __name__ == "__main__": - with open("./input/03.txt", "r") as f: - inp = f.read().strip() - - parsed = presolve(inp) - - a = part_1(parsed) - b = part_2(parsed) - - assert a == 553825 - assert b == 93994191 diff --git a/2023-python/output/day_04.py b/2023-python/output/day_04.py deleted file mode 100644 index 66c9f8c..0000000 --- a/2023-python/output/day_04.py +++ /dev/null @@ -1,47 +0,0 @@ -import re -from collections import defaultdict -from output import answer - -n = 4 -title = "Scratchcards" - - -@answer(1, "Sum of all scratchcard points are {}") -def part_1(presolved): - scores, _ = presolved - return scores - - -@answer(2, "Ends up wih a total of {} scratchcards") -def part_2(presolved): - _, count = presolved - return count - - -def presolve(data): - scores = [] - count = defaultdict(int) - for cid, line in enumerate(data.splitlines()): - a, b = line.split("|") - a = set(re.findall(r"\d+", a)[1:]) - b = set(re.findall(r"\d+", b)) - ab = len(a & b) - if ab > 0: - scores.append(2 ** (ab - 1)) - count[cid] += 1 - for i in range(cid + 1, cid + ab + 1): - count[i] += count[cid] - return sum(scores), sum(count.values()) - - -if __name__ == "__main__": - with open("./input/04.txt", "r") as f: - inp = f.read().strip() - - inp = presolve(inp) - - a = part_1(inp) - b = part_2(inp) - - assert a == 21919 - assert b == 9881048 diff --git a/2023-python/output/day_05.py b/2023-python/output/day_05.py deleted file mode 100644 index 4147802..0000000 --- a/2023-python/output/day_05.py +++ /dev/null @@ -1,73 +0,0 @@ -import re -from math import inf -from output import answer - -n = 5 -title = "If You Give A Seed A Fertilizer" - - -@answer(1, "Nearest location for seed id list is {}") -def part_1(presolved): - l, _ = presolved - return l - - -@answer(2, "Interpreting ranges of seeds, nearest location is {}") -def part_2(presolved): - _, l = presolved - return l - - -def presolve(data): - seeds, *process = data.split("\n\n") - seed_ranges = [[int(x) for x in ar.split()] for ar in re.findall(r"\d+ \d+", seeds)] - seed_values = [int(v) for v in seeds.split()[1:]] - processes = [ - [tuple(map(int, line.split())) for line in step.splitlines()[1:]] - for step in process - ] - - p1 = _process(seed_values, processes) - - p2 = 26829000 # takes 5m if starting from 0 - while True: - g = _process([p2], processes, reverse=True) - if any(g >= a and g < a + r for a, r in seed_ranges): - break - p2 += 1 - - return p1, p2 - - -def _process(seeds, processes, reverse=False): - n = inf - for start in seeds: - n = min(n, _nearest(start, processes, reverse=reverse)) - return n - - -def _nearest(start, processes, reverse=False): - procs = processes if not reverse else processes[::-1] - v = start - for steps in procs: - for line in steps: - dest, src, r = line - if reverse: - dest, src = src, dest - if v >= src and v < src + r: - v = dest + v - src - break - return v - - -if __name__ == "__main__": - with open("./input/05.txt", "r") as f: - inp = f.read().strip() - - inp = presolve(inp) - - a = part_1(inp) - b = part_2(inp) - - assert a == 278755257 - assert b == 26829166 diff --git a/2023-python/output/day_06.py b/2023-python/output/day_06.py deleted file mode 100644 index 1f9bfa7..0000000 --- a/2023-python/output/day_06.py +++ /dev/null @@ -1,47 +0,0 @@ -from math import prod, sqrt, ceil, floor -from output import answer - -n = 6 -title = "Wait For It" - - -@answer(1, "The product of all record times for all races is {}") -def part_1(presolved): - return presolved[0] - - -@answer(2, "The product of all record times for the single long race is {}") -def part_2(presolved): - return presolved[1] - - -def presolve(data): - values = data.split() - l = len(values) // 2 - races = list( - map( - lambda x: (int(x[0]), int(x[1])), list(zip(values[: l + 1], values[l:]))[1:] - ) - ) - p1 = prod(sum(bpt * (t - bpt) > d for bpt in range(t)) for t, d in races) - t = int("".join(values[1:l])) - d = int("".join(values[l + 1 :])) - # quadratic formula: - # https://en.wikipedia.org/wiki/Quadratic_formula - l = ceil((-t + sqrt(t**2 - 4 * d)) / -2) - h = floor((-t - sqrt(t**2 - 4 * d)) / -2) - p2 = h - l + 1 - return p1, p2 - - -if __name__ == "__main__": - with open("./input/06.txt", "r") as f: - inp = f.read().strip() - - inp = presolve(inp) - - a = part_1(inp) - b = part_2(inp) - - assert a == 1083852 - assert b == 23501589 diff --git a/2023-python/output/day_07.py b/2023-python/output/day_07.py deleted file mode 100644 index a583564..0000000 --- a/2023-python/output/day_07.py +++ /dev/null @@ -1,94 +0,0 @@ -from collections import Counter -from output import answer - -n = 7 -title = "Camel Cards" - - -@answer(1, "Total winnings are {}") -def part_1(data): - return _solve(data) - - -@answer(2, "Including jokers, total winnings are {}") -def part_2(data): - return _solve(data.replace("J", "0")) - - -def _solve(data): - return sum( - c[-1] * i for i, c in enumerate(sorted(_hand(h) for h in data.splitlines()), 1) - ) - - -def _hand(hb): - h, b = hb.split() - b = int(b) - h = h.translate(M) - return (_rank(h), h, b) - - -def _rank(h): - """ - Rank hand 0-6, letting jokers (0) aid the highest possible hand - - >>> _rank("10110") - 6 - >>> _rank("11110") - 6 - >>> _rank("12110") - 5 - >>> _rank("12100") - 5 - >>> _rank("12000") - 5 - >>> _rank("12120") - 4 - >>> _rank("12300") - 3 - >>> _rank("12310") - 3 - >>> _rank("12340") - 1 - """ - hc = Counter(h) - hcv = hc.values() - j = hc["0"] - del hc["0"] - p = sum(c == 2 for v, c in hc.items() if v != 0) - if j == 5 or max(hcv) + j == 5: - return 6 - if max(hcv) + j == 4: - return 5 - if (3 in hcv and 2 in hcv) or (p == 2 and j > 0): - return 4 - if max(hcv) + j == 3: - return 3 - if p + j > 1: - return 2 - if p + j > 0: - return 1 - return 0 - - -M = dict( - [ - (ord(o), ord(n)) - for o, n in {"T": "A", "J": "B", "Q": "C", "K": "D", "A": "E"}.items() - ] -) - - -if __name__ == "__main__": - import doctest - - doctest.testmod() - - with open("./input/07.txt", "r") as f: - inp = f.read().strip() - - a = part_1(inp) - b = part_2(inp) - - assert a == 249390788 - assert b == 248750248 diff --git a/2023-python/output/day_08.py b/2023-python/output/day_08.py deleted file mode 100644 index b2f397b..0000000 --- a/2023-python/output/day_08.py +++ /dev/null @@ -1,61 +0,0 @@ -import re -from math import lcm -from output import answer - -n = 8 -title = "Haunted Wasteland" - - -@answer(1, "One can reach Z in {} steps") -def part_1(presolved): - steps, _ = presolved - return steps - - -@answer(2, "Ghost path takes {} steps before all ghosts are at a Z positon") -def part_2(presolved): - _, ghost_meet_point = presolved - return ghost_meet_point - - -def presolve(data): - d, els = data.split("\n\n") - r = len(d) - e = dict() - for el in els.splitlines(): - k, *lr = re.match(r"(\w+) = \((\w+), (\w+)\)", el).groups() - e[k] = lr - - p1 = 0 - pos = "AAA" - while pos != "ZZZ": - i = 0 if d[p1 % r] == "L" else 1 - pos = e[pos][i] - p1 += 1 - - p2 = 0 - z = list() - for spos in [p for p in e.keys() if p.endswith("A")]: - s = 0 - pos = spos - while not pos.endswith("Z"): - i = 0 if d[s % r] == "L" else 1 - pos = e[pos][i] - s += 1 - z.append(s) - p2 = lcm(*z) - - return p1, p2 - - -if __name__ == "__main__": - with open("./input/08.txt", "r") as f: - inp = f.read().strip() - - inp = presolve(inp) - - a = part_1(inp) - b = part_2(inp) - - assert a == 17141 - assert b == 10818234074807 diff --git a/2023-python/output/day_09.py b/2023-python/output/day_09.py deleted file mode 100644 index 423047f..0000000 --- a/2023-python/output/day_09.py +++ /dev/null @@ -1,40 +0,0 @@ -from output import answer - -n = 9 -title = "Mirage Maintenance" - - -@answer(1, "OASIS report extrapolated values sum is {}") -def part_1(data): - lines = [[int(d) for d in line.split()] for line in data.splitlines()] - return _solve(lines) - - -@answer(2, "Using prepending, OASIS report extrapolated values sum is {}") -def part_2(data): - lines = [[int(d) for d in line.split()[::-1]] for line in data.splitlines()] - return _solve(lines) - - -def _solve(lines): - vs = 0 - for l in lines: - h = [l] - while any(n != 0 for n in h[-1]): - h.append([b - a for a, b in zip(h[-1], h[-1][1:])]) - h = h[::-1] - for i in range(len(h)): - h[i].append(0 if i == 0 else h[i - 1][-1] + h[i][-1]) - vs += h[-1][-1] - return vs - - -if __name__ == "__main__": - with open("./input/09.txt", "r") as f: - inp = f.read().strip() - - a = part_1(inp) - b = part_2(inp) - - assert a == 1702218515 - assert b == 925 diff --git a/2023-python/output/day_10.py b/2023-python/output/day_10.py deleted file mode 100644 index 7420d86..0000000 --- a/2023-python/output/day_10.py +++ /dev/null @@ -1,94 +0,0 @@ -from collections import deque -from output import answer - -n = 10 -title = "Pipe Maze" - - -D = (-1, 0), (0, 1), (1, 0), (0, -1) - -C = { - (-1, 0): ["|", "7", "F"], - (0, 1): ["-", "7", "J"], - (1, 0): ["|", "L", "J"], - (0, -1): ["-", "L", "F"], -} - -A = { - "S": [0, 1, 2, 3], - "-": [1, 3], - "|": [0, 2], - "F": [1, 2], - "L": [0, 1], - "7": [2, 3], - "J": [0, 3], -} - - -@answer(1, "Farthest away pipe is at {}") -def part_1(presolved): - return presolved[0] - - -@answer(2, "{} spots are encapsulated by pipes") -def part_2(presolved): - return presolved[1] - - -def presolve(data): - matrix = data.split() - w = len(matrix[0]) - h = len(matrix) - q = deque() - visited = set() - - for r in range(h): - if "S" in matrix[r]: - start = (r, matrix[r].index("S")) - q.append(start) - break - - while q: - o = q.popleft() - visited.add(o) - for di in A[matrix[o[0]][o[1]]]: - d = D[di] - r = o[0] + d[0] - c = o[1] + d[1] - if r >= 0 and r < h and c >= 0 and c < w: - t = matrix[r][c] - p = (r, c) - if p not in visited and t != "." and t in C[d]: - q.append(p) - p1 = len(visited) // 2 - - p2 = 0 - for y in range(h): - for x in range(w): - if (y, x) in visited: - continue - crosses = 0 - y2, x2 = y, x - while y2 < h and x2 < w: - c2 = matrix[y2][x2] - if (y2, x2) in visited and c2 not in "L7": - crosses += 1 - x2 += 1 - y2 += 1 - if crosses % 2 == 1: - p2 += 1 - - return p1, p2 - - -if __name__ == "__main__": - with open("./input/10.txt", "r") as f: - inp = f.read() - - inp = presolve(inp) - - a = part_1(inp) - b = part_2(inp) - - assert a == 6846 - assert b == 325 diff --git a/2023-python/output/day_11.py b/2023-python/output/day_11.py deleted file mode 100644 index 5ad4def..0000000 --- a/2023-python/output/day_11.py +++ /dev/null @@ -1,63 +0,0 @@ -from itertools import combinations -from output import answer - -n = 11 -title = "Cosmic Expansion" - - -@answer(1, "Sum of all galaxy shortest distances is {}") -def part_1(data): - return data[0] - - -@answer(2, "Exapanding by 1M, sum is {}") -def part_2(data): - return data[1] - - -def presolve(data): - m = data.splitlines() - er = set() - ec = set() - for i, r in enumerate(m): - if "#" not in r: - er.add(i) - for i, c in enumerate(zip(*m)): - if "#" not in c: - ec.add(i) - h = len(m) - w = len(m[0]) - g1 = [] - g2 = [] - e = 1e6 - for r in range(h): - for c in range(w): - if m[r][c] == "#": - ro = len(er & set(range(r))) - co = len(ec & set(range(c))) - g1.append((r + ro, c + co)) - g2.append((ro * e + r - ro, co * e + c - co)) - p1 = sum( - abs(rc1[0] - rc2[0]) + abs(rc1[1] - rc2[1]) for rc1, rc2 in combinations(g1, 2) - ) - p2 = int( - sum( - abs(rc1[0] - rc2[0]) + abs(rc1[1] - rc2[1]) - for rc1, rc2 in combinations(g2, 2) - ) - ) - - return p1, p2 - - -if __name__ == "__main__": - with open("./input/11.txt", "r") as f: - inp = f.read().strip() - - inp = presolve(inp) - - a = part_1(inp) - b = part_2(inp) - - assert a == 9370588 - assert b == 746207878188 diff --git a/2023-python/output/day_12.py b/2023-python/output/day_12.py deleted file mode 100644 index d0cc2a3..0000000 --- a/2023-python/output/day_12.py +++ /dev/null @@ -1,64 +0,0 @@ -from functools import cache -from output import answer - -n = 12 -title = "Hot Springs" - - -@answer(1, "sum of all possible combinations is {}") -def part_1(presolved): - return presolved[0] - - -@answer(2, "sum of all possible combinations is {} when unfolded") -def part_2(presolved): - return presolved[1] - - -def presolve(data): - lines = [ - (a, list(map(int, b.split(",")))) - for a, b in (line.split() for line in data.splitlines()) - ] - p1 = sum(_inspect(a, tuple(b)) for a, b in lines) - p2 = sum(_inspect("?".join([a] * 5), tuple(b * 5)) for a, b in lines) - return p1, p2 - - -@cache -def _inspect(s, cs): - r = len(s) - csl = len(cs) - if r == 0: - return 1 if csl == 0 else 0 - o, *f = s - f = "".join(f) - if o == ".": - return _inspect(f, cs) - if o == "?": - return _inspect("." + f, cs) + _inspect("#" + f, cs) - if not csl: - return 0 - g = cs[0] - if g > r or "." in s[0:g]: - return 0 - elif csl > 1: - if g + 1 > r or s[g] == "#": - return 0 - else: - return _inspect(s[g + 1 :], cs[1:]) - elif csl == 1: - return _inspect(s[g:], ()) - - -if __name__ == "__main__": - with open("./input/12.txt", "r") as f: - inp = f.read().strip() - - inp = presolve(inp) - - a = part_1(inp) - b = part_2(inp) - - assert a == 7118 - assert b == 7030194981795 diff --git a/2023-python/output/day_13.py b/2023-python/output/day_13.py deleted file mode 100644 index fa6f361..0000000 --- a/2023-python/output/day_13.py +++ /dev/null @@ -1,56 +0,0 @@ -from output import answer - -n = 13 -title = "Point of Incidence" - - -@answer(1, "Summarizing the notes gives {}") -def part_1(presolved): - return presolved[0] - - -@answer(2, "Summarizing the notes allowing off-by-1 gives {}") -def part_2(presolved): - return presolved[1] - - -def presolve(data): - g = [l.split() for l in data.split("\n\n")] - - p1 = sum(d * n for d, n in _inspect(g)) - p2 = sum(d * n for d, n in _inspect(g, 1)) - - return p1, p2 - - -def _inspect(g, a=0): - af = [] - for m in g: - for d, n in [(100, m), (1, tuple(zip(*m)))]: - af.append((d, _compare(n, a))) - return af - - -def _compare(l, a=0): - for i in range(1, len(l)): - if ( - sum( - sum(a != b for a, b in zip(x, y)) for x, y in zip(l[i - 1 :: -1], l[i:]) - ) - == a - ): - return i - return 0 - - -if __name__ == "__main__": - with open("./input/13.txt", "r") as f: - inp = f.read().strip() - - inp = presolve(inp) - - a = part_1(inp) - b = part_2(inp) - - assert a == 29213 - assert b == 37453 diff --git a/2023-python/output/day_14.py b/2023-python/output/day_14.py deleted file mode 100644 index 48d3ea2..0000000 --- a/2023-python/output/day_14.py +++ /dev/null @@ -1,83 +0,0 @@ -from output import answer - -n = 14 -title = "Parabolic Reflector Dish" - - -@answer(1, "Total initial load on the northern beams: {}") -def part_1(presolved): - return presolved[0] - - -@answer(2, "After some humble load testing, the northern beam load is {}") -def part_2(presolved): - return presolved[1] - - -BAEST = 1000_000_000 - - -def presolve(data): - m = [list(l) for l in data.split()] - s = len(m[0]) - m1 = _tilt(m) - - p1 = sum(sum((s - w) * o.count("O") for o in r) for w, r in enumerate(m1)) - - def impl(rc): - return "".join(["".join(r) for r in rc]) - - i = 0 - seen = [] - while True: - i += 1 - for _ in range(4): - m = _tilt(m) - m = _rotate(m) - im = impl(m) - if im in seen: - break - else: - seen.append(im) - m2 = m - c = seen.index(im) + 1 - for _ in range((BAEST - i) % (i - c)): - for j in range(4): - m2 = _tilt(m2) - m2 = _rotate(m2) - p2 = sum(sum((s - w) * o.count("O") for o in r) for w, r in enumerate(m2)) - return p1, p2 - - -def _rotate(m): - return [list(l) for l in zip(*m[::-1])] - - -def _tilt(m): - m = [list(l) for l in zip(*m)] - h = len(m[0]) - for c in m: - u = True - while u: - u = False - for i in range(h - 1): - j = i + 1 - if c[i] == "#" or c[j] == "#": - continue - if c[i] < c[j]: - c[j], c[i] = c[i], c[j] - u = True - return [list(l) for l in zip(*m)] - - -if __name__ == "__main__": - with open("./input/14.txt", "r") as f: - inp = f.read().strip() - - inp = presolve(inp) - - a = part_1(inp) - b = part_2(inp) - - assert a == 109596 - assert b == 96105 diff --git a/2023-python/output/day_15.py b/2023-python/output/day_15.py deleted file mode 100644 index 31a3e98..0000000 --- a/2023-python/output/day_15.py +++ /dev/null @@ -1,63 +0,0 @@ -from collections import OrderedDict, defaultdict - -from output import answer - -n = 15 -title = "Lens Library" - - -@answer(1, "Sum of HASH algorithm results: {}") -def part_1(presolved): - return presolved[0] - - -@answer(2, "Focusing power of the resulting configuration: {}") -def part_2(presolved): - return presolved[1] - - -def presolve(data): - def h(s): - v = 0 - for a in s: - if a == "\n": - continue - v += ord(a) - v *= 17 - v = v % 256 - return v - - p1 = sum(h(c) for c in data.split(",")) - - b = defaultdict(OrderedDict) - for lr in data.split(","): - if "=" in lr: - l, r = lr.split("=") - if r == "": - continue - k = h(l) - b[k][l] = r - if "-" in lr: - l, _r = lr.split("-") - k = h(l) - if l in b[k]: - del b[k][l] - p2 = 0 - for i, c in b.items(): - for j, f in enumerate(b[i].values(), 1): - p2 += (i + 1) * j * int(f) - - return p1, p2 - - -if __name__ == "__main__": - with open("./input/16.txt", "r") as f: - inp = f.read().strip() - - inp = presolve(inp) - - a = part_1(inp) - b = part_2(inp) - - assert a == 509784 - assert b == 230197 diff --git a/2023-python/output/day_16.py b/2023-python/output/day_16.py deleted file mode 100644 index bab2c68..0000000 --- a/2023-python/output/day_16.py +++ /dev/null @@ -1,86 +0,0 @@ -from itertools import chain - -from output import D, answer, matrix - -n = 16 -title = "The Floor Will Be Lava" - - -@answer(1, "Energized tiles count, starting at top-left facing right: {}") -def part_1(presolved): - return presolved[0] - - -@answer(2, "Max energized tiles count, starting from all edges: {}") -def part_2(presolved): - return presolved[1] - - -def presolve(data): - m, w, h = matrix(data) - p1 = 0 - p2 = 0 - for sp in chain( - [(h - 1, n, 0) for n in range(w)], - [(n, 0, 1) for n in range(h)], - [(0, n, 2) for n in range(w)], - [(n, w - 1, 3) for n in range(h)], - ): - q = [sp] - seen = set() - while q: - rcd = q.pop(0) - if (rcd) in seen: - continue - r, c, d = rcd - if r < 0 or r >= h or c < 0 or c >= w: - continue - seen.add((r, c, d)) - match m[r][c]: - case ".": - o1, o2 = D[d] - q.append((o1 + r, o2 + c, d)) - case "|": - if d in [0, 2]: - o1, o2 = D[d] - q.append((o1 + r, o2 + c, d)) - else: - for d in [(d - 1) % 4, (d + 1) % 4]: - o1, o2 = D[d] - q.append((o1 + r, o2 + c, d)) - case "-": - if d in [1, 3]: - o1, o2 = D[d] - q.append((o1 + r, o2 + c, d)) - else: - for d in [(d - 1) % 4, (d + 1) % 4]: - o1, o2 = D[d] - q.append((o1 + r, o2 + c, d)) - case "\\": - d += 1 if d in [1, 3] else -1 - d = d % 4 - o1, o2 = D[d] - q.append((o1 + r, o2 + c, d)) - case "/": - d += 1 if d in [0, 2] else -1 - d = d % 4 - o1, o2 = D[d % 4] - q.append((o1 + r, o2 + c, d)) - b = len(set([(r, c) for r, c, d in seen])) - if sp == (0, 0, 1): - p1 = b - p2 = max(p2, b) - return p1, p2 - - -if __name__ == "__main__": - with open("./input/16.txt", "r") as f: - inp = f.read().strip() - - inp = presolve(inp) - - a = part_1(inp) - b = part_2(inp) - - assert a == 7884 - assert b == 8185