| @@ -1,9 +1,11 @@ | |||
| import builtins | |||
| import collections | |||
| import functools | |||
| import hashlib | |||
| import itertools | |||
| import importlib | |||
| import itertools | |||
| import math | |||
| import operator | |||
| import os | |||
| import re | |||
| import string | |||
| @@ -13,8 +15,7 @@ from pathlib import Path | |||
| import requests | |||
| def integers(line): | |||
| return [int(n) for n in re.findall(r'\d+', line)] | |||
| product = functools.partial(functools.reduce, operator.mul) | |||
| def lcm(a, b): | |||
| @@ -106,6 +107,7 @@ if __name__ == '__main__': | |||
| data_file = Path(sys.argv[1]).with_suffix('.dat') | |||
| ensure_data(data_file) | |||
| builtins.df = data_file | |||
| builtins.text = data_file.read_text() | |||
| builtins.string = string | |||
| builtins.re = re | |||
| rel = re.sub(r'.+(y\d+)/(p\d+).+', r'\1.\2', os.environ['FILE']) | |||
| @@ -0,0 +1,19 @@ | |||
| import json | |||
| def recurse(data, avoid=None): | |||
| type_ = type(data) | |||
| if type_ is int: | |||
| return data | |||
| elif type_ is str: | |||
| return 0 | |||
| elif type_ is list: | |||
| return sum(recurse(el, avoid) for el in data) | |||
| elif type_ is dict: | |||
| if any(val == avoid for val in data.values()): return 0 | |||
| return sum(recurse(value, avoid) for key, value in data.items()) | |||
| data = json.loads(df.read_text()) | |||
| ans1 = recurse(data) | |||
| ans2 = recurse(data, 'red') | |||
| @@ -0,0 +1,20 @@ | |||
| import collections | |||
| import itertools | |||
| happiness = collections.defaultdict(int) | |||
| people = set() | |||
| for line in df.read_text().splitlines(): | |||
| A, verb, N, B = re.findall(r'([A-Z][a-z]+|gain|lose|\d+)', line) | |||
| happiness[A, B] = int(N) if verb == 'gain' else -int(N) | |||
| people.update({A, B}) | |||
| def calc(seq): | |||
| return sum(happiness[b, a] + happiness[b, c] | |||
| for a, b, c in zip(seq[-1:] + seq[:-1], seq, seq[1:] + seq[:1]) | |||
| ) | |||
| ans1 = max(calc(combo) for combo in itertools.permutations(people)) | |||
| ans2 = max(calc(combo) for combo in itertools.permutations(people | {'me'})) | |||
| @@ -0,0 +1,16 @@ | |||
| import collections | |||
| import itertools | |||
| reindeers = [ | |||
| itertools.accumulate(itertools.cycle([speed] * act + [0] * rest)) | |||
| for line in df.read_text().splitlines() | |||
| for speed, act, rest in [map(int, re.findall(r'\d+', line))] | |||
| ] | |||
| traveled = [[next(rr) for _ in range(2503)] for rr in reindeers] | |||
| transpose = list(zip(*traveled)) | |||
| ans1 = max(transpose[-1]) | |||
| points = [[d == max(dsts) for d in dsts] for dsts in transpose] | |||
| ans2 = max(sum(pts) for pts in zip(*points)) | |||
| @@ -0,0 +1,19 @@ | |||
| from toolkit import product | |||
| def combine(qtys): | |||
| return [max(0, sum(a * b for a, b in zip(qtys, vs))) for vs in zip(*data)] | |||
| data = [ | |||
| [int(n) for n in re.findall(r'-?\d+', ln)] | |||
| for ln in df.read_text().splitlines() | |||
| ] | |||
| qtys = [ | |||
| combine([A, B, C, 100 - A - B - C]) | |||
| for A in range(101) | |||
| for B in range(101 - A) | |||
| for C in range(101 - A - B) | |||
| ] | |||
| ans1 = max(product(combo[:-1]) for combo in qtys) | |||
| ans2 = max(product(combo[:-1]) for combo in qtys if combo[-1] == 500) | |||
| @@ -0,0 +1,24 @@ | |||
| known = { | |||
| 'children': 3, | |||
| 'cats': 7, | |||
| 'samoyeds': 2, | |||
| 'pomeranians': 3, | |||
| 'akitas': 0, | |||
| 'vizslas': 0, | |||
| 'goldfish': 5, | |||
| 'trees': 3, | |||
| 'cars': 2, | |||
| 'perfumes': 1, | |||
| } | |||
| for line in df.read_text().splitlines(): | |||
| sue, N, *rest = re.findall(r'\w+', line) | |||
| stuff = {k: int(v) for k, v in zip(rest[::2], rest[1::2])} | |||
| if all(known[k] == stuff[k] for k in stuff): | |||
| ans1 = N | |||
| if all( | |||
| known[k] < stuff[k] if k in {'cats', 'trees'} | |||
| else known[k] > stuff[k] if k in {'pomeranians', 'goldfish'} | |||
| else known[k] == stuff[k] | |||
| for k in stuff): | |||
| ans2 = N | |||
| @@ -0,0 +1,14 @@ | |||
| def recurse(pending, opts, chain=tuple()): | |||
| if pending == 0: | |||
| yield chain | |||
| else: | |||
| for i, opt in enumerate(opts): | |||
| yield from recurse(pending - opt, opts[i + 1:], chain + (opt,)) | |||
| ns = sorted([int(n) for n in df.read_text().splitlines()], reverse=True) | |||
| opts = list(recurse(150, ns)) | |||
| ans1 = len(opts) | |||
| min_n = min(map(len, opts)) | |||
| ans2 = sum(len(opt) == min_n for opt in opts) | |||
| @@ -0,0 +1,24 @@ | |||
| def condition(x, y, on): | |||
| is_on = (x, y) in on | |||
| neighbours_on = sum( | |||
| (x + dx, y + dy) in on | |||
| for dx in [1, 0, -1] for dy in [1, 0, -1] | |||
| if dx or dy | |||
| ) | |||
| return (is_on and neighbours_on in {2, 3}) or neighbours_on == 3 | |||
| on1 = { | |||
| (x, y) | |||
| for y, ln in enumerate(df.read_text().splitlines()) | |||
| for x, ch in enumerate(ln) | |||
| if ch == '#' | |||
| } | |||
| on2 = set(on1) | |||
| grid = {(x, y) for x in range(100) for y in range(100)} | |||
| corners = {(0, 0), (0, 99), (99, 0), (99, 99)} | |||
| for _ in range(100): | |||
| on1 = {(x, y) for x, y in grid if condition(x, y, on1)} | |||
| on2 = {(x, y) for x, y in grid if condition(x, y, on2)} | corners | |||
| ans1 = len(on1) | |||
| ans2 = len(on2) | |||
| @@ -0,0 +1,19 @@ | |||
| pre, seq = text.strip().split('\n\n') | |||
| formulas = re.findall(r'(\w+) => (\w+)', pre) | |||
| ans1 = len({ | |||
| seq[:match.start()] + seq[match.start():].replace(key, val, 1) | |||
| for key, val in formulas | |||
| for match in re.finditer(key, seq) | |||
| }) | |||
| ops = formulas | |||
| ans2 = 0 | |||
| while seq != 'e': | |||
| ans2 += 1 | |||
| for k, v in ops: | |||
| if v in seq: | |||
| seq = seq.replace(v, k, 1) | |||
| break | |||
| @@ -0,0 +1,50 @@ | |||
| import itertools | |||
| def is_win(p_hp, p_atk, p_def, b_hp, b_atk, b_def): | |||
| while True: | |||
| b_hp -= max(1, p_atk - b_def) | |||
| if b_hp <= 0: | |||
| return True | |||
| p_hp -= max(1, b_atk - p_def) | |||
| if p_hp <= 0: | |||
| return False | |||
| weapons = [ | |||
| (8, 4, 0), | |||
| (10, 5, 0), | |||
| (25, 6, 0), | |||
| (40, 7, 0), | |||
| (74, 8, 0), | |||
| ] | |||
| armors = [ | |||
| (0, 0, 0), | |||
| (13, 0, 1), | |||
| (31, 0, 2), | |||
| (53, 0, 3), | |||
| (75, 0, 4), | |||
| (102, 0, 5), | |||
| ] | |||
| rings = [ | |||
| (0, 0, 0), | |||
| (0, 0, 0), | |||
| (25, 1, 0), | |||
| (50, 2, 0), | |||
| (100, 3, 0), | |||
| (20, 0, 1), | |||
| (40, 0, 2), | |||
| (80, 0, 3), | |||
| ] | |||
| opts = [ | |||
| [sum(vs) for vs in zip(*[w, a, r1, r2])] | |||
| for w, a in itertools.product(weapons, armors) | |||
| for r1, r2 in itertools.combinations(rings, 2) | |||
| ] | |||
| p_hp = 100 | |||
| b_hp, b_atk, b_def = map(int, re.findall(r'(\d+)', text)) | |||
| wins = {True: set(), False: set()} | |||
| for cost, p_atk, p_def in opts: | |||
| wins[is_win(p_hp, p_atk, p_def, b_hp, b_atk, b_def)].add(cost) | |||
| ans1 = min(wins[True]) | |||
| ans2 = max(wins[False]) | |||