Watch
1
0
Fork
You've already forked advent-of-code
0

Move everything to a 2017 folder

This commit is contained in:
Jeremy Kaplan 2018-12-01 09:31:24 -08:00
commit fc87233242
15 changed files with 94 additions and 4017 deletions

27
2017/day1.py Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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))