if opcode == 5 or opcode == 6: if opcode == 5: if params[0] != 0: i = params[1] continue elif opcode == 6: if params[0] == 0: i = params[1] continue # ~~ end special cases ~~ else: if m == 1: # write intcode[intcode[i+n+m]] = operation(*params) elif m == 0: operation(*params) i += n + m + 1 return intcode assert extract_opcode(1002) == 2 assert extract_opcode(1001) == 1 # same tests from day 2 (should be backwards compatible) assert tuple(run_program([1,0,0,0,99])) == (2,0,0,0,99) assert tuple(run_program([2,3,0,3,99])) == (2,3,0,6,99) assert tuple(run_program([2,4,4,5,99,0])) == (2,4,4,5,99,9801) assert tuple(run_program([1,1,1,4,99,5,6,0,99])) == (30,1,1,4,2,5,6,0,99) input_data = [int(n) for n in helper.read_day(5).split(",")] run_program(input_data)
"""Advent of Code Day 19: A Series of Tubes""" import helper import numpy as np input_data = helper.read_day(19) def traverse_diagram(input_data): # Convert data to a numpy grid (easier to index into) grid = np.array([list(line) for line in input_data.splitlines()]) curr = '|' # Start from '|' on first line row, col = 0, np.where(grid[0,:] == '|')[0][0] face = 'S' # Compass direction path = [] steps = 0 while True: steps += 1 if face == 'S': row += 1 elif face == 'N': row -= 1 elif face == 'W': col -= 1 elif face == 'E': col += 1 curr = grid[row, col] if curr == '+': # Which direction to turn into? Check non-empty adjacent cells. if face == 'N' or face == 'S': face = 'E' if grid[row, col+1] != ' ' else 'W' elif face == 'E' or face == 'W': face = 'S' if grid[row+1, col] != ' ' else 'N'
sum_weight = [] for child in curr["children"]: sum_weight.append(self.get_total_weight(child)) # Children are unbalanced / find index of unbalanced child if len(set(sum_weight)) > 1: i = [ sum_weight.index(w) for w in list(set(sum_weight)) if sum_weight.count(w) == 1 ][0] j = [ sum_weight.index(w) for w in list(set(sum_weight)) if sum_weight.count(w) > 1 ][0] wrong_weight = sum_weight[i] - sum_weight[j] wrong_node = curr["children"][i] return self.find_unbalanced(wrong_node) return node data = helper.read_day(7).splitlines() tower = TowerGraph() for row in data: # Collect alphanumeric pieces (ignore cruft) node, weight, *children = re.findall("\w+", row) tower.add_node(node, weight) tower.add_children(node, children) root = tower.find_root(node) # Check balance print(tower.find_unbalanced(root))
def parse_firewall(inp): """Parse input file to firewall data (dictionary).""" firewall = {} inp = inp.splitlines() for layer in inp: depth, rng = map(int, layer.split(": ")) firewall[depth] = rng return firewall # Part 1 # Simulate packet stepping through firewall. firewall = parse_firewall(helper.read_day(13).strip()) severity = 0 time = 0 for step in range(max(firewall) + 1): # Get caught if: time % ((firewall[step]-1)*2) == 0 where # (firewall[step]-1)*2 is the number of moves to complete one cycle of # going down and up the range of a layer. Equals 0 since we are travelling # on the tops of each layer. if step in firewall and time % ((firewall[step] - 1) * 2) == 0: severity += step * firewall[step] time += 1 print(severity) # Part 2 # Brute-force this by restarting the simulation with increased delay each time
cancelled = True else: count_garbage += 1 else: if c == "{": # Open group depth += 1 score += depth elif c == "}": # Close group depth -= 1 elif c == "<": in_garbage = True return (score, count_garbage) # Test Part 1 assert score("{}")[0] == 1 assert score("{{{}}}")[0] == 6 assert score("{{},{}}")[0] == 5 assert score("{<a>,<a>,<a>,<a>}")[0] == 1 assert score("{{<ab>},{<ab>},{<ab>},{<ab>}}")[0] == 9 assert score("{{<a!>},{<a!>},{<a!>},{<ab>}}")[0] == 3 # Test Part 2 assert score("<>")[1] == 0 assert score("<random characters>")[1] == 17 assert score("<<<<>")[1] == 3 assert score("<!!!>>")[1] == 0 assert score("""<{o"i!a,<{i<a>""")[1] == 10 stream = helper.read_day(9).strip() print(score(stream))
def enumerate_patterns(grid): """Given a base grid pattern, enumerates over the rest of the patterns attainable by flip/rotates and returns the set.""" pat_set = set() tmp = grid[:, :] for rot in range(4): tmp = tmp.T[:, ::-1] # 90deg rotation pat_set.add(grid_to_pattern(tmp[:, :])) # Horizontal and vertical flips pat_set.add(grid_to_pattern(tmp[::-1, :])) pat_set.add(grid_to_pattern(tmp[:, ::-1])) return pat_set # Parse enhancement guide input_file = helper.read_day(21).strip().splitlines() guide = {} for line in input_file: inp, out = line.split(' => ') # Consider all rotations / flips in input equivs = enumerate_patterns(pattern_to_grid(inp)) for pat in equivs: guide[pat] = out # Enhance loop grid = pattern_to_grid(".#./..#/###") pixels_on = [] for step in range(18): # Split grid into subgrids out_subs = [] for sub in split_grid(grid):
import helper import re from collections import defaultdict, deque # Parse input as graph # Store graph as a dictionary of lists (adjacency list) graph = defaultdict(list) inp = helper.read_day(12).strip().splitlines() for line in inp: ids = list(map(int, re.findall("\w+", line))) for i in ids[1:]: graph[ids[0]].append(i) # Part 1 # How many programs are in the group with program ID 0? # Use Breadth-First Search, starting with node 0. def bfs(g, s): """Breadth-first search for graph g, starting at node s. Returns a set of traversed nodes.""" q = deque([s]) discovered = set([s]) while len(q) > 0: curr = q.popleft() for neigh in g[curr]: if neigh not in discovered: discovered.add(neigh) q.append(neigh) return discovered nodes = bfs(graph, 0)
"""Advent of Code 2017 Day 5: A Maze of Twisty Trampolines, All Alike""" import helper def jump(jumps, offset): """Given jump array and offset function, compute number of jumps.""" jumps = jumps.copy() # Copy to avoid modifying list idx = 0 # current location prev = 0 # previous location count = 0 # number of jumps so far while not (idx >= len(jumps) or idx < 0): prev = idx idx += jumps[idx] # Jump to new position count += 1 jumps[prev] += offset(jumps[prev]) # Shift previous position return count # Offset functions for part 1 & 2 offset_p1 = lambda offset: 1 offset_p2 = lambda offset: -1 if offset >= 3 else 1 assert jump([0,3,0,1,-3], offset_p1) == 5 assert jump([0,3,0,1,-3], offset_p2) == 10 # Try for actual input input_data = list(map(int, helper.read_day(5).splitlines())) print("Part 1:", jump(input_data, offset_p1)) print("Part 2:", jump(input_data, offset_p2))
- keep track of every point along the path for each wire - find set of points that intersect w1 and w2 - calculate manhatten distance for each and return lowest """ w1, w1_lowest_steps = get_points_on_wire_path(w1.split(",")) w2, w2_lowest_steps = get_points_on_wire_path(w2.split(",")) intersections = w1.intersection(w2) intersections.discard((0, 0)) # ignore output port # Part 1: sort all intersection points by Manhattan distance and return lowest closest_point = sorted(intersections, key=manhattan)[0] # Part 2: sort by lowest combined step count fewest_steps_point = sorted( intersections, key=lambda p: w1_lowest_steps[p] + w2_lowest_steps[p])[0] return (manhattan(closest_point), w1_lowest_steps[fewest_steps_point] + w2_lowest_steps[fewest_steps_point]) assert get_closest_and_nearest_intersections( "R75,D30,R83,U83,L12,D49,R71,U7,L72", "U62,R66,U55,R34,D71,R55,D58,R83") == (159, 610) assert get_closest_and_nearest_intersections( "R98,U47,R26,D63,R33,U87,L62,D20,R33,U53,R51", "U98,R91,D20,R16,D67,R40,U7,R15,U6,R7") == (135, 410) input_wires = helper.read_day(3).splitlines() output = get_closest_and_nearest_intersections(*input_wires) print("Part 1:", output[0]) print("Part 2:", output[1])
curr, skip = 0, 0 for i in range(64): lst, curr, skip = hash_round(lst, lengths, curr, skip) # Get dense hash by taking blocks of numbers dense = [] for i in range(16): xor = 0 for j in range(16): xor ^= lst[i * 16 + j] dense.append(xor) # Convert 16 numbers to hex string knothash = ''.join([hex(num)[2:].zfill(2) for num in dense]) return (knothash) assert hash_round([0, 1, 2, 3, 4], [3, 4, 1, 5])[0][0:2] == [3, 4] assert knot_hash("") == "a2582a3a0e66e6e86e3812dcb672a272" assert knot_hash("AoC 2017") == "33efeb34ea91902bb2f59c9920caa6cd" assert knot_hash("1,2,3") == "3efbe78a8d82f29979031a4aa0b16a9d" assert knot_hash("1,2,4") == "63960835bcdc130f0b66d7ff4f6a5a8e" if __name__ == "__main__": # Need to re-use the functions on day 14 lst = list(range(0, 256)) lengths = list(map(int, helper.read_day(10).strip().split(','))) part1 = hash_round(lst, lengths)[0] print("Part 1 solution:", part1[0] * part1[1]) input_string = helper.read_day(10).strip() print("Part 2 solution:", knot_hash(input_string))
import helper import math def calculate_fuel(mass): return math.floor(mass / 3) - 2 def calculate_fuel_recursive(mass): fuel = calculate_fuel(mass) if fuel > 0: return fuel + calculate_fuel_recursive(fuel) else: return 0 assert calculate_fuel(12) == 2 assert calculate_fuel(14) == 2 assert calculate_fuel(1969) == 654 assert calculate_fuel(100756) == 33583 input_masses = [int(mass) for mass in helper.read_day(1).splitlines()] print("Fuel requirements (1):", sum(calculate_fuel(m) for m in input_masses)) assert calculate_fuel_recursive(14) == 2 assert calculate_fuel_recursive(1969) == 966 assert calculate_fuel_recursive(100756) == 50346 print("Fuel requirements (2)", sum(calculate_fuel_recursive(m) for m in input_masses))
"""Advent of Code Day 18: Duet""" import helper import re from collections import defaultdict, deque input_data = helper.read_day(18).strip().splitlines() # Part 1 def map_value(registers, val): """Maps an argument in an instruction to a numeric value. Letters are converted to the values in their register, while numbers are treated normally (as integers).""" return int(val) if re.match("[a-z]", val) is None else int(registers[val]) def parse_instruction(registers, instruction, args, line_num): """Parses a line of the instructions, excluding snd/rcv (special cases). Returns a line number (to deal with jumps).""" # First argument is a register, second is a value ch = args[0] val = map_value(registers, args[1]) if instruction == "set": registers[ch] = val elif instruction == "add": registers[ch] += val elif instruction == "mul": registers[ch] *= val elif instruction == "mod":
import helper from collections import defaultdict def process_registers(instructs): """Processes register instructions. Returns tuple consisting of largest value in a register after completion (part 1) and highest value held at any point in time in a register (part 2).""" registers = defaultdict(int) # Default to zero highest = 0 OPS = {"inc": "+=", "dec": "-="} for line in instructs: key, op, opval, *cond = line.split() # Build an expression like "a += 1 if b >= 2 else 0" # (Pass registers into local namespace for expression) expr = [key, OPS[op], opval] + cond + ["else 0"] exec(' '.join(expr), None, registers) # Part 2 - Query highest value highest = max(registers[key], highest) return (max(registers.values()), highest) # Test Part 1 & 2 assert process_registers("""b inc 5 if a > 1 a inc 1 if b < 5 c dec -10 if a >= 1 c inc -20 if c == 10""".splitlines()) == (1, 10) instructs = helper.read_day(8).splitlines() print(process_registers(instructs))
meaning distance can be represented as: max(abs(n), abs(ne), abs(n+ne)) """ def steps_away(dirs): """Compute shortest number of steps away based on steps taken so far.""" n = dirs["n"] - dirs["s"] + dirs["nw"] - dirs["se"] ne = dirs["ne"] - dirs["sw"] - dirs["nw"] + dirs["se"] return (max(abs(n), abs(ne), abs(n + ne))) def hex_steps(path): """Count number of steps to return to origin given directions (part 1) and furthest number of steps (part 2).""" furthest = 0 dirs = {"n": 0, "nw": 0, "ne": 0, "sw": 0, "s": 0, "se": 0} for step in path: dirs[step] += 1 if steps_away(dirs) > furthest: furthest = steps_away(dirs) return (steps_away(dirs), furthest) # Test Part 1 assert hex_steps(["ne", "ne", "ne"])[0] == 3 assert hex_steps(["ne", "ne", "sw", "sw"])[0] == 0 assert hex_steps(["ne", "ne", "s", "s"])[0] == 2 assert hex_steps(["se", "sw", "se", "sw", "sw"])[0] == 3 data = helper.read_day(11).strip().split(',') print(hex_steps(data))
if move[0] == "s": # Spin spin = int(move[1:]) progs = progs[-spin:] + progs[:-spin] elif move[0] == "x": # Exchange a, b = map(int, move[1:].split("/")) progs[a], progs[b] = progs[b], progs[a] elif move[0] == "p": # Partner a, b = move[1:].split("/") ia, ib = progs.index(a), progs.index(b) progs[ia], progs[ib] = progs[ib], progs[ia] return ''.join(progs) assert dance("abcde", ["s1", "x3/4", "pe/b"]) == "baedc" progs = "abcdefghijklmnop" moves = helper.read_day(16).strip().split(",") progs = dance(progs, moves) print("Part 1:", progs) # Part 2 # We can't brute-force a billion answers: let's try to instead find # a recurring cycle. # Initialize list of "solutions" with part 1 answer. solutions = [progs] for i in range(1,1000000000): progs = dance(progs, moves) if progs in solutions: solutions.append(progs) break else:
import helper def count_cycles(banks): """Returns number of cycles needed and length of cycle.""" banks = banks.copy() N = len(banks) prev_configs = [] # Store previous cycle configurations count = 0 while banks not in prev_configs: prev_configs.append(banks.copy()) # Redistribute the blocks: # Get max value and its index (prioritizing lowest index first) # Note banks.index(max(banks)) works, but this needs to traverse twice idx, blocks = max([(i, v) for i, v in enumerate(banks)], key=lambda k: (k[1], N - k[0])) banks[idx] = 0 for i in range(1, blocks + 1): banks[(idx + i) % N] += 1 count += 1 return (count, count - prev_configs.index(banks)) assert count_cycles([0, 2, 7, 0]) == (5, 4) banks_input = list(map(int, helper.read_day(6).split())) output = count_cycles(banks_input) print("{} cycles completed with infinite loop length of {}.".format(*output))