Beispiel #1
0
def solve(steps, file):
    data = load(file)
    grid = [[int(c) for c in x] for x in data]
    n, m = len(grid), len(grid[0])
    flashes = 0

    def flash(q, x, y):
        grid[y][x] = (grid[y][x] + 1) % 10
        if not grid[y][x]:
            for dx, dy in DIRS_8:
                if 0 <= y + dy < n and 0 <= x + dx < m:
                    q += (x + dx, y + dy),
        return not grid[y][x]

    for round in range(1, (steps or 999) + 1):
        q = []
        for x in range(m):
            for y in range(n):
                flashes += flash(q, x, y)
        while q:
            x, y = q.pop()
            if grid[y][x]:
                flashes += flash(q, x, y)
        if set(x for row in grid for x in row) == {0}:
            return round
    return flashes
Beispiel #2
0
def solve(part, file):
    ingredients = {}
    for line in load(file):
        name, spec = line.split(':')
        ingredients[name] = Ingredient(*parse_nums(spec))

    return max(scores(ingredients, 500 * (part - 1)))
Beispiel #3
0
def solve(part, file):
  data = load(file)
  graph = defaultdict(list)
  for line in data:
    a, b = line.split('-')
    graph[a] += b,
    graph[b] += a,

  def dfs(node, visited, mulligans):
    added = 0
    if node == 'end':
      return 1
    elif node.islower():
      if node in visited and (node == 'start' or not mulligans):
        return 0
      elif node in visited:
        mulligans -= 1
      else:
        added = 1
        visited.add(node)
    try:
      return sum(dfs(g, visited, mulligans) for g in graph[node])
    finally:
      if added:
        visited.remove(node)

  return dfs('start', set(), part - 1)
Beispiel #4
0
def solve(part, file):
    input = load(file)[0]
    g = hashgen(input, part == 2)
    code = [next(g) for _ in range(8)]
    if part == 2:
        code = sorted((x[::-1] for x in code), key=itemgetter(1))
    return ''.join(c[0] for c in code)
Beispiel #5
0
def solve(part, file):
  data = load(file)[0]
  bits = iter(bin(int(data, 16))[2:].zfill(len(data)*4))

  versions = pos = 0

  def read(x):
    nonlocal pos
    pos += x
    return int(''.join(next(bits) for _ in range(x)), 2)

  def parts(type):
    if type == 4:
      while read(1):
        yield read(4)
      yield read(4)
    elif read(1):
      for _ in range(read(11)):
        yield parse()
    else:
      size = read(15)
      end = pos + size
      while pos < end:
        yield parse()

  def parse():
    nonlocal versions
    versions += read(3)
    type = read(3)
    return reduce(OPS[type], parts(type))

  root = int(parse())
  return (versions, root)[part == 2]
Beispiel #6
0
def solve(file, compares=None):
  data = load(file)
  instructions = [list(g) for _, g in groupby(sorted(data), key=itemgetter(0))]

  splits = {}
  for line in instructions[0]:
    _, bot, _, _, _, is_out_lo, lo, _, _, _, is_out_hi, hi = find_tokens(line)
    bot, lo, hi = map(int, (bot, lo, hi))
    is_out_lo, is_out_hi = is_out_lo[0] == 'o', is_out_hi[0] == 'o'
    assert bot not in splits
    splits[bot] = ((is_out_lo, lo), (is_out_hi, hi))

  bots = defaultdict(list)
  for line in instructions[1]:
    value, bot = parse_nums(line)
    assert len(bots[bot]) < 2
    bots[bot].append(value)

  def run_bot():
    for b in bots:
      if len(bots[b]) == 2:
        (is_out_lo, lo_id), (is_out_hi, hi_id) = splits[b]
        if (is_out_lo or len(bots[lo_id]) < 2) and (is_out_hi or len(bots[hi_id]) < 2):
          return b

  outs = defaultdict(list)

  while ((b := run_bot()) is not None):
    lo, hi = sorted(bots[b])
    if compares and (lo, hi) == compares:
      return b
    bots[b] = []
    (is_out_lo, lo_id), (is_out_hi, hi_id) = splits[b]
    (outs if is_out_lo else bots)[lo_id] += lo,
    (outs if is_out_hi else bots)[hi_id] += hi,
