from santas_little_helpers import day, get_data, timed today = day(2017, 6) def redistribute(page: list, page_size: int = 16) -> tuple: i = page.index(max(page)) val = page[i] page[i] = 0 for idx in range(i, i + val): page[(idx + 1) % page_size] += 1 return tuple(page) def reallocate(page: tuple) -> (int, int): seen = {} while page not in seen: seen[page] = len(seen) page = redistribute(list(page)) return len(seen), len(seen) - seen[page] def main() -> None: data = next(get_data(today, [('split', '\t'), ('map', int)])) star1, star2 = reallocate(tuple(data)) print(f'{today} star 1 = {star1}') print(f'{today} star 2 = {star2}') if __name__ == '__main__': timed(main)
from santas_little_helpers import day, get_data, timed from collections import deque today = day(2018, 21) def optimised_activation_system(r2_value): seen = set() last_r2 = None r2 = 0 while True: r4 = r2 | 65536 r2 = r2_value while True: r2 += r4 & 255 r2 &= 16777215 r2 *= 65899 r2 &= 16777215 if 256 > r4: if r2 in seen: yield last_r2 return seen.add(r2) last_r2 = r2 yield r2 break else: r4 //= 256 def main() -> None:
from santas_little_helpers import day, get_data, timed today = day(2017, 9) def process(stream: str) -> (int, int): skip_next = False in_garbage = False count = 0 depth = 0 garbage_count = 0 for x in stream: if skip_next: skip_next = False else: if x == '!': skip_next = True elif in_garbage: if x == '>': in_garbage = False else: garbage_count += 1 elif x == '<': in_garbage = True elif x == '{': depth += 1 count += depth elif x == '}': depth -= 1 return count, garbage_count
from santas_little_helpers import day, get_data, timed from itertools import islice today = day(2018, 8) def parse_child(data): child_count, meta_count = next(data), next(data) if child_count == 0: return [sum(islice(data, meta_count))] * 2 meta_total = meta_val = 0 child_metas = [] for _ in range(child_count): child_sum, child_meta = parse_child(data) meta_total += child_sum child_metas += [child_meta] for metadata in islice(data, meta_count): meta_total += metadata meta_val += child_metas[metadata - 1] \ if metadata > 0 and metadata <= child_count else 0 return meta_total, meta_val def main() -> None: data = iter(next(get_data(today, [('split', ' '), ('map', int)]))) star1, star2 = parse_child(data) print(f'{today} star 1 = {star1}') print(f'{today} star 2 = {star2}')
from santas_little_helpers import day, get_data, timed from collections import deque today = day(2018, 15) class Player(object): def __init__(self, y, x, team, elf_ap=3): self.y = y self.x = x self.team = team self.hp = 200 self.ap = elf_ap if team == 'E' else 3 self.enemy_team = 'G' if self.team == 'E' else 'E' def __lt__(self, other): return self.position < other.position def attack(self, other): other.hp -= self.ap def _get_alive(self): return self.hp > 0 def _get_position(self): return self.y, self.x def _set_position(self, pos): self.y, self.x = pos position = property(_get_position, _set_position)
from santas_little_helpers import day, get_data, timed from collections import defaultdict import operator today = day(2017, 8) regs = defaultdict(int) ops = { '==': operator.eq, '!=': operator.ne, '<=': operator.le, '>=': operator.ge, '<': operator.lt, '>': operator.gt } class Instruction(): def __init__(self, arr): self.reg = arr[0] self.run = self.__inc if arr[1] == 'inc' else self.__dec self.__amt = int(arr[2]) self.__cond_reg = arr[4] self.__cond_op = ops[arr[5]] self.__cond_amt = int(arr[6]) def valid(self): return self.__cond_op(regs[self.__cond_reg], self.__cond_amt) def __inc(self):
from santas_little_helpers import day, get_data, timed import re today = day(2017, 7) class Node(): rx = re.compile(r'([\w]*) \(([\d]*)([\w, ->]+)') def __init__(self, string): m = self.rx.match(string) self.name = m.group(1) self.weight = int(m.group(2)) self.total_weight = self.weight self.nodes = [] subnodes = m.group(3)[5:].split(', ') if len(subnodes[0]) == 0: subnodes = [] self.carrying = subnodes def carries(self, other): self.nodes += [other] self.total_weight += other.total_weight def get_node(string, nodes): for node in nodes: if node.name == string: return node return None def build_tree(root, nodes): for node_name in root.carrying: node = build_tree(get_node(node_name, nodes), nodes)
from santas_little_helpers import day, get_data, timed today = day(2018, 19) pc_reg = 0 regs = [] # Meta def _ip(a): global pc_reg pc_reg = a # Math def addr(a, b, c): regs[c] = regs[a] + regs[b] def addi(a, b, c): regs[c] = regs[a] + b def mulr(a, b, c): regs[c] = regs[a] * regs[b] def muli(a, b, c): regs[c] = regs[a] * b
from santas_little_helpers import day, get_data, timed today = day(2018, 18) OPEN_GROUND = '.' TREE = '|' LUMBERYARD = '#' TARGET = 1e9 def forest_generator(grid): maps = { str(grid): 0 } minute = 1 while minute <= TARGET: grid = iterate_grid(grid) if str(grid) in maps: loop_size = minute - maps[str(grid)] multiplier = (TARGET - minute) // loop_size minute += loop_size * multiplier if minute == 10: yield value(grid) if minute == TARGET: yield value(grid) maps[str(grid)] = minute minute += 1 def value(grid): full = ''.join(grid) return full.count(TREE) * full.count(LUMBERYARD) def iterate_grid(grid): grid_size = len(grid)
from santas_little_helpers import day, get_data, timed, alphabet from collections import defaultdict today = day(2018, 7) alphas = alphabet.upper() def singlesledded(steps): completed = '' while len(completed) < len(steps): completed += possible_steps(steps, completed)[0] return completed def multisledded(steps, workers=5): available_at = defaultdict(list) in_progress = set() time_index = -1 completed = [] while len(completed) < len(steps): time_index += 1 if time_index in available_at: now_available = available_at[time_index] workers += len(now_available) completed += sorted(now_available) if workers == 0: continue for step in possible_steps(steps, completed, in_progress): if workers > 0: in_progress.add(step)
from santas_little_helpers import day, get_data, timed today = day(2017, 17) def hurricane(data: int) -> (int, int): l = [0] idx = 0 for x in range(1, 2017 + 1): idx = (idx + data) % len(l) + 1 l = l[:(idx + 1)] + [x] + l[(idx + 1):] star1 = l[(l.index(2017) + 1) % len(l)] second_val = 0 for x in range(1, 50000000 + 1): idx = (idx + data) % x + 1 if idx == 1: second_val = x return star1, second_val def main() -> None: data = int(next(get_data(today))) star1, star2 = hurricane(data) print(f'{today} star 1 = {star1}') print(f'{today} star 2 = {star2}') if __name__ == '__main__': timed(main)
from santas_little_helpers import day, get_data, timed import networkx as nx, re today = day(2017, 12) rx = re.compile(r'([\d]*) <-> ([\w, ]*)') def graph(data: [(int, [int])]) -> (int, int): g = nx.Graph() for x, l in data: g.add_edges_from((x, n) for n in l) star1 = len(nx.node_connected_component(g, 0)) star2 = nx.number_connected_components(g) return star1, star2 def fun(line: str) -> (int, [int]): m = rx.match(line) return int(m.group(1)), [int(x) for x in m.group(2).split(', ')] def main() -> None: data = get_data(today, [('func', fun)]) star1, star2 = graph(data) print(f'{today} star 1 = {star1}') print(f'{today} star 2 = {star2}') if __name__ == '__main__': timed(main)
from santas_little_helpers import day, get_data, timed today = day(2018, 14) def part1(it, recipes): for _ in range(recipes + 10): recipe_scores = next(it) return ''.join(map(str, recipe_scores[recipes:recipes + 10])) def part2(it, token): while True: recipe_scores = next(it) offset = token_found(token, recipe_scores) if offset >= 0: return len(recipe_scores) - len(token) - offset def recipe_generator(): recipe_scores = [3, 7] elf1, elf2 = 0, 1 while True: new_val = map(int, str(recipe_scores[elf1] + recipe_scores[elf2])) recipe_scores += new_val yield recipe_scores elf1 = (elf1 + recipe_scores[elf1] + 1) % len(recipe_scores) elf2 = (elf2 + recipe_scores[elf2] + 1) % len(recipe_scores) def token_found(token, recipe_scores):
from santas_little_helpers import day, get_data, timed today = day(2017, 21) book = {} def rotate(grid: tuple) -> tuple: size = len(grid) new = [] for x in range(size): row = '' for y in range(size): row += grid[size - 1 - y][x] new += [row] grid = new return tuple(grid) def mirror(grid: tuple) -> tuple: size = len(grid) new = [] for y in range(size): row = '' for x in range(size): row += grid[y][size - 1 - x] new += [row] return tuple(new) def pixels(grid: [str], bound: int = 5) -> int: for _ in range(bound):
from santas_little_helpers import day, get_data, timed, alphabet today = day(2018, 5) pairs = [c + c.upper() for c in alphabet] pairs += [c.upper() + c for c in alphabet] def react(polymer): old_p = None while old_p != polymer: old_p = polymer for pair in pairs: polymer = polymer.replace(pair, '') return len(polymer), polymer def shortest_polymer(polymer): return min([react(without(c, polymer))[0] for c in alphabet]) def without(c, polymer): return polymer.replace(c, '').replace(c.upper(), '') def main() -> None: star1, first_run = react(next(get_data(today))) print(f'{today} star 1 = {star1}') print(f'{today} star 2 = {shortest_polymer(first_run)}')
from santas_little_helpers import day, get_data, timed from operator import itemgetter today = day(2017, 24) def fits(socket: int, left: int, right: int) -> bool: return any([socket == left, socket == right]) def bridges(cmps: {}, loops: {}, l:int=0, s:int=0, port:int=0) -> (int, int): l += 1 compatible = [nxt for nxt in cmps if fits(port, *nxt)] if port in loops: l += 1 s += port*2 if len(compatible) < 1: yield l, s for c in compatible: ns = s+sum(c) yield l, ns other = c[(c.index(port)+1)%2] yield from bridges(cmps-{c}, loops-{port}, l, ns, other) def parse(line: str) -> (int, int): return tuple(sorted(map(int, line.split('/')))) def main() -> None: all_components = set(get_data(today, [('func', parse)])) loops = set((a,b) for a,b in all_components if a==b) table = set(bridges(all_components-loops, set(a for a, _ in loops)))
from santas_little_helpers import day, get_data, timed from enum import Enum from typing import Type today = day(2017, 3) class Direction(Enum): NORTH = ('y', 1) WEST = ('x', -1) SOUTH = ('y', -1) EAST = ('x', 1) def __init__(self, axis: str, diff: int): self.axis = axis self.diff = diff star1 = None star2 = None target = None x, y = (1, 0) step = 1 adjacency_values = {(0, 0): 1} adjacency_matrix = [(-1, 1), (0, 1), (1, 1), (-1, 0), (1, 0), (-1, -1), (0, -1), (1, -1)] def adjacent_value(x: int, y: int):
from santas_little_helpers import day, get_data, timed from collections import defaultdict today = day(2017, 25) def turing(machine: {}) -> int: state = machine[machine['start']] pos = 0 tape = defaultdict(int) for _ in range(machine['check']): tape[pos], m, s = state[tape[pos]] pos += m state = machine[s] return sum(tape.values()) def main() -> None: machine = {} for l in get_data(today, [('func', lambda l: l.strip())]): if l[:3] == 'Beg': machine['start'] = l[-2] elif l[:3] == 'Per': machine['check'] = int(l.split(' ')[-2]) elif l[:3] == 'In ': c_s, machine[c_s] = l[-2], {} elif l[:3] == 'If ': c_v = int(l[-2]) elif l[:3] == '- W': machine[c_s][c_v] = (int(l[-2]), ) elif l[:3] == '- M': machine[c_s][c_v] += (1 if l[-6] == 'r' else -1, ) elif l[:3] == '- C': machine[c_s][c_v] += (l[-2], ) print(f'{today} star 1 = {turing(machine)}') print(f'{today} star 2 = Merry Christmas!')
from santas_little_helpers import day, get_data, timed today = day(2020, 3) def predict_slope(xp, yp): x = xp y = yp trees = 0 while y < slope_length: trees += forest[y][x % map_width] == '#' x += xp y += yp return trees def solve(): slopes = [(1, 1), (3, 1), (5, 1), (7, 1), (1, 2)] tree_product = 1 for i, slope in enumerate(slopes): trees = predict_slope(*slope) if i == 1: yield trees tree_product *= trees yield tree_product def main(): global forest, map_width, slope_length forest = list(get_data(today)) slope_length, map_width = len(forest), len(forest[0])
from santas_little_helpers import day, get_data, timed from itertools import combinations today = day(2020, 1) def part1(ns): for n1, n2 in combinations(ns, 2): if n1 + n2 == 2020: return n1 * n2 def part2(ns): for n1, n2, n3 in combinations(ns, 3): if n1 + n2 + n3 == 2020: return n1 * n2 * n3 def main() -> None: inp = list(get_data(today, [('func', int)])) print(f'{today} star 1 = {part1(inp)}') print(f'{today} star 2 = {part2(inp)}') if __name__ == '__main__': timed(main)
from santas_little_helpers import day, get_data, timed from typing import Callable regs = {} today = day(2017, 23) def put(args: tuple) -> int: reg, val = args if val in regs: val = regs[val] regs[reg] = val return 1 def sub(args: tuple) -> int: reg, val = args if val in regs: val = regs[val] regs[reg] -= val return 1 def mul(args: tuple) -> int: reg, val = args if val in regs: val = regs[val] regs[reg] *= val return 1 def jnz(args: tuple) -> int: cond, jmp = args if cond in regs:
from santas_little_helpers import base_ops, day, get_data, timed from direction import Direction, ds today = day(2017, 22) the_map = {} def infect(p: (int, int), evolved: bool = False) -> int: d = Direction.NORTH infections = 0 bound = 10000000 if evolved else 10000 for _ in range(bound): c = the_map[p] if p in the_map else '.' if c == '#': d = ds[d.right] the_map[p] = 'f' if evolved else '.' elif c == 'f': d = ds[d.back] the_map[p] = '.' elif c == '.': d = ds[d.left] the_map[p] = 'w' if evolved else '#' if not evolved: infections += 1 elif c == 'w': the_map[p] = '#' infections += 1 p = d + p return infections
from collections import namedtuple from networkx import Graph from networkx.algorithms import shortest_path_length class Point(namedtuple('Point', ['x', 'y'])): def __add__(self, other): return Point(self.x + other.x, self.y + other.y) def __lt__(self, other): return self.y < other.y graph = Graph() today = day(2018, 20) direction = { 'N': Point(0, -1), 'E': Point(1, 0), 'S': Point(0, 1), 'W': Point(-1, 0) } def build_map(path_regex): pos = starts = {Point(0, 0)} stack = [] ends = set() for current in path_regex:
from santas_little_helpers import day, get_data, timed from operator import attrgetter as attr import re r = re.compile(r'(?P<units>\d*) units each with ' \ + r'(?P<hp>\d*) hit points (?:\(' \ + r'(?P<modifiers>[^\)]*)\) |)with an attack that does ' \ + r'(?P<dmg>\d*) ' \ + r'(?P<dmgtype>\w*) damage at initiative ' \ + r'(?P<initiative>\d*)') today = day(2018, 24) active_team = None teams = {'immune_system': [], 'infection': []} original_input = [] def parse_modifiers(modifiers): if not modifiers: return [], [] modifiers = sorted(modifiers.split('; ')) if len(modifiers) < 2: if modifiers[0].startswith('w'): modifiers.insert(0, []) else: modifiers.append([]) immunities, weaknesses = modifiers if immunities:
from santas_little_helpers import day, get_data, timed from collections import defaultdict import re today = day(2018, 4) r = re.compile( r'\[[\d\-: ]*(?P<minute>\d{2})\] (?P<event>Guard #(?P<guardid>\d+)|w|f)') guardid = '' def find_lazy_guard(log_entries): times = [] minutes_sleeping = {} active_guard = log_entries[0][1] for guardid, event, minute in log_entries: if guardid != active_guard: if active_guard not in minutes_sleeping: minutes_sleeping[active_guard] = defaultdict(int) handle_guard_times(times, minutes_sleeping[active_guard]) active_guard = guardid times = [] times += [(event, minute)] highest = None, 0 for guard, minutes in minutes_sleeping.items(): total = sum(minutes.values()) if total > highest[1]: highest = guard, total most_slept = None, 0 for minute, count in minutes_sleeping[highest[0]].items():
from santas_little_helpers import day, get_data, timed today = day(2018, 11) def measure_grid_power(grid, grid_size=3): square_parts = 300 - grid_size for y in range(square_parts): for x in range(square_parts): yield square_power_level(grid, x, y, grid_size) def square_power_level(grid, x, y, grid_size): total = 0 for yp in range(grid_size): for xp in range(grid_size): total += grid[yp + y][xp + x] return total, (x + 1, y + 1, grid_size) def cell_power_level(x, y, serial_number): raw = str(((x + 10) * y + serial_number) * (x + 10)) power = 0 if len(raw) < 3 else int(raw[-3]) return power - 5 def variable_grid_size(grid): power_levels = [] for i in range(1, 300): new_reading = max(measure_grid_power(grid, grid_size=i)) power_levels += [new_reading]
from santas_little_helpers import day, get_data, timed today = day(2017, 16) def spin(progs: str, n: int) -> str: return progs[-n:] + progs[:-n] def exchange(progs: str, a: int, b: int) -> str: progs[a], progs[b] = progs[b], progs[a] return progs def partner(progs: str, a: str, b: str) -> str: return exchange(progs, progs.index(a), progs.index(b)) def dance(routine: list, progs: str, repetitions: int = 1) -> str: seen = [] for i in range(repetitions): s = ''.join(progs) if s in seen: return seen[repetitions % i] seen += [s] for move in routine: if move[0] == 's': progs = spin(progs, int(move[1:])) elif move[0] == 'x': args = move[1:].split('/') progs = exchange(progs, int(args[0]), int(args[1]))
def left(self): return self + Point(-1, 0) @property def down(self): return self + Point(0, 1) @property def right(self): return self + Point(1, 0) base = Point(0, 0) LEFT, DOWN, RIGHT = base.left, base.down, base.right today = day(2018, 17) rl = re.compile(r'([xy])=(\d+)') rr = re.compile(r'([xy])=(\d+)..(\d+)') settled, flowing = set(), set() ymax, ymin = 0, 0 clay = defaultdict(bool) def flow(current, direction=DOWN): if current in flowing: return True flowing.add(current) if not clay[current.down]:
def right(self): return self + Point(1, 0) @property def neighbours(self): return [self.up, self.down, self.left, self.right] @property def erosion(self): return erosion[self] ENTRANCE = Point(0, 0) UP, DOWN, LEFT, RIGHT = ENTRANCE.neighbours today = day(2018, 22) def geologic_index(p, target): if p in [ENTRANCE, target]: return 0 elif p.x == 0: return p.y * 48271 elif p.y == 0: return p.x * 16807 else: return p.left.erosion * p.up.erosion def equipment(erosion_level): erosion_level %= 3
from santas_little_helpers import day, get_data, timed from direction import Direction, ds today = day(2017, 19) the_map = {} def follow(the_map: [str]) -> (int, int): p = the_map[0].index('|'), 0 d = Direction.SOUTH letters = '' steps = 0 while True: c = the_map[p[1]][p[0]] if c == ' ': break elif c == '+': l = ds[d.left] + p d = ds[d.left] if the_map[l[1]][l[0]] != ' ' else ds[d.right] elif c not in '-|': letters += c p = d + p steps += 1 return letters, steps def main() -> None: data = get_data(today, [('func', list)]) star1, star2 = follow(list(data)) print(f'{today} star 1 = {star1}')