Move everything to a 2017 folder
This commit is contained in:
parent
32d7dded75
commit
fc87233242
15 changed files with 94 additions and 4017 deletions
27
2017/day1.py
Normal file
27
2017/day1.py
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
def part1(digits):
|
||||
return sum(int(d) for i, d in enumerate(digits) if d == digits[(i+1) % len(digits)])
|
||||
|
||||
for inp, out in [
|
||||
('1122', 3),
|
||||
('1111', 4),
|
||||
('1234', 0),
|
||||
('91212129', 9),
|
||||
]:
|
||||
assert part1(inp) == out, f'Expected {out}, got {part1(inp)}'
|
||||
|
||||
def part2(digits):
|
||||
return sum(int(d) for i, d in enumerate(digits) if d == digits[(i+len(digits)//2) % len(digits)])
|
||||
|
||||
for inp, out in [
|
||||
('1212', 6),
|
||||
('1221', 0),
|
||||
('123425', 4),
|
||||
('123123', 12),
|
||||
('12131415', 4),
|
||||
]:
|
||||
assert part2(inp) == out, f'Expected {out}, got {part2(inp)}'
|
||||
|
||||
puzzle_input = 'TODO'
|
||||
|
||||
print(part1(puzzle_input))
|
||||
print(part2(puzzle_input))
|
||||
37
2017/day2.py
Normal file
37
2017/day2.py
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
def parse(text):
|
||||
return [[int(num) for num in line.split('\t')] for line in text.splitlines()]
|
||||
|
||||
with open('input/day2') as inp:
|
||||
puzzle_input = parse(inp.read())
|
||||
|
||||
def part1(sheet):
|
||||
return sum(max(row) - min(row) for row in sheet)
|
||||
|
||||
assert part1([
|
||||
[5, 1, 9, 5],
|
||||
[7, 5, 3],
|
||||
[2, 4, 6, 8],
|
||||
]) == 18
|
||||
|
||||
print(part1(puzzle_input))
|
||||
|
||||
from itertools import combinations
|
||||
|
||||
def part2(sheet):
|
||||
s = 0
|
||||
for row in sheet:
|
||||
for n, m in combinations(row, 2):
|
||||
lo = min(n, m)
|
||||
hi = max(n, m)
|
||||
if hi % lo == 0:
|
||||
s += hi // lo
|
||||
break
|
||||
return s
|
||||
|
||||
assert part2([
|
||||
[5, 9, 2, 8],
|
||||
[9, 4, 7, 3],
|
||||
[3, 8, 6, 5],
|
||||
]) == 9
|
||||
|
||||
print(part2(puzzle_input))
|
||||
94
2017/day3.py
Normal file
94
2017/day3.py
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
puzzle_input = 0 # TODO
|
||||
|
||||
def cardinals(distance):
|
||||
d = 0
|
||||
distance[1] = d
|
||||
yield 1
|
||||
|
||||
d += 1
|
||||
distance[2] = d
|
||||
yield 2
|
||||
|
||||
while True:
|
||||
v = (2*d)**2 - (d-1)
|
||||
distance[v] = d
|
||||
yield v
|
||||
|
||||
v = (2*d)**2 + (d+1)
|
||||
distance[v] = d
|
||||
yield v
|
||||
|
||||
v = (2*d + 1)**2 - d
|
||||
distance[v] = d
|
||||
yield v
|
||||
|
||||
v = (2*d + 1)**2 + (d+1)
|
||||
distance[v] = d + 1
|
||||
yield v
|
||||
|
||||
d += 1
|
||||
|
||||
def closest_cardinals(n, distance):
|
||||
cs = cardinals(distance)
|
||||
lo = next(cs)
|
||||
hi = next(cs)
|
||||
while not lo <= n <= hi:
|
||||
lo, hi = hi, next(cs)
|
||||
return lo, hi
|
||||
|
||||
def manhattan_to_center(n):
|
||||
distance = {}
|
||||
best = sorted(closest_cardinals(n, distance), key=lambda x: abs(n - x))[0]
|
||||
return distance[best] + abs(n - best)
|
||||
|
||||
for inp, expected in [
|
||||
(1, 0),
|
||||
(2, 1),
|
||||
(12, 3),
|
||||
(23, 2),
|
||||
(1024, 31),
|
||||
]:
|
||||
actual = manhattan_to_center(inp)
|
||||
assert actual == expected, f'Expected {expected} got {actual}'
|
||||
|
||||
print(manhattan_to_center(puzzle_input))
|
||||
|
||||
def walking_order():
|
||||
start = (0, 0)
|
||||
R, D, L, U = [(+1, 0), (0, -1), (-1, 0), (0, +1)]
|
||||
|
||||
yield R
|
||||
yield U
|
||||
|
||||
count = 2
|
||||
while True:
|
||||
for deltas in [(L, D), (R, U)]:
|
||||
for delta in deltas:
|
||||
for _ in range(count):
|
||||
yield delta
|
||||
count += 1
|
||||
|
||||
def neighbors(cell):
|
||||
x, y = cell
|
||||
for dx in range(-1, 2):
|
||||
for dy in range(-1, 2):
|
||||
if dx == dy == 0: continue
|
||||
yield (x+dx, y+dy)
|
||||
|
||||
def sum_neighbors_until(limit):
|
||||
grid = {}
|
||||
deltas = walking_order()
|
||||
|
||||
cell = (0, 0)
|
||||
grid[cell] = 1
|
||||
|
||||
while True:
|
||||
x, y = cell
|
||||
dx, dy = next(deltas)
|
||||
cell = (x+dx, y+dy)
|
||||
value = sum(grid.get(n, 0) for n in neighbors(cell))
|
||||
grid[cell] = value
|
||||
if value > limit:
|
||||
return value
|
||||
|
||||
print(sum_neighbors_until(puzzle_input))
|
||||
40
2017/day4.py
Normal file
40
2017/day4.py
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
with open('input/day4') as f:
|
||||
puzzle_input = f.read().splitlines()
|
||||
|
||||
def is_valid(passphrase):
|
||||
words = passphrase.split()
|
||||
return len(words) == len(set(words))
|
||||
|
||||
def validity_count(passphrases):
|
||||
return len({p for p in passphrases if is_valid(p)})
|
||||
|
||||
class Counter:
|
||||
def __init__(self, iterable):
|
||||
self._counts = {}
|
||||
for i in iterable:
|
||||
self._counts[i] = self._counts.get(i, 0) + 1
|
||||
|
||||
def __eq__(self, other):
|
||||
if self._counts.keys() != other._counts.keys():
|
||||
return False
|
||||
for k in self._counts.keys():
|
||||
if self._counts[k] != other._counts[k]:
|
||||
return False
|
||||
return True
|
||||
|
||||
def are_anagrams(w1, w2):
|
||||
return Counter(w1) == Counter(w2)
|
||||
|
||||
def pairs(l):
|
||||
for i, e1 in enumerate(l):
|
||||
for e2 in l[i+1:]:
|
||||
yield (e1, e2)
|
||||
|
||||
def is_valid2(passphrase):
|
||||
return all(not are_anagrams(w1, w2) for w1, w2 in pairs(passphrase.split()))
|
||||
|
||||
def validity_count2(passphrases):
|
||||
return len({p for p in passphrases if is_valid2(p)})
|
||||
|
||||
print(validity_count(puzzle_input))
|
||||
print(validity_count2(puzzle_input))
|
||||
24
2017/day5.py
Normal file
24
2017/day5.py
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
with open('input/day5') as f:
|
||||
puzzle_input = tuple(int(line) for line in f.read().splitlines())
|
||||
|
||||
def step(jumps, index):
|
||||
jump = jumps[index]
|
||||
return (jumps[:index] + (offset(jump),) + jumps[index+1:]), index + jump
|
||||
|
||||
def escape(jumps):
|
||||
index = 0
|
||||
steps = 0
|
||||
while 0 <= index < len(jumps):
|
||||
jumps, index = step(jumps, index)
|
||||
steps += 1
|
||||
return steps
|
||||
|
||||
def offset(jump):
|
||||
return jump+1
|
||||
|
||||
# print(escape(puzzle_input))
|
||||
|
||||
def offset(jump):
|
||||
return jump + (-1)**(jump >= 3)
|
||||
|
||||
print(escape(puzzle_input))
|
||||
39
2017/day6.py
Normal file
39
2017/day6.py
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
with open('input/day6') as f:
|
||||
puzzle_input = tuple(map(int, f.read().split()))
|
||||
|
||||
def cycle(banks):
|
||||
banks = list(banks)
|
||||
idx, blocks = max(enumerate(banks), key=lambda x: x[1])
|
||||
banks[idx] = 0
|
||||
idx = (idx+1) % len(banks)
|
||||
while blocks:
|
||||
banks[idx] += 1
|
||||
blocks -= 1
|
||||
idx = (idx+1) % len(banks)
|
||||
return tuple(banks)
|
||||
|
||||
def reallocate(banks):
|
||||
seen = set()
|
||||
seen.add(banks)
|
||||
count = 0
|
||||
while True:
|
||||
banks = cycle(banks)
|
||||
count += 1
|
||||
if banks in seen:
|
||||
return count
|
||||
seen.add(banks)
|
||||
|
||||
print(reallocate(puzzle_input))
|
||||
|
||||
def reallocate_loop(banks):
|
||||
seen = {}
|
||||
time = 0
|
||||
seen[banks] = time
|
||||
while True:
|
||||
banks = cycle(banks)
|
||||
time += 1
|
||||
if banks in seen:
|
||||
return time - seen[banks]
|
||||
seen[banks] = time
|
||||
|
||||
print(reallocate_loop(puzzle_input))
|
||||
71
2017/day7.py
Normal file
71
2017/day7.py
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
from collections import namedtuple
|
||||
|
||||
Node = namedtuple('Node', ['name', 'disc', 'children'])
|
||||
|
||||
with open('input/day7') as f:
|
||||
puzzle_input = []
|
||||
for line in f.read().splitlines():
|
||||
children = []
|
||||
if '->' in line:
|
||||
line, rest = line.split('-> ')
|
||||
children = rest.split(', ')
|
||||
name, weight = line.split()
|
||||
weight = int(weight[1:-1])
|
||||
|
||||
puzzle_input.append(Node(name, weight, tuple(children)))
|
||||
|
||||
def find_root(nodes):
|
||||
names = {node.name for node in nodes}
|
||||
supported = {child for node in nodes for child in node.children}
|
||||
return (names - supported).pop()
|
||||
|
||||
print(find_root(puzzle_input))
|
||||
|
||||
def memoized(f):
|
||||
cache = {}
|
||||
def _(*args):
|
||||
args = tuple(args)
|
||||
if args in cache:
|
||||
return cache[args]
|
||||
val = f(*args)
|
||||
cache[args] = val
|
||||
return val
|
||||
return _
|
||||
|
||||
def identical(iterable):
|
||||
return not iterable or len(set(iterable)) == 1
|
||||
|
||||
def counts(iterable):
|
||||
count = {}
|
||||
for i in iterable:
|
||||
count[i] = count.get(i, 0) + 1
|
||||
return count
|
||||
|
||||
def unbalanced(nodes):
|
||||
by_name = {node.name: node for node in nodes}
|
||||
root = by_name[find_root(nodes)]
|
||||
|
||||
@memoized
|
||||
def weight(n):
|
||||
return n.disc + sum(weight(by_name[c]) for c in n.children)
|
||||
|
||||
@memoized
|
||||
def is_balanced(n):
|
||||
return identical(weight(by_name[c]) for c in n.children)
|
||||
|
||||
def walk(tree):
|
||||
if not is_balanced(tree):
|
||||
weights = [weight(by_name[c]) for c in tree.children]
|
||||
yield tree.name
|
||||
for n in (by_name[c] for c in tree.children):
|
||||
yield from walk(n)
|
||||
|
||||
unbalanced = list(walk(root))[-1]
|
||||
children = [by_name[c] for c in by_name[unbalanced].children]
|
||||
weights = [weight(c) for c in children]
|
||||
weight_counts = counts(weights)
|
||||
odd_one = min(children, key=lambda n: weight_counts[weight(n)])
|
||||
majority = max(set(weights), key=lambda w: weight_counts[w])
|
||||
return majority - sum(weight(by_name[c]) for c in odd_one.children)
|
||||
|
||||
print(unbalanced(puzzle_input))
|
||||
54
2017/day8.py
Normal file
54
2017/day8.py
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
from collections import defaultdict
|
||||
|
||||
registers = None
|
||||
|
||||
comparison = {
|
||||
'==': lambda x, y : registers[x] == y,
|
||||
'!=': lambda x, y : registers[x] != y,
|
||||
'>=': lambda x, y : registers[x] >= y,
|
||||
'<=': lambda x, y : registers[x] <= y,
|
||||
'<': lambda x, y : registers[x] < y,
|
||||
'>': lambda x, y : registers[x] > y,
|
||||
}
|
||||
|
||||
def inc(x, y):
|
||||
registers[x] += y
|
||||
|
||||
def dec(x, y):
|
||||
registers[x] -= y
|
||||
|
||||
operation = {
|
||||
'inc': inc,
|
||||
'dec': dec,
|
||||
}
|
||||
|
||||
class Instruction:
|
||||
def __init__(self, line):
|
||||
self.reg, self.op, self.delta, _, self.flag, self.comp, self.val = line.split()
|
||||
|
||||
def evaluate(self):
|
||||
if comparison[self.comp](self.flag, int(self.val)):
|
||||
operation[self.op](self.reg, int(self.delta))
|
||||
|
||||
|
||||
with open('input/day8') as f:
|
||||
puzzle_input = [Instruction(line) for line in f.read().splitlines()]
|
||||
|
||||
def largest(instructions):
|
||||
global registers
|
||||
registers = defaultdict(int)
|
||||
for i in instructions:
|
||||
i.evaluate()
|
||||
return max(registers.values())
|
||||
|
||||
def highest(instructions):
|
||||
global registers
|
||||
registers = defaultdict(int)
|
||||
high = float('-inf')
|
||||
for i in instructions:
|
||||
i.evaluate()
|
||||
high = max(high, max(registers.values()))
|
||||
return high
|
||||
|
||||
print(largest(puzzle_input))
|
||||
print(highest(puzzle_input))
|
||||
94
2017/day9.py
Normal file
94
2017/day9.py
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
import enum
|
||||
|
||||
@enum.unique
|
||||
class Token(enum.Enum):
|
||||
GroupStart = '{'
|
||||
GroupEnd = '}'
|
||||
GarbageStart = '<'
|
||||
GarbageEnd = '>'
|
||||
Cancel = '!'
|
||||
|
||||
def log(f):
|
||||
def _(*args):
|
||||
ret = f(*args)
|
||||
print(*args, '->', ret)
|
||||
return ret
|
||||
return _
|
||||
|
||||
def tokenize(stream):
|
||||
tokens = {t.value: t for t in Token}
|
||||
return [tokens[c] for c in stream if c in tokens]
|
||||
|
||||
def cancel(tokens):
|
||||
stack = []
|
||||
i = 0
|
||||
cancellations = 0
|
||||
while i < len(tokens):
|
||||
t = tokens[i]
|
||||
if t == Token.Cancel:
|
||||
cancellations += 1
|
||||
i += 2
|
||||
continue
|
||||
stack.append(t)
|
||||
i += 1
|
||||
|
||||
assert len(tokens) == len(stack) + 2*cancellations
|
||||
|
||||
return stack
|
||||
|
||||
def clean(tokens):
|
||||
stack = []
|
||||
i = 0
|
||||
while i < len(tokens):
|
||||
if tokens[i] == Token.GarbageStart:
|
||||
while tokens[i] != Token.GarbageEnd:
|
||||
i += 1
|
||||
i += 1
|
||||
continue
|
||||
print('PUSH:', i, tokens[i])
|
||||
stack.append(tokens[i])
|
||||
i += 1
|
||||
return stack
|
||||
|
||||
class Group:
|
||||
def __init__(self):
|
||||
self.children = []
|
||||
|
||||
def append(self, child):
|
||||
self.children.append(child)
|
||||
|
||||
def __repr__(self):
|
||||
return '{{}}'.format(''.join(repr(c) for c in self.children))
|
||||
|
||||
__str__ = __repr__
|
||||
|
||||
def parse(tokens):
|
||||
stack = []
|
||||
outer = Group()
|
||||
last = None
|
||||
tokens = list(clean(cancel(tokens)))
|
||||
print(len([t for t in tokens if t == Token.GroupStart]), len([t for t in tokens if t == Token.GroupEnd]))
|
||||
assert len([t for t in tokens if t == Token.GroupStart]) == len([t for t in tokens if t == Token.GroupEnd])
|
||||
for t in tokens:
|
||||
if t == Token.GroupStart:
|
||||
g = Group()
|
||||
if stack:
|
||||
stack[-1].append(g)
|
||||
stack.append(g)
|
||||
elif t == Token.GroupEnd:
|
||||
last = stack.pop(-1)
|
||||
return last
|
||||
|
||||
def score(group, depth=1):
|
||||
return depth + sum(score(c, depth+1) for c in group.children)
|
||||
|
||||
def solve(stream):
|
||||
stuff = parse(tokenize(stream))
|
||||
if stuff is None: return 0
|
||||
return score(stuff)
|
||||
|
||||
with open('input/day9') as f:
|
||||
puzzle_input = f.read()
|
||||
|
||||
# print(solve('<{o"i!a,<{i<a>'))
|
||||
print(solve(puzzle_input))
|
||||
Loading…
Reference in a new issue