2023 solutions (#6)
* Prep Advent of Code 2023 * Solve 2023:01 "Trebuchet?!" Turns out re methods are non-overlapping. And in true AoC manners, no provided test cases had overlaps. Luckily for me, some of the last lines in the input contained the string "oneight", so I was able to find it out quite fast. Revisions: 1) Reverse strings to find last digit 2) Use isdigit() and skip regex. 3) Use regexp with positive look-ahead. * Solve 2023:02 "Cube Conundrum" Very intermediate pythonic solution, regex would have made the code more compact. But since 2023:01 decreased the regex courage, This code will do. * Solve 2023:03 "Gear Ratios" Overslept, took about 55 mins. * Solve 2023:04 "Scratchcards" On a train that according to swedish tradition was late. Not a good environment to focus. Got stuck 2 times: - Initial code asumed the | was always after the 5th number, because of the example. Puzzle input had it at pos 10. Classic AoC mistake. - I had a hard time trying to understand the score count, I insisted there was meant to be a +1 at some point. > That means card 1 is worth 8 points (1 for > the first match, then doubled three times for > each of the three matches after the first) I should instead have just looked at the numbers. * Solve 2023:05 "If You Give A Seed A Fertilizer" Part 2 takes 66 minutes to run. There is some smart things to realize here. * Solve 2023:06 "Wait for it" * Solve 2023:07 "Camel Cards" * Solve 2023:08 "Haunted Wasteland" Part 2 would have taken 10-15 hours with brute force. After I figured out the puzzle input had circular A-Z paths, it was plain as day that LCM was the solution to the problem. https://en.wikipedia.org/wiki/Least_common_multiple * Solve 2023:09 "Mirage Maintenance" * Remove parse_input helper * Refactor 2023:05 Increasing speed from 66mins to 4mins. Caching the location value in the code to keep things at highest speed. From the subreddit, the algorithm looks like this. 1. Start att location 0 2. Traverse the whole process backwards, by reversing process steps and flipping dest/src positions. 3. Output is not a location, instead it's a seed. 4. if seed is in any seed range, use seed to get location as in step 1. 5. If not, increase location by 1 and repeat 2-4. * Solve 2023:10 "Pipe Maze" Got completely stuck on part 2. Tried some polygon area calculations, but none provided the correct answer, most likely due to the unorthodox polygon points. I also tried _shoelace method_ without any luck. Had not heard about that one earlier, it was a good learning experience even though I vould not use it here. By the subreddit, several people had had luck using the ray method. > Part 2 using one of my favorite facts from > graphics engineering: lets say you have an > enclosed shape, and you want to color every > pixel inside of it. How do you know if a given > pixel is inside the shape or not? Well, it > turns out: if you shoot a ray in any direction > from the pixel and it crosses the boundary an > _odd number_ of times, it's _inside_. if it crosses > an even number of times, it's outside. Works > for all enclosed shapes, even self-intersecting > and non-convex ones. * Fix flake8 errors for 2023:1-10 * Solve 2023:11 "Cosmic Expansion" * Solve 2023:12 "Hot Springs" * Solve 2023:13 "Point of Incidence" * Solve 2023:14 "Parabolic Reflector Dish" * Solve 2023:15 "Lens Library" WALLOFTEXT for part 2, took me 90 minutes to find this important text: > Each step begins with a sequence of letters that > indicate the label of the lens on which the step > operates. The result of running the HASH algorithm > on the label indicates the correct box for that > step. It also clarifies how part 2 and part 1 relates. * Solve 2023:16 "The Floor Will Be Lava" --------- Co-authored-by: Anders Englöf Ytterström <anders@playmaker.ai>
This commit is contained in:
parent
a153d0e09e
commit
b6e048e4a7
20 changed files with 1291 additions and 0 deletions
2
2023-python/.gitignore
vendored
Normal file
2
2023-python/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
__pycache__
|
||||
input
|
||||
42
2023-python/README.md
Normal file
42
2023-python/README.md
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
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
|
||||
126
2023-python/aoc.py
Normal file
126
2023-python/aoc.py
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
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("")
|
||||
82
2023-python/output/__init__.py
Normal file
82
2023-python/output/__init__.py
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
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)]))
|
||||
51
2023-python/output/day_01.py
Normal file
51
2023-python/output/day_01.py
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
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
|
||||
49
2023-python/output/day_02.py
Normal file
49
2023-python/output/day_02.py
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
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
|
||||
68
2023-python/output/day_03.py
Normal file
68
2023-python/output/day_03.py
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
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
|
||||
47
2023-python/output/day_04.py
Normal file
47
2023-python/output/day_04.py
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
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
|
||||
73
2023-python/output/day_05.py
Normal file
73
2023-python/output/day_05.py
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
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
|
||||
47
2023-python/output/day_06.py
Normal file
47
2023-python/output/day_06.py
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
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
|
||||
94
2023-python/output/day_07.py
Normal file
94
2023-python/output/day_07.py
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
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
|
||||
61
2023-python/output/day_08.py
Normal file
61
2023-python/output/day_08.py
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
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
|
||||
40
2023-python/output/day_09.py
Normal file
40
2023-python/output/day_09.py
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
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
|
||||
94
2023-python/output/day_10.py
Normal file
94
2023-python/output/day_10.py
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
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
|
||||
63
2023-python/output/day_11.py
Normal file
63
2023-python/output/day_11.py
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
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
|
||||
64
2023-python/output/day_12.py
Normal file
64
2023-python/output/day_12.py
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
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
|
||||
56
2023-python/output/day_13.py
Normal file
56
2023-python/output/day_13.py
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
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
|
||||
83
2023-python/output/day_14.py
Normal file
83
2023-python/output/day_14.py
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
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
|
||||
63
2023-python/output/day_15.py
Normal file
63
2023-python/output/day_15.py
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
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
|
||||
86
2023-python/output/day_16.py
Normal file
86
2023-python/output/day_16.py
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
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