Solve 2016:13 p1-2 "A Maze of Twisty Little Cubicles"
BFS baby.
This commit is contained in:
parent
c46c2c5126
commit
8d4af7e6e9
4 changed files with 328 additions and 0 deletions
50
2016-python2/README.md
Normal file
50
2016-python2/README.md
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
# Advent of Code 2016
|
||||
|
||||
Solutions for #aoc2016 in Python 3 (3.12.7).
|
||||
|
||||
## Setup
|
||||
|
||||
Since I want to remember, this is what was used to solve
|
||||
the puzzles.
|
||||
|
||||
- Lenovo Thinkpad x260 laptop with Arch Linux.
|
||||
- Hyprland with gBar.
|
||||
- Editor: Zed.
|
||||
- Terminal: Alacritty.
|
||||
|
||||
## Help scripts
|
||||
|
||||
Display all solved puzzles:
|
||||
|
||||
python aoc.py
|
||||
|
||||
To bootstrap a new puzzle (creates `input/<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 'wlpaste | python -m output.day_XX'
|
||||
ls output/*.py | entr -c -s 'cat tmpfile | python -m output.day_XX'
|
||||
ls output/*.py | entr -c -r python -m output.day_XX
|
||||
|
||||
(requires `entr` and `wl-paste`, Mac users can instead use `pbpaste`. If you
|
||||
prefer X at Linux, use `xclip -selection clipboard -o`).
|
||||
|
||||
To lint files:
|
||||
|
||||
ls output/*.py | entr -r -c flake8 output --ignore=E741,E501,E203
|
||||
125
2016-python2/aoc.py
Normal file
125
2016-python2/aoc.py
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def headline(n, title):
|
||||
"""Print day number and name, followed by a ruler. Used by the answer decorator"""
|
||||
print(f"\n--- Day {n}: {title} ---\n")
|
||||
|
||||
|
||||
year = 2016
|
||||
|
||||
try:
|
||||
_, day_no, *name = sys.argv
|
||||
except ValueError:
|
||||
day_no = None
|
||||
name = None
|
||||
|
||||
print(
|
||||
f"\nAdvent of Code {year}" "\n###################" "\n\nby Anders Englöf Ytterström"
|
||||
)
|
||||
|
||||
Path("./input").mkdir(parents=True, exist_ok=True)
|
||||
Path("./output").mkdir(parents=True, exist_ok=True)
|
||||
|
||||
if day_no and name:
|
||||
name = " ".join(name)
|
||||
padded_no = day_no.zfill(2)
|
||||
print(f"\n- creating output/day_{padded_no}.py")
|
||||
with open("output/day_{}.py".format(padded_no), "w") as s:
|
||||
s.write(
|
||||
f"""
|
||||
from output import answer # , matrix, D, DD, ADJ, ints, mhd, mdbg, vdbg
|
||||
|
||||
n = {day_no}
|
||||
title = "{name}"
|
||||
|
||||
|
||||
@answer(1, "Answer is {{}}")
|
||||
def part_1(presolved):
|
||||
return presolved[0]
|
||||
|
||||
|
||||
@answer(2, "Actually, answer is {{}}")
|
||||
def part_2(presolved):
|
||||
return presolved[1]
|
||||
|
||||
|
||||
def solve(data):
|
||||
return 1, 2
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# use dummy data
|
||||
inp = \"\"\"
|
||||
replace me
|
||||
\"\"\".strip()
|
||||
|
||||
# uncomment to instead use stdin
|
||||
# import sys; inp = sys.stdin.read().strip()
|
||||
|
||||
# uncomment to use AoC provided puzzle input
|
||||
# with open("./input/{padded_no}.txt", "r") as f:
|
||||
# inp = f.read().strip()
|
||||
|
||||
# uncomment to do initial data processing shared by part 1-2
|
||||
inp = solve(inp)
|
||||
|
||||
a = part_1(inp)
|
||||
# b = part_2(inp)
|
||||
|
||||
# uncomment and replace 0 with actual output to refactor code
|
||||
# and ensure nonbreaking changes
|
||||
# assert a == 0
|
||||
# assert b == 0
|
||||
""".strip()
|
||||
+ "\n"
|
||||
)
|
||||
print(
|
||||
f"""
|
||||
Done! start coding.
|
||||
|
||||
Puzzle link:
|
||||
https://adventofcode.com/{year}/day/{day_no}
|
||||
|
||||
Puzzle input (copy and paste to input/{day_no.zfill(2)}.txt):
|
||||
https://adventofcode.com/{year}/day/{day_no}/input
|
||||
"""
|
||||
)
|
||||
exit(0)
|
||||
|
||||
|
||||
stars = 0
|
||||
for i in [str(n).zfill(2) for n in range(1, 26)]:
|
||||
if not day_no or day_no.zfill(2) == i:
|
||||
try:
|
||||
day = __import__(
|
||||
"output.day_{}".format(i),
|
||||
globals(),
|
||||
locals(),
|
||||
["n", "title", "part_1", "part_2"],
|
||||
0,
|
||||
)
|
||||
with open(f"./input/{i}.txt", "r") as f:
|
||||
data = f.read().strip()
|
||||
headline(day.n, day.title)
|
||||
try:
|
||||
data = day.presolve(data)
|
||||
except AttributeError:
|
||||
pass
|
||||
try:
|
||||
data = day.solve(data)
|
||||
except AttributeError:
|
||||
pass
|
||||
if day.part_1(data, decorate=True):
|
||||
stars += 1
|
||||
if day.part_2(data, decorate=True):
|
||||
stars += 1
|
||||
except IOError:
|
||||
pass
|
||||
except ImportError:
|
||||
pass
|
||||
if not day_no:
|
||||
print(f"\nStars: {stars}")
|
||||
print("".join("*" if n < stars else "•" for n in range(50)))
|
||||
print("")
|
||||
98
2016-python2/output/__init__.py
Normal file
98
2016-python2/output/__init__.py
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
import functools
|
||||
import re
|
||||
|
||||
# Directions/Adjacents for 2D matrices, in the order UP, RIGHT, DOWN, LEFT
|
||||
D = [
|
||||
(-1, 0),
|
||||
(0, 1),
|
||||
(1, 0),
|
||||
(0, -1),
|
||||
]
|
||||
|
||||
# Directions for 2D matrices, as a dict with keys U, R, D, L
|
||||
DD = {
|
||||
"U": (-1, 0),
|
||||
"R": (0, 1),
|
||||
"D": (1, 0),
|
||||
"L": (0, -1),
|
||||
}
|
||||
|
||||
# Adjacent relative positions including diagonals for 2D matrices, in the order NW, N, NE, W, E, SW, S, SE
|
||||
ADJ = [
|
||||
(-1, -1),
|
||||
(-1, 0),
|
||||
(1, -1),
|
||||
(0, -1),
|
||||
(0, 1),
|
||||
(1, 1),
|
||||
(1, 0),
|
||||
(1, -1),
|
||||
]
|
||||
|
||||
|
||||
def answer(part_index, fmt_string):
|
||||
"""Decorator to present a solution in a human readable format"""
|
||||
|
||||
def decorator_aoc(func):
|
||||
@functools.wraps(func)
|
||||
def wrapper_aoc(*args, **kwargs):
|
||||
decorate = kwargs.get("decorate", False)
|
||||
if decorate:
|
||||
del kwargs["decorate"]
|
||||
answer = func(*args, **kwargs)
|
||||
if not decorate:
|
||||
print(answer)
|
||||
else:
|
||||
formatted = fmt_string.format(answer)
|
||||
print(f" {part_index}) {formatted}")
|
||||
return answer
|
||||
|
||||
return wrapper_aoc
|
||||
|
||||
return decorator_aoc
|
||||
|
||||
|
||||
def ints(s):
|
||||
"""Extract all integers from a string"""
|
||||
return [int(n) for n in re.findall(r"\d+", s)]
|
||||
|
||||
|
||||
def mhd(a, b):
|
||||
"""Calculates the Manhattan distance between 2 positions in the format (y, x) or (x, y)"""
|
||||
ar, ac = a
|
||||
br, bc = b
|
||||
return abs(ar - br) + abs(ac - bc)
|
||||
|
||||
|
||||
def matrix(d):
|
||||
"""Transform a string into an iterable matrix. Returns the matrix, row count and col count"""
|
||||
m = [tuple(r) for r in d.split()]
|
||||
return m, len(m), len(m[0])
|
||||
|
||||
|
||||
def mdbg(m):
|
||||
"""Print-debug a matrix"""
|
||||
for r in m:
|
||||
print("".join(r))
|
||||
|
||||
|
||||
def vdbg(seen, h, w):
|
||||
"""Print-debug visited positions of a matrix"""
|
||||
for r in range(h):
|
||||
print("".join(["#" if (r, c) in seen else "." for c in range(w)]))
|
||||
|
||||
|
||||
def bfs(S, E=None):
|
||||
"""BFS algorithm, equal weighted nodes"""
|
||||
seen = set()
|
||||
q = [(S, 0)]
|
||||
g = {} # graph, required to be provided at some point
|
||||
while q:
|
||||
m, w = q.pop(0)
|
||||
if m in seen:
|
||||
continue
|
||||
seen.add(m)
|
||||
# investigate here
|
||||
for s in g[m]:
|
||||
q.append((s, w + 1))
|
||||
# return insights
|
||||
55
2016-python2/output/day_13.py
Normal file
55
2016-python2/output/day_13.py
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
from collections import Counter
|
||||
from math import inf
|
||||
|
||||
from output import D
|
||||
|
||||
|
||||
def solve(data, E=(31, 39)):
|
||||
S = (1, 1)
|
||||
p1, p2 = bfs(S, E, int(data), inf, 0)
|
||||
return p1, p2
|
||||
|
||||
|
||||
def bfs(S, E, z, p1, p2):
|
||||
seen = set()
|
||||
q = [(S, 0)]
|
||||
while q:
|
||||
m, w = q.pop(0)
|
||||
if m in seen:
|
||||
continue
|
||||
seen.add(m)
|
||||
|
||||
if m == E:
|
||||
p1 = min(w, p1)
|
||||
if w <= 50:
|
||||
p2 = max(p2, len(seen))
|
||||
|
||||
x, y = m
|
||||
g = [
|
||||
(x, y)
|
||||
for x, y in [(x + c, y + r) for r, c in D]
|
||||
if x >= 0 and y >= 0 and valid(x, y, z)
|
||||
]
|
||||
|
||||
for s in g:
|
||||
q.append((s, w + 1))
|
||||
return p1, p2
|
||||
|
||||
|
||||
def valid(x, y, z):
|
||||
n = (x * x + 3 * x + 2 * x * y + y + y * y) + z
|
||||
s = Counter(f"{n:b}")["1"]
|
||||
return s % 2 == 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
with open("./input/13.txt", "r") as f:
|
||||
inp = f.read().strip()
|
||||
|
||||
t, _ = solve("10", (7, 4))
|
||||
assert t == 11
|
||||
|
||||
p1, p2 = solve(inp)
|
||||
|
||||
print(p1)
|
||||
print(p2)
|
||||
Loading…
Add table
Reference in a new issue