def parse(data): for batch in data.split('\n\n'): passport = {} for pair in batch.split(): key, value = pair.split(':') passport[key] = value yield passport def has_required_fields(passport): return passport.keys() >= required_fields def num_valid_passports(passports): return sum(has_required_fields(p) for p in passports) def num_valid_passports_strict(passports): num = 0 for passport in passports: if has_required_fields(passport): num += sum(validators[field](value) for field, value in passport.items()) == len(passport) return num if __name__ == "__main__": solve(4, parse, num_valid_passports, num_valid_passports_strict)
next[cup] = dest_next next[dest] = cup prev[dest_next] = cup dest = cup if verbose: print('-- final --') print_cups(next, head) return next def crab_cups_100(cups): next = simulate(cups, 100) result = '' cup = next[1] for i in range(len(cups) - 1): result += str(cup) cup = next[cup] return result def crab_cups_10m(cups): cups.extend(range(max(cups) + 1, 1000001)) next = simulate(cups, 10000000) return next[1] * next[next[1]] if __name__ == "__main__": solve(23, parse, crab_cups_100, crab_cups_10m)
#!/usr/bin/env python """Advent of Code 2020, Day 25""" from aoc import solve def parse(data): return [int(x) for x in data.split('\n')] def encryption_key(keys): n = 1 encryption_key = 1 while n != keys[0]: n = (n * 7) % 20201227 encryption_key = (encryption_key * keys[1]) % 20201227 return encryption_key if __name__ == "__main__": solve(25, parse, encryption_key)
def parse(data): pattern = re.compile(r'(\d+)-(\d+) (\w): (\w+)') for line in data.split('\n'): match = pattern.match(line) yield ( int(match.group(1)), int(match.group(2)), match.group(3), match.group(4) ) def num_valid_passwords_wrong(passwords): return sum( low <= sum(c == repeated for c in password) <= high for low, high, repeated, password in passwords ) def num_valid_passwords_right(passwords): return sum( (password[a-1] == once) ^ (password[b-1] == once) for a, b, once, password in passwords ) if __name__ == "__main__": solve(2, parse, num_valid_passwords_wrong, num_valid_passwords_right)
print(f"...anyway, back to game {game}.") else: winner = max(enumerate(plays), key=lambda x: x[1])[0] if verbose: print(f"Player {winner+1} wins round {round} of game {game}!") decks[winner].append(plays[winner]) decks[winner].append(plays[1 - winner]) round += 1 if verbose: print(f"The winner of game {game} is player {winner+1}!") print() if game == 1: if verbose: print() print(f"== Post-game results ==") print(f"Player 1's deck:", ', '.join(map(str, decks[0]))) print(f"Player 2's deck:", ', '.join(map(str, decks[1]))) deck = max(decks, key=lambda x: len(x)) deck.reverse() return sum((i+1)*x for i, x in enumerate(deck)) else: return winner if __name__ == "__main__": solve(22, parse, combat, recursive_combat)
offsets.remove(tuple(0 for x in range(dim))) for cycle in range(cycles): neighbors = set() for pos in active: for offset in offsets: neighbors.add(tuple(a + b for a, b in zip(pos, offset))) new = set() for pos in neighbors: n = 0 for offset in offsets: neighbor = tuple(a + b for a, b in zip(pos, offset)) n += neighbor in active if pos in active and n in [2, 3]: new.add(pos) elif pos not in active and n == 3: new.add(pos) active = new return len(active) def sim3(init): return simulate(init, dim=3) def sim4(init): return simulate(init, dim=4) if __name__ == "__main__": solve(17, parse, sim3, sim4)
from aoc import solve def parse(data): return [int(x) for x in data.split('\n')] def compute_diffs(adapters): jolts = [0] + sorted(adapters) + [3 + max(adapters)] return [jolts[i] - jolts[i-1] for i in range(1, len(jolts))] def jolt_1_3(adapters): diffs = compute_diffs(adapters) return diffs.count(1) * diffs.count(3) def num_combos(n): return 1 if n == 0 else sum(num_combos(n-k-1) for k in range(min(n, 3))) def arrangements(adapters): diffs = compute_diffs(adapters) runs = [len(list(g)) for k, g in groupby(diffs, lambda x: x == 1) if k] return reduce(lambda x, y: x*y, (num_combos(n) for n in runs)) if __name__ == "__main__": solve(10, parse, jolt_1_3, arrangements)
#!/usr/bin/env python """Advent of Code 2020, Day 15""" from aoc import solve def parse(data): return [int(x) for x in data.split(',')] def memory_game(start, max_turns=2020): spoken = {n: i for i, n in enumerate(start[:-1])} prev = start[-1] for turn in range(len(start), max_turns): n = turn - spoken[prev] - 1 if prev in spoken else 0 spoken[prev] = turn - 1 prev = n return prev def challenge(start): return memory_game(start, max_turns=30000000) if __name__ == "__main__": solve(15, parse, memory_game, challenge)
def initialize(program): mem = defaultdict(int) for a, b in program: if a == 'mask': mask_out = int(b.replace('1', '0').replace('X', '1'), 2) mask_in = int(b.replace('X', '0'), 2) else: mem[a] = (b & mask_out) | mask_in return sum(mem.values()) def initialize_v2(program): mem = defaultdict(int) for a, b in program: if a == 'mask': c = b.count('X') masks = [] for n in range(1 << b.count('X')): mask = b for bit in f'{n:0{c}b}': mask = mask.replace('X', bit, 1) masks.append(int(mask, 2)) else: for mask in masks: mem[(a & ~masks[-1]) | mask] = b return sum(mem.values()) if __name__ == "__main__": solve(14, parse, initialize, initialize_v2)
def get(slf, pos): return slf.tiles[Vec2(pos.x % slf.w, pos.y)] def parse(data): return Terrain(data) def count_trees(terrain, pos=Vec2(0, 0), slope=Vec2(3, 1)): num_trees = 0 while pos.y < terrain.h: num_trees += terrain.get(pos) == '#' pos += slope return num_trees def check_slopes(terrain): results = [ count_trees(terrain, slope=Vec2(1, 1)), count_trees(terrain, slope=Vec2(3, 1)), count_trees(terrain, slope=Vec2(5, 1)), count_trees(terrain, slope=Vec2(7, 1)), count_trees(terrain, slope=Vec2(1, 2)) ] return reduce(lambda x, y: x*y, results) if __name__ == "__main__": solve(3, parse, count_trees, check_slopes)
#!/usr/bin/env python """Advent of Code 2020, Day 6""" from functools import reduce from aoc import solve def parse(data): return [[set(p) for p in g.split('\n')] for g in data.split('\n\n')] def anyone_answers(groups): return sum(len(reduce(lambda x, y: x | y, g)) for g in groups) def everyone_answers(groups): return sum(len(reduce(lambda x, y: x & y, g)) for g in groups) if __name__ == "__main__": solve(6, parse, anyone_answers, everyone_answers)
def parse(data): yield from data.split('\n') def bsp(d0, d1, s): return int(s.replace(d0, '0').replace(d1, '1'), 2) def seat_coord(seat): return (bsp('F', 'B', seat[:7]), bsp('L', 'R', seat[7:])) def seat_id(row, col): return row * 8 + col def high_seat_id(seats): return max(seat_id(*seat_coord(seat)) for seat in seats) def find_my_seat(seats): filled = set(seat_coord(seat) for seat in seats) for row in range(9, 120): for col in range(8): if (row, col) not in filled: return seat_id(row, col) if __name__ == "__main__": solve(5, parse, high_seat_id, find_my_seat)
new.extend(repeat('31', n11)) new.append(')') else: new.append('(') new.extend(rules[c]) new.append(')') else: new.append(c) if len(new) == len(expanded): return re.compile('^' + ''.join(new) + '$') expanded = new def part1(input): pattern = to_regex(input.rules) return sum(pattern.match(msg) is not None for msg in input.messages) def part2(input): patterns = [to_regex(input.rules, fix=True, n11=i) for i in range(1, 10)] matches = set() for pattern in patterns: for msg in input.messages: if pattern.match(msg): matches.add(msg) return len(matches) if __name__ == "__main__": solve(19, parse, part1, part2)
black.remove(tile) else: black.add(tile) return len(black) if num else black def adjacencies(tile): yield from (tile + dir for dir in dirs.values()) def art_exhibit(instrs, days=100, verbose=False): black = flip_tiles(instrs, num=False) for day in range(1, days + 1): new_black = set() for tile in {adj for tile in black for adj in adjacencies(tile)}: n = sum(adj in black for adj in adjacencies(tile)) if tile in black and 1 <= n <= 2: new_black.add(tile) elif tile not in black and n == 2: new_black.add(tile) black = new_black if verbose and (day <= 10 or day % 10 == 0): print(f'Day {day}: {len(black)}') if day == 10: print() return len(black) if __name__ == "__main__": solve(24, parse, flip_tiles, art_exhibit)
def find_inert(foods): dangerous = set() for a in {a for _, alls in foods for a in alls}: dangerous |= reduce(and_, (ings for ings, alls in foods if a in alls)) return {i for ings, _ in foods for i in ings} - dangerous def num_inert(foods): inert = find_inert(foods) return sum(i in inert for ings, _ in foods for i in ings) def danger_list(foods): ignore = find_inert(foods) open = {a for _, alls in foods for a in alls} map = {} while open: for a in list(open): contains = reduce(and_, (ings - ignore for ings, alls in foods if a in alls)) if len(contains) == 1: ing = contains.pop() map[a] = ing open.remove(a) ignore.add(ing) return ','.join(map[k] for k in sorted(map.keys())) if __name__ == "__main__": solve(21, parse, num_inert, danger_list)
slf.timestamp = int(lines[0]) slf.buses = [ (i, int(id)) for i, id in enumerate(lines[1].split(',')) if id != 'x' ] def parse(data): return Notes(data) def earliest_bus(notes): bus = min( ((id - notes.timestamp % id, id) for off, id in notes.buses), key=lambda x: x[0] ) return bus[0] * bus[1] def contest(notes): step = t = 1 for n in range(len(notes.buses)): while not all((t + off) % id == 0 for off, id in notes.buses[:n+1]): t += step step *= notes.buses[n][1] return t if __name__ == "__main__": solve(13, parse, earliest_bus, contest)
for v in ticket: if not valid(doc, v): error_rate += v return error_rate def identify(doc): tickets = [t for t in doc.nearby if all(valid(doc, v) for v in t)] fields = set(doc.fields.keys()) positions = set(range(len(doc.ticket))) assigned = {} while len(fields) > 0: for field in list(fields): rules = doc.fields[field] possible = [] for p in positions: n = sum(lo <= t[p] <= hi for t in tickets for lo, hi in rules) if n == len(tickets): possible.append(p) if len(possible) == 1: position = possible.pop() assigned[field] = position fields.remove(field) positions.remove(position) return reduce(mul, (doc.ticket[assigned[f]] for f in assigned if f.startswith('departure'))) if __name__ == "__main__": solve(16, parse, error_rate, identify)
from aoc import solve def parse(data): bag_pattern = re.compile(r'(\d+) (\w+ \w+) bags?') rules = {} for rule in data.split('\n'): color, bags = rule.split(' bags contain ') rules[color] = {c: int(n) for n, c in bag_pattern.findall(bags)} return rules def has_target(rules, color, target): rule = rules[color] return target in rule or any( has_target(rules, c, target) for c in rule.keys()) def num_colors_that_fit(rules, target='shiny gold'): return sum(has_target(rules, color, target) for color in rules.keys()) def bags_within(rules, target='shiny gold'): return sum(n * (1 + bags_within(rules, color)) for color, n in rules[target].items()) if __name__ == "__main__": solve(7, parse, num_colors_that_fit, bags_within)
visited = set() while pc not in visited and pc < len(prog): visited.add(pc) instr, val = prog[pc] if instr == 'acc': acc += int(val) pc += 1 elif instr == 'jmp': pc += int(val) else: pc += 1 if error and (pc in visited or pc > len(prog)): raise BadTerminationError() return acc def repair(prog): for pc, (instr, val) in enumerate(prog): if instr == 'nop': prog[pc] = ('jmp', val) elif instr == 'jmp': prog[pc] = ('nop', val) try: return exec(prog, True) except BadTerminationError: prog[pc] = (instr, val) if __name__ == "__main__": solve(8, parse, exec, repair)
if oriented[pos] == '#': matches.add(pos) if len(matches) == len(monster): monsters.update(matches) if len(monsters) > 0: return sum(c == '#' for pos, c in oriented.items() if pos not in monsters) def water_roughness(tiles, print_image=False): grid, size = assemble(tiles) image, size = reconstruct(grid, size) if print_image: print() for y in range(size): for x in range(size): pos = Vec2(x, y) if pos in image: print(image[pos], end='') else: print(' ', end='') print() monster, w, h = get_sea_monster() return find_monsters(image, size, monster, w, h) if __name__ == "__main__": solve(20, parse, corner_ids, water_roughness)
#!/usr/bin/env python """Advent of Code 2020, Day 1""" from functools import reduce from itertools import product from aoc import solve def parse(data): return [int(x) for x in data.split('\n')] def find_sum_2020(expenses, n): for p in product(expenses, repeat=n): if sum(p) == 2020: return reduce(lambda x, y: x*y, p) if __name__ == "__main__": solve(1, parse, lambda x: find_sum_2020(x, 2), lambda x: find_sum_2020(x, 3))
from itertools import combinations from aoc import solve def parse(data): return [int(x) for x in data.split('\n')] def invalid(numbers, preamble=25): for i, n in enumerate(numbers[preamble:]): if n not in { a + b for a, b in combinations(numbers[i:i + preamble], 2) }: return n def weakness(numbers, preamble=25): v = invalid(numbers, preamble) s = [sum(numbers[:i]) for i in range(len(numbers))] for i in range(len(s)): for j in range(i + 1, len(s)): if s[j] - s[i] == v: r = numbers[i:j] return min(r) + max(r) if __name__ == "__main__": solve(9, parse, invalid, weakness)
slf.waypoint = slf.waypoint.rot90left() def R(slf, value): for i in range(value // 90): slf.waypoint = slf.waypoint.rot90right() def F(slf, value): slf.pos += value * slf.waypoint def parse(data): return [(instr[0], int(instr[1:])) for instr in data.split('\n')] def navigate(ship, instrs): for action, value in instrs: getattr(ship, action)(value) return manhattan(ship.pos, Vec2(0, 0)) def navigate1(instrs): return navigate(Ship1(), instrs) def navigate2(instrs): return navigate(Ship2(), instrs) if __name__ == "__main__": solve(12, parse, navigate1, navigate2)
q.append(ops.pop()) return q def eval(q): n = [] for c in q: if c == '+': n.append(n.pop() + n.pop()) elif c == '*': n.append(n.pop() * n.pop()) else: n.append(c) return n[0] def sum_eval(exprs, prec): return sum(eval(shunting_yard(x, prec)) for x in exprs) def part1(exprs): return sum_eval(exprs, prec={'+': 0, '*': 0}) def part2(exprs): return sum_eval(exprs, prec={'+': 1, '*': 0}) if __name__ == "__main__": solve(18, parse, part1, part2)
changed = True elif seat == OCCUPIED_SEAT and vision.count(OCCUPIED_SEAT) >= 5: seat = EMPTY_SEAT changed = True new_seats[pos] = seat slf.seats = new_seats return changed def occupied(slf): return list(slf.seats.values()).count(OCCUPIED_SEAT) def parse(data): return SeatLayout(data=data) def occupied_seats1(layout): while layout.step1(): pass return layout.occupied() def occupied_seats2(layout): while layout.step2(): pass return layout.occupied() if __name__ == "__main__": solve(11, parse, occupied_seats1, occupied_seats2)