.DS_Store | .DS_Store | ||||
venv/ | venv/ | ||||
.env | .env | ||||
*.js |
include .env | include .env | ||||
FILE = $(shell find . -path "./y????/p??.py" -type f | xargs ls -rt | tail -n 1) | FILE = $(shell find . -path "./y????/p??.py" -type f | xargs ls -rt | tail -n 1) | ||||
DATA = $(shell echo $(FILE) | sed -e s/\.py/\.dat/) | |||||
PYTHONPATH = . | PYTHONPATH = . | ||||
export | export | ||||
main: venv/ $(DATA) | main: venv/ $(DATA) | ||||
@cat $(DATA) | venv/bin/python -u $(FILE) | |||||
venv/bin/python -u toolkit.py $(FILE) | |||||
flake: venv/ | flake: venv/ | ||||
venv/bin/flake8 --exclude=venv/ | venv/bin/flake8 --exclude=venv/ | ||||
$(DATA): | |||||
@echo $(DATA) | venv/bin/python -c "import toolkit; toolkit.get_dat()" $(DATA) | |||||
venv/: requirements.txt | venv/: requirements.txt | ||||
rm -rf venv/ | rm -rf venv/ | ||||
~/.pyenv/versions/3.8.0/bin/python -m venv venv | ~/.pyenv/versions/3.8.0/bin/python -m venv venv | ||||
venv/bin/pip install -r requirements.txt | venv/bin/pip install -r requirements.txt | ||||
touch requirements.txt venv/ | touch requirements.txt venv/ | ||||
# install flake8 git hook | # install flake8 git hook | ||||
echo 'venv/bin/flake8 --exclude=venv/' > .git/hooks/pre-commit | |||||
chmod +x .git/hooks/pre-commit | |||||
# echo 'venv/bin/flake8 --exclude=venv/' > .git/hooks/pre-commit | |||||
# chmod +x .git/hooks/pre-commit |
import builtins | |||||
import collections | import collections | ||||
import hashlib | import hashlib | ||||
import itertools | import itertools | ||||
import importlib | |||||
import math | import math | ||||
import os | import os | ||||
import re | import re | ||||
import string | |||||
import sys | import sys | ||||
from pathlib import Path | from pathlib import Path | ||||
return path[::-1] | return path[::-1] | ||||
def get_dat(): | |||||
path = sys.argv[1] | |||||
year, qn = map(int, re.findall(r'\d+', sys.argv[1])) | |||||
url = f'https://adventofcode.com/{year}/day/{qn}/input' | |||||
cookies = {'session': os.environ['SESSION']} | |||||
response = requests.get(url, cookies=cookies) | |||||
response.raise_for_status() | |||||
Path(path).write_bytes(response.content) | |||||
def ensure_data(path): | |||||
if not path.exists(): | |||||
year, qn = map(int, re.findall(r'\d+', sys.argv[1])) | |||||
url = f'https://adventofcode.com/{year}/day/{qn}/input' | |||||
cookies = {'session': os.environ['SESSION']} | |||||
response = requests.get(url, cookies=cookies) | |||||
response.raise_for_status() | |||||
path.write_bytes(response.content) | |||||
def batch(iterable, size): | def batch(iterable, size): | ||||
count += 1 | count += 1 | ||||
else: | else: | ||||
raise RuntimeError('Reached steady state') | raise RuntimeError('Reached steady state') | ||||
if __name__ == '__main__': | |||||
data_file = Path(sys.argv[1]).with_suffix('.dat') | |||||
ensure_data(data_file) | |||||
builtins.data_file = data_file | |||||
builtins.string = string | |||||
builtins.re = re | |||||
rel = re.sub(r'.+(y\d+)/(p\d+).+', r'\1.\2', os.environ['FILE']) | |||||
mod = importlib.import_module(rel) | |||||
print(getattr(mod, 'ans1', None)) | |||||
print(getattr(mod, 'ans2', None)) |
import sys | |||||
ans1 = 0 | ans1 = 0 | ||||
ans2 = None | ans2 = None | ||||
for i, char in enumerate(sys.stdin.read(), 1): | |||||
for i, char in enumerate(data_file.read_text(), 1): | |||||
ans1 += {'(': 1, ')': -1}[char] | ans1 += {'(': 1, ')': -1}[char] | ||||
if ans1 == -1 and ans2 is None: | if ans1 == -1 and ans2 is None: | ||||
ans2 = i | ans2 = i | ||||
print(ans1) | |||||
print(ans2) |
import sys | |||||
ans1 = 0 | ans1 = 0 | ||||
ans2 = 0 | ans2 = 0 | ||||
for line in sys.stdin.readlines(): | |||||
for line in data_file.read_text().splitlines(): | |||||
a, b, c = sorted(map(int, line.split('x'))) | a, b, c = sorted(map(int, line.split('x'))) | ||||
ans1 += 2 * (a * b + b * c + a * c) + a * b | ans1 += 2 * (a * b + b * c + a * c) + a * b | ||||
ans2 += a * b * c + 2 * (a + b) | ans2 += a * b * c + 2 * (a + b) | ||||
print(ans1) | |||||
print(ans2) |
import sys | |||||
from itertools import accumulate as acc | from itertools import accumulate as acc | ||||
text = sys.stdin.read() | |||||
text = data_file.read_text() | |||||
dirs = dict(zip('<>^v', [-1, 1, -1j, 1j])) | dirs = dict(zip('<>^v', [-1, 1, -1j, 1j])) | ||||
ans1 = len({*acc(map(dirs.get, text))}) | ans1 = len({*acc(map(dirs.get, text))}) | ||||
print(ans1) | |||||
ans2 = len({*acc(map(dirs.get, text[::2])), *acc(map(dirs.get, text[1::2]))}) | ans2 = len({*acc(map(dirs.get, text[::2])), *acc(map(dirs.get, text[1::2]))}) | ||||
print(ans2) |
import sys | |||||
from hashlib import md5 | from hashlib import md5 | ||||
from multiprocessing import Pool | from multiprocessing import Pool | ||||
return i, hasher.hexdigest() | return i, hasher.hexdigest() | ||||
if __name__ == '__main__': | |||||
code = sys.stdin.read().strip() | |||||
ans1 = None | |||||
ans2 = None | |||||
with Pool() as pool: | |||||
for i, coin in pool.starmap(mine, [(code, i) for i in range(10**7)]): | |||||
if ans1 is None and coin.startswith('00000'): | |||||
ans1 = i | |||||
if ans2 is None and coin.startswith('000000'): | |||||
ans2 = i | |||||
print(ans1) | |||||
print(ans2) | |||||
code = data_file.read_text().strip() | |||||
ans1 = None | |||||
ans2 = None | |||||
for i in range(10**7): | |||||
i, coin = mine(code, i) | |||||
if ans1 is None and coin.startswith('00000'): | |||||
ans1 = i | |||||
if ans2 is None and coin.startswith('000000'): | |||||
ans2 = i | |||||
break |
import re | |||||
import sys | |||||
def checks1(string): | def checks1(string): | ||||
yield len(re.findall(r'([aeiou])', string)) >= 3 | yield len(re.findall(r'([aeiou])', string)) >= 3 | ||||
yield len(re.findall(r'(.)\1', string)) | yield len(re.findall(r'(.)\1', string)) | ||||
ans1 = 0 | ans1 = 0 | ||||
ans2 = 0 | ans2 = 0 | ||||
for line in sys.stdin.read().splitlines(): | |||||
for line in data_file.read_text().splitlines(): | |||||
ans1 += all(checks1(line)) | ans1 += all(checks1(line)) | ||||
ans2 += all(checks2(line)) | ans2 += all(checks2(line)) | ||||
print(ans1) | |||||
print(ans2) |
def pythonize(string): | |||||
op, out = ( | |||||
re.sub(r'([a-z]+)', r'\1_', string) | |||||
.replace('AND', '&') | |||||
.replace('OR', '|') | |||||
.replace('NOT ', '~') | |||||
.replace('RSHIFT', '>>') | |||||
.replace('LSHIFT', '<<') | |||||
).split(' -> ') | |||||
return f'{out} = {op}' | |||||
def process(instructions, registers={}): | |||||
while instructions: | |||||
curr, *instructions = instructions | |||||
out = curr.split()[0] | |||||
if out in registers: | |||||
continue | |||||
try: | |||||
exec(curr, None, registers) | |||||
except NameError: | |||||
instructions.append(curr) | |||||
return registers | |||||
inp = [pythonize(string) for string in data_file.read_text().splitlines()] | |||||
found = process(inp.copy()) | |||||
ans1 = found['a_'] | |||||
ans2 = process(inp.copy(), {'b_': ans1})['a_'] |
import sys | |||||
from itertools import permutations | |||||
text = sys.stdin.read() | |||||
# transform text into data structures | |||||
valid = set() | |||||
target = {} | |||||
for y, line in enumerate(text.splitlines()): | |||||
for x, char in enumerate(line): | |||||
if char == '#': | |||||
continue | |||||
valid.add(x + 1j * y) | |||||
if char != '.': | |||||
target[x + 1j * y] = char | |||||
# generate travel map | |||||
graph = {} | |||||
for A in target: | |||||
seen = {A} | |||||
boundary = {A} | |||||
pending = set(target) - seen | |||||
N = 0 | |||||
while pending: | |||||
N += 1 | |||||
boundary = {pos + step for pos in boundary for step in [1, -1, 1j, -1j]} | |||||
boundary &= valid - seen | |||||
seen.update(boundary) | |||||
for B in boundary & pending: | |||||
pending -= {B} | |||||
graph[target[A], target[B]] = N | |||||
# use map to determine routes | |||||
calc = lambda combo: sum(graph[pair] for pair in zip(combo, combo[1:])) | |||||
z = '0' | |||||
rest = set(target.values()) - {z} | |||||
options = [(z,) + combo for combo in permutations(rest)] | |||||
ans = min(options, key=calc) | |||||
print(calc(ans)) | |||||
options = [(z,) + combo + (z,) for combo in permutations(rest)] | |||||
ans = min(options, key=calc) | |||||
print(calc(ans)) |
inp = data_file.read_text().strip() | |||||
out = {} | |||||
for a in [''] + list(string.ascii_lowercase): | |||||
text = inp.replace(a, '').replace(a.swapcase(), '') | |||||
stack1 = list(text) | |||||
stack2 = [] | |||||
while stack1: | |||||
x = stack1.pop() | |||||
if stack2 and x.swapcase() == stack2[-1]: | |||||
y = stack2.pop() | |||||
else: | |||||
stack2.append(x) | |||||
out[a] = len(stack2) | |||||
ans1 = out[''] | |||||
ans2 = min(out.values()) |
import collections | |||||
import itertools | |||||
import re | |||||
import sys | |||||
txt = '''90342 ;2 correct | |||||
70794 ;0 correct | |||||
39458 ;2 correct | |||||
34109 ;1 correct | |||||
51545 ;2 correct | |||||
12531 ;1 correct | |||||
''' # 39542 is unique | |||||
txt = sys.stdin.read() | |||||
exclusions = collections.defaultdict(set) | |||||
clues = [] | |||||
for string, n in re.findall(r'(\d+) ;(\d)', txt): | |||||
choices = set(enumerate(string)) | |||||
combos = [frozenset(cs) for cs in itertools.combinations(choices, int(n))] | |||||
for combo in combos: | |||||
exclusions[combo] |= (choices - combo) | |||||
if int(n): | |||||
clues.append(combos) | |||||
for pos in range(len(string)): | |||||
choices = {(pos, char) for char in '0123456789'} | |||||
for choice in choices: | |||||
exclusions[frozenset({choice})] |= (choices - {choice}) | |||||
def solve(clues_left, known=frozenset()): | |||||
if not clues_left: | |||||
avail = {a for gr in exclusions for a in gr} | |||||
avail -= {v for k, vs in exclusions.items() if k <= known for v in vs} | |||||
yield ''.join(c for _, c in sorted(avail)) | |||||
else: | |||||
choices = min(clues_left, key=len) | |||||
for choice in choices: | |||||
chosen = known | choice | |||||
bads = {v for k, vs in exclusions.items() if k <= chosen for v in vs} | |||||
mods = [[c for c in cl if not c & bads] for cl in clues_left if cl != choices] | |||||
yield from solve(mods, chosen) | |||||
solution = next(solve(clues)) | |||||
print(solution) |