Beispiel #1
0
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}')
Beispiel #5
0
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)
Beispiel #6
0
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)
Beispiel #8
0
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)
Beispiel #10
0
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)
Beispiel #11
0
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):
Beispiel #14
0
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):
Beispiel #15
0
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)))
Beispiel #17
0
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)
Beispiel #21
0
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:
Beispiel #22
0
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
Beispiel #23
0
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:
Beispiel #24
0
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():
Beispiel #26
0
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}')