Beispiel #7
0
def load_floors(file):
    locations = defaultdict(lambda: [0, 0])
    for floor_num, line in enumerate(load(file), 1):
        for material, type in re.findall(
                r'a (\w+)( generator|-compatible microchip)', line):
            locations[material][type[0] == '-'] = floor_num
    return tuple(map(tuple, sorted(locations.values())))
Beispiel #8
0
def solve(part, file):
    data = [*map(parse_nums, load(file))][0]
    if part == 1:
        return compute(data, lambda x: x, sorted(data)[len(data) // 2])
    else:
        avg = sum(data) / len(data)
        return compute(data, lambda x: x * (x + 1) // 2,
                       *{floor(avg), ceil(avg)})
Beispiel #9
0
def solve(part, file, lo=0, hi=0):
    ranges = sorted(parse_nums(line) for line in load(file))
    res = 0
    for x, y in ranges:
        if lo + 1 <= x - 1:
            if part == 1:
                return lo + 1
            res += x - lo - 1
        lo = max(lo, y)
    return res + hi - lo
Beispiel #10
0
def solve(part, file):
    lines = [*map(parse_nums, load(file))]
    grid = defaultdict(int)
    for x1, y1, x2, y2 in lines:
        dx, dy = sign(x2 - x1), sign(y2 - y1)
        if part == 2 or not dx or not dy:
            for i in range(abs(x2 - x1 or y2 - y1) + 1):
                grid[x1 + i * dx, y1 + i * dy] += 1

    return sum(c > 1 for c in grid.values())
Beispiel #11
0
def load_amphibians(part, file):
    hall = [-1] * 11
    rooms = [[], [], [], []]
    for line in load(file)[3:1:-1]:
        for i, c in enumerate(line[3:11:2]):
            rooms[i].append(ord(c) - ord('A'))
    if part == 2:
        for room, fold in zip(rooms, [[3, 3], [1, 2], [0, 1], [2, 0]]):
            room[:] = [room[0], *fold, room[1]]
    return tuple(hall), tuples(rooms)
Beispiel #12
0
def solve(part, file):
  data = load(file)
  total = 0
  for line in data:
    seqs = re.split(r'\[|\]', line)
    outs, ins = seqs[::2], seqs[1::2]
    if part == 1:
      total += not any(map(has_abba, ins)) and any(map(has_abba, outs))
    else:
      total += any(bab in s for bab, s in product(find_babs(outs), ins))

  return total
Beispiel #13
0
def solve(part, file):
    data = load(file)
    sues = {}
    for line in data:
        m = re.split(r'[:, ]+', line)
        sues[int(m[1])] = {
            item: int(count)
            for item, count in zip(*[iter(m[2:])] * 2)
        }
    ops = (ops_part_2 if part == 2 else {})
    for num, info in sues.items():
        if all(
                ops.get(item, eq)(count, my_sue[item])
                for item, count in info.items()):
            yield num
Beispiel #14
0
def solve(part, file):
  cube_input = [(line[:2] == 'on', parse_nums(line)) for line in load(file)]

  cubes = {}
  for on, cube in cube_input:
    if part == 2 or is_within(cube, 50):
      cube = tuple(cube)
      for other_cube in list(cubes):
        if is_overlapping(cube, other_cube):
          del cubes[other_cube]
          for piece in cut_cube(other_cube, cube):
            cubes[piece] = area(piece)
      if on:
        cubes[cube] = area(cube)
  return sum(cubes.values())
Beispiel #15
0
def solve(part, file):
  boss_hp, boss_damage = (parse_nums(line)[0] for line in load(file))

  start = Fight(mana=500, player_hp=50, boss_hp=boss_hp)

  def is_goal(fight):
    return fight.boss_hp <= 0

  def expand(fight):
    for spell, cost in spell_costs.items():
      result = fight.copy()
      if result.round(boss_damage, spell, part == 2) != False:
        yield cost, result

  return djyk(start, expand, is_goal)
Beispiel #16
0
def solve(part, file):
  boss = parse_character(load(file))
  shop = parse_shop()
  players = []
  for weapon in shop['Weapons']:
    for armor in chain(shop['Armor'], [None]):
      for num_rings in range(3):
        for rings in permutations(shop['Rings'], num_rings):
          stats = combine(weapon, armor, *rings)
          player = Character(100, stats.damage, stats.armor)
          players.append((stats.cost, player))

  players.sort()
  if part == 1:
    return next(cost for cost, player in players if fight(player, boss))
  else:
    return next(cost for cost, player in reversed(players) if not fight(player, boss))
Beispiel #17
0
def solve(file):
    data = load(file)
    n, m = len(data), len(data[0])
    grid = {(x, y): data[y][x]
            for y in range(n) for x in range(m) if data[y][x] != '.'}

    for r in count(1):
        moved = 0
        for dx, dy, c in ((1, 0, '>'), (0, 1, 'v')):
            moves = [(x, y) for x, y in grid
                     if grid[x, y] == c and ((x + dx) % m,
                                             (y + dy) % n) not in grid]
            moved = moved or len(moves)
            for x, y in moves:
                del grid[x, y]
                grid[(x + dx) % m, (y + dy) % n] = c
        if not moved:
            return r
Beispiel #18
0
def solve(part, file):
    x1, x2, y1, y2 = parse_nums(load(file)[0])
    if part == 1:
        return y1 * (y1 - 1) // 2

    def tryit(vx, vy):
        x = y = 0
        while y > -y1:
            x += vx
            y += vy
            if vx > 0:
                vx -= 1
            vy -= 1
            if x1 <= x <= x2 and -y1 <= y <= -y2:
                return 1
        return 0

    return sum(
        tryit(vx, vy) for vx in range(1, x2 + 1) for vy in range(-y1, y1))
Beispiel #19
0
def solve(part, file):
  data = load(file)
  grid = {}
  for line in data[2:]:
    pattern = r'/dev/grid/node-x(\d+)-y(\d+) +(\d+)T +(\d+)T +(\d+)T +(\d+)%'
    x, y, size, used, avail, percent = map(
        int, re.fullmatch(pattern, line).groups())
    grid[x, y] = (size, used, avail, percent)

  space_x, space_y = next((x, y) for x, y in grid if grid[x, y][1] == 0)
  too_big = set(grid) - {(space_x, space_y)} \
      - {(x, y) for x, y in grid
         if 0 < grid[x, y][1] <= grid[space_x, space_y][2]}
  barrier_x = {x for x, _ in too_big}
  opening_x = next(x for x in range(space_x, -1, -1) if x not in barrier_x)
  goal_x = max(x for x, _ in grid)

  if part == 1:
    return sum(0 < a[1] <= b[2] for a, b in permutations(grid.values(), 2))
  else:
    return 2*(space_x-opening_x) + space_y + (goal_x - space_x) + 5*(goal_x-1)
Beispiel #20
0
def solve(part, file):
  data = load(file)

  grid = defaultdict(lambda: 9)
  for y, line in enumerate(data):
    for x, c in enumerate(line):
      grid[x, y] = int(c)

  res = []
  if part == 1:
    for x, y in list(grid.keys()):
      if all(grid[x, y] < grid[x+dx, y+dy] for dx, dy in DIRS):
        res.append(1 + grid[x, y])
    return sum(res)
  else:
    def dfs(x, y):
      return grid.pop((x, y), 9) < 9 and 1 + sum(dfs(x+dx, y+dy) for dx, dy in DIRS)
    for x, y in list(grid.keys()):
      if (size := dfs(x, y)):
        res.append(size)
    return reduce(int.__mul__, nlargest(3, res))
Beispiel #21
0
def solve(part, file):
  data = load(file)
  res = []
  keypad = keypads[part].splitlines()
  x, y = starts[part]
  for line in data:
    for c in line:
      if c == 'U':
        if keypad[y-1][x] != ' ':
          y -= 1
      elif c == 'D':
        if keypad[y+1][x] != ' ':
          y += 1
      elif c == 'L':
        if keypad[y][x-1] != ' ':
          x -= 1
      elif c == 'R':
        if keypad[y][x+1] != ' ':
          x += 1
    res += keypad[y][x],
  return ''.join(res)
Beispiel #22
0
def solve(part, file):
    grid = [[int(x) for x in line] for line in load(file)]
    seen = defaultdict(lambda: inf, {(0, 0): 0})
    n, m = len(grid), len(grid[0])
    goal = (m - 1, n - 1) if part == 1 else (m * 5 - 1, n * 5 - 1)
    q = [(0, 0, 0)]
    while q:
        score, x, y = heappop(q)
        if (x, y) == goal:
            return score
        for dx, dy in DIRS_4:
            if 0 <= (x1 := x + dx) <= goal[0] and 0 <= (y1 :=
                                                        y + dy) <= goal[1]:
                if part == 1:
                    val = score + grid[y1][x1]
                else:
                    yd, ym = divmod(y1, n)
                    xd, xm = divmod(x1, m)
                    val = score + (grid[ym][xm] + xd + yd - 1) % 9 + 1
                if seen[x1, y1] > val:
                    seen[x1, y1] = val
                    heappush(q, (val, x1, y1))
Beispiel #23
0
def solve(part, input, file):
    s = list(input)
    lines = load(file)
    if part == 2:
        lines = lines[::-1]

    for line in lines:
        tokens = line.split()
        nums = parse_nums(line)
        if tokens[0] == 'swap' and tokens[1] == 'position':
            s = swap_pos(s, *nums)
        elif tokens[0] == 'swap' and tokens[1] == 'letter':
            s = swap_letter(s, tokens[2], tokens[5])
        elif tokens[0] == 'move':
            if part == 1:
                s = move(s, *nums)
            else:
                s = move(s, *nums[::-1])
        elif tokens[0] == 'reverse':
            s = reverse(s, *nums)
        elif tokens[0] == 'rotate' and tokens[1] == 'left':
            if part == 1:
                s = rotate_left(s, *nums)
            else:
                s = rotate_right(s, *nums)
        elif tokens[0] == 'rotate' and tokens[1] == 'right':
            if part == 1:
                s = rotate_right(s, *nums)
            else:
                s = rotate_left(s, *nums)
        elif tokens[0] == 'rotate' and tokens[1] == 'based':
            if part == 1:
                s = rotate_by_letter(s, tokens[-1])
            else:
                s = rotate_by_letter_inverse(s, tokens[-1])
        else:
            raise Exception('no match', line)
    return ''.join(s)
Beispiel #24
0
def solve(part, file):
    lines = load(file)
    blocks = [
        block.split('\n') for block in '\n'.join(lines).split('inp w\n')[1:]
    ]
    model = [0] * 14
    stack = []
    for i, block in enumerate(blocks):
        if block[3] == 'div z 1':
            x = int(block[14].split(' ')[-1])
            stack.append((i, x))
        elif block[3] == 'div z 26':
            y = int(block[4].split(' ')[-1])
            j, x = stack.pop()
            diff = x + y
            if diff < 0:
                i, j, diff = j, i, -diff
            if part == 1:
                model[i] = 9
                model[j] = 9 - diff
            else:
                model[i] = 1 + diff
                model[j] = 1
    return int(''.join(map(str, model)))
Beispiel #25
0
def solve(part, file):
    lines = load(file)
    optimize(lines)
    instructions = parse_instructions(lines)
    return run(instructions, {'c': part - 1})
Beispiel #26
0
def solve(file):
    row, col = parse_nums(load(file)[0])
    return find_code(row, col)
Beispiel #27
0
def solve(part, file):
  data = load(file)
  res = filter(None, (get_score(line)[part-1] for line in data))
  return (sum, median)[part-1](res)
Beispiel #28
0
def load_instructions(file):
    return [line.replace(',', '').split() for line in load(file)]
Beispiel #29
0
def load_discs(file):
    return [parse_nums(line)[3::-2] for line in load(file)]
Beispiel #30
0
def solve(part, file):
    salt = load(file)[0]
    gen = keys(salt, (1, 2017)[part - 1])
    return next(islice(gen, 63, None))