parent
fcbeb4bfdd
commit
bad86889d6
20 changed files with 0 additions and 1291 deletions
2
2023-python/.gitignore
vendored
2
2023-python/.gitignore
vendored
|
|
@ -1,2 +0,0 @@
|
||||||
__pycache__
|
|
||||||
input
|
|
||||||
|
|
@ -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/<day_no>.txt` and `output/day_<day_no>.py`):
|
|
||||||
|
|
||||||
python aoc.py <day_no> <puzzle_name>
|
|
||||||
|
|
||||||
Manually copy the puzzle input from https://adventofcode.com and paste it in `input/<day_no>.txt`
|
|
||||||
to start coding.
|
|
||||||
|
|
||||||
wl-paste > input/<day_no>.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
|
|
||||||
|
|
@ -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("")
|
|
||||||
|
|
@ -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)]))
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
Loading…
Add table
Reference in a new issue