hashed_integer += 1 hashed = md5() hashed.update((door_id + str(hashed_integer)).encode()) digest = hashed.hexdigest() # While the first 5 characters are not all 0s, increment the counter and generate a new hash while digest[:5] != "00000": hashed_integer += 1 hashed = md5() hashed.update((door_id + str(hashed_integer)).encode()) digest = hashed.hexdigest() code.append(digest[5]) # Print the password aoc.p1("".join(code)) ## Part 2 # Door ID which prefixes hashed value door_id = "reyedfim" # Starting value to hash hashed_integer = -1 values_found = 0 code = [None, None, None, None, None, None, None, None] while values_found < 8: hashed_integer += 1 hashed = md5()
for n in numbers_drawn: bingo_cards = [ [[b[r][c] if b[r][c] != n else None for c in range(5)] for r in range(5)] for b in bingo_cards ] finished_cards = list(filter(is_bingo, bingo_cards)) if finished_cards: last_drawn = n bingo = finished_cards[0] break unmarked = sum(filter(None, flatten(bingo))) aoc.p1(unmarked * last_drawn) # Part 2 bingo_cards = chunk(5, list(filter(None, data.numbers_by_line()[1:]))) for n in numbers_drawn: bingo_cards = [ [[b[r][c] if b[r][c] != n else None for c in range(5)] for r in range(5)] for b in bingo_cards ] unfinished_cards = list(filter(lambda x: not is_bingo(x), bingo_cards)) if len(bingo_cards) == 1 and len(unfinished_cards) == 0: last_drawn = n bingo = bingo_cards[0]
data = aoc.load() # Part 1 fish = data.nums() for _ in range(80): nfish = [] for f in fish: if f == 0: nfish.append(6) nfish.append(8) else: nfish.append(f - 1) fish = nfish aoc.p1(len(fish)) # Part 2 fish = [0] * 9 for f in data.nums(): fish[f] += 1 for _ in range(256): nfish = [0] * 9 for i, f in enumerate(fish[1:]): nfish[i] = f nfish[6] += fish[0] nfish[8] = fish[0] fish = nfish
flashed.add((x, y)) for adj in Position(x, y).adjacent(): if adj.tuple not in flashed: # Octo's can only flash once in a step, so skip if they've already flashed needs_increment.append(adj.tuple) clear_flashed(octos) return octos, len(flashed) # Part 1 total = 0 for _ in range(100): octos, flash_count = step(octos) total += flash_count aoc.p1(total) # Part 2 octos = data.digits_by_line() flash_count = 0 step_count = 0 while flash_count != 100: octos, flash_count = step(octos) step_count += 1 aoc.p2(step_count)
moving_water.append(under_left) else: spilled_water.add(under_left) under_right = under(right_edge) if not right_enclosed: if in_range(under_right) and (not moving_water or moving_water[-1] != under_right): moving_water.append(under_right) else: spilled_water.add(under_right) if left_enclosed and right_enclosed: moving_water.append(above(water)) aoc.p1(touched) ## Part 2 clay = set() left = right = 500 min_height = max_height = 1 for l in data.lines(): values = numbers_from(l) if l[0] == "x": x = values[0] left = min(left, x) right = max(right, x) min_height = min(min_height, values[1]) max_height = max(max_height, values[2])
def command_ship(ship, dir, ins, val): if ins in ["L", "R"]: return ship, turn_ship(dir, ins, val) elif ins == "F": return move_ship(ship, Direction[dir].position, val), dir else: return move_ship(ship, Direction[ins].position, val), dir ship, direction = Position(0, 0), "E" for instruction in data.parse_lines(r"(\w)(\d+)"): ship, direction = command_ship(ship, direction, instruction[0], int(instruction[1])) aoc.p1(abs(ship.x) + abs(ship.y)) # Part 2 def rotate_waypoint(waypoint, ins, val): while val >= 90: if ins == "R": waypoint = Position(-waypoint.y, waypoint.x) if ins == "L": waypoint = Position(waypoint.y, -waypoint.x) val -= 90 return waypoint def command_waypoint(ship, waypoint, ins, val):
visited = set([start]) while q: risk, pos = heappop(q) if pos == end: return risk for adj in pos.adjacent(diagonal=False): if adj not in visited: new_risk = risk + grid[adj.y][adj.x] visited.add(adj) heappush(q, (new_risk, adj)) base_grid = data.digits_by_line() aoc.p1(least_risk_path(base_grid)) full_grid = [[0 for _ in range(len(base_grid[0]) * 5)] for _ in range(len(base_grid) * 5)] # Generate first row for full_grid for y, row in enumerate(base_grid): for i in range(5): for x, value in enumerate(row): full_grid[y][x + i * len(row)] = (value + i - 1) % 9 + 1 # Copy first X rows to rest of full_grid for i in range(5): for y, row in enumerate(full_grid[:len(base_grid)]): for x, value in enumerate(row): full_grid[y + i * len(base_grid)][x] = (value + i - 1) % 9 + 1
def resolve_rule(r): @lru_cache def resolver(r): if type(rules[r][0]) is str: return rules[r][0] else: return ( "(" + "|".join(["".join([resolver(y) for y in x]) for x in rules[r]]) + ")") return "^" + resolver(r) + "$" rule_zero = resolve_rule(0) aoc.p1(len([1 for x in chunks[1] if re.match(rule_zero, x)])) # Part 2 rules[8] = [[42], [42, 8]] rules[11] = [[42, 31], [42, 11, 31]] def resolve_infinite_rule(r): @lru_cache def manual_resolution(r): if r == 8: return "(" + resolver(42) + ")" + "+" elif r == 11: return ("(" + "|".join( [resolver(42) * i + resolver(31) * i
from aoc import AOC import math from itertools import chain aoc = AOC(year=2020, day=1) data = aoc.load() # Part 1 expenses = set(data.numbers()) product = math.prod([e for e in expenses if (2020 - e) in expenses]) aoc.p1(product) # Part 2 product = math.prod( set( chain(*[[e for e in expenses if (2020 - f - e) in expenses] for f in expenses]))) aoc.p2(product)
# Apply the process 40 times for i in range(40): puzzle_output = "" # Get character repeated at start, number of times its repeated and add to output while len(puzzle_input) > 0: digits = re.search(r"(\d)\1*", puzzle_input) puzzle_input = puzzle_input[len(digits.group(0)):] puzzle_output = (puzzle_output + str(len(digits.group(0))) + str(digits.group(0)[:1])) # Update input to iterate puzzle_input = puzzle_output aoc.p1(len(puzzle_input)) ## Part 2 # Input from the site puzzle_input = "1113122113" # Apply the process 50 times for i in range(50): puzzle_output = "" # Get character repeated at start, number of times its repeated and add to output while len(puzzle_input) > 0: digits = re.search(r"(\d)\1*", puzzle_input) puzzle_input = puzzle_input[len(digits.group(0)):] puzzle_output = (puzzle_output + str(len(digits.group(0))) +
aoc = AOC(year=2015, day=5) data = aoc.load() ## Part 1 # Create regex to match the rules # 1. Contains at least one pair of two letters that appears twice (non-overlapping) # 2. At least one letter that repeats, with one letter between them nicestring_regex = re.compile( r"^(?=\w*(\w)\w\1\w*)(\w*(\w\w)\w*\3\w*)$", flags=re.MULTILINE ) total_nicestrings = len(re.findall(nicestring_regex, data.contents())) # Print the total number of nice strings aoc.p1(total_nicestrings) ## Part 2 # Create regex to match the rules # 1. Contains at least 3 values # 2. At least one letter that appears twice in a row # 3. Does not contain 'ab', 'cd', 'pq', or 'xy' nicestring_regex = re.compile( r"^(?=\w*(\w)\1)(?!\w*(ab|cd|pq|xy))((\w*[aeiou]\w*){3,})$", flags=re.MULTILINE ) total_nicestrings = len(re.findall(nicestring_regex, data.contents())) # Print the total number of nice strings aoc.p2(total_nicestrings)
aoc = AOC(year=2021, day=2) data = aoc.load() # Part 1 x, y = 0, 0 for command, value in data.parse(r"(\w+) (\d+)"): if command == "forward": x += value if command == "down": y += value if command == "up": y -= value aoc.p1(x * y) # Part 2 x, y, aim = 0, 0, 0 for command, value in data.parse(r"(\w+) (\d+)"): if command == "forward": x += value y += aim * value if command == "down": aim += value if command == "up": aim -= value aoc.p2(x * y)
for b in bitstrings: bits[int(b[position])] += 1 return 0 if bits[0] > bits[1] else 1 gamma = "" for position in range(data.line_length): gamma += str(most_common_bit(data.lines(), position)) epsilon = "".join(["0" if g == "1" else "1" for g in gamma]) gamma = int(gamma, 2) epsilon = int(epsilon, 2) aoc.p1(gamma * epsilon) # Part 2 def least_common_bit(bitstrings, position): bits = {0: 0, 1: 0} for b in bitstrings: bits[int(b[position])] += 1 return 1 if bits[1] < bits[0] else 0 def reduce(bitstrings, position, bit_extractor): reduced = [] comparator = str(bit_extractor(bitstrings, position))
# Part 1 cups = get_cups(False) lowest = min(cups.keys()) highest = max(cups.keys()) current = next(iter(cups.keys())) for _ in range(100): step() current = cups[1]["n"] labels = [] while current != 1: labels.append(str(cups[current]["v"])) current = cups[current]["n"] aoc.p1("".join(labels)) # Part 2 cups = get_cups(True) lowest = min(cups.keys()) highest = max(cups.keys()) current = next(iter(cups.keys())) for _ in range(10_000_000): step() aoc.p2(cups[1]["n"] * cups[cups[1]["n"]]["n"])
from aoc import AOC, chinese_remainder import itertools import re aoc = AOC(year=2020, day=13) contents = aoc.load().lines() # Part 1 start_time = int(contents[0]) buses = [int(v) for v in re.findall(r"\d+", contents[1])] for i in itertools.count(start_time): departing_bus = next((bid for bid in buses if (start_time + i) % bid == 0), False) if departing_bus: aoc.p1(departing_bus * i) break # Part 2 buses = [int(b) if b != "x" else b for b in contents[1].split(",")] n = [bid for bid in buses if bid != "x"] a = [0] + [bid - (idx + 1) for idx, bid in enumerate(buses[1:]) if bid != "x"] aoc.p2(chinese_remainder(n, a))
# Part 1 comp = Computer(data) def run_until_finished(comp): seen = set() while comp.position not in seen and not comp.is_finished(): seen.add(comp.position) comp.step() return comp.is_finished() run_until_finished(comp) aoc.p1(comp.accumulator) # Part 2 for idx, ins in enumerate(comp.instructions()): modified_comp = Computer(data) if ins[0] == "jmp": modified_comp.replace(idx, "nop") elif ins[0] == "nop": modified_comp.replace(idx, "jmp") if run_until_finished(modified_comp): break aoc.p2(modified_comp.accumulator)
code = [] for line in data.lines(): for char in line: if char == "L": start = (start[0], max(start[1] - 1, 0)) if char == "U": start = (max(start[0] - 1, 0), start[1]) if char == "R": start = (start[0], min(start[1] + 1, 2)) if char == "D": start = (min(start[0] + 1, 2), start[1]) code.append(layout[start[0]][start[1]]) aoc.p1("".join([str(c) for c in code])) ## Part 2 # Keypad layout layout = [ [-1, -1, 1, -1, -1], [-1, 2, 3, 4, -1], [5, 6, 7, 8, 9], [-1, "A", "B", "C", -1], [-1, -1, "D", -1, -1], ] # Initial position (number 5) start = (2, 0) code = []
updated_points[new_point] = [(xx, yy)] points = updated_points seconds += 1 count -= 1 if count == 0: count = 10 ys = [x[0] for x in points.keys()] top, height = min(ys), max(ys) did_print = should_print should_print = abs(top - height) < 100 if not should_print and did_print != should_print: break aoc.p1("CRXKEZPZ") ## Part 2 points = {} for l in data.numbers_by_line(): x, y, xx, yy = l points[(x, y)] = [(xx, yy)] xs = [x[0] for x in points.keys()] ys = [x[0] for x in points.keys()] left, width = min(xs), max(xs) top, height = min(ys), max(ys)
from math import prod from aoc import AOC, mins aoc = AOC(year=2015, day=2) data = aoc.load() ## Part 1 total_square_feet = 0 for sides in data.numbers_by_line(): first = sides[0] * sides[1] second = sides[1] * sides[2] third = sides[2] * sides[0] total_square_feet += 2 * (first + second + third) + min( first, second, third) aoc.p1(total_square_feet) ## Part 2 # Initialize to 0 feet of ribbon total_length = 0 for sides in data.numbers_by_line(): total_length += sides[0] * sides[1] * sides[2] + (2 * sum(mins(sides, 2))) aoc.p2(total_length)
# Start with checking 1 lowest_positive_int = 1 hashed = md5() hashed.update(("ckczppom" + str(lowest_positive_int)).encode()) digest = hashed.hexdigest() # While the first 5 characters are not all 0s, increment the counter and generate a new hash while digest[:5] != "00000": lowest_positive_int += 1 hashed = md5() hashed.update((PUZZLE_INPUT + str(lowest_positive_int)).encode()) digest = hashed.hexdigest() # Print the lowest valid positive integer aoc.p1(lowest_positive_int) ## Part 2 # Start with checking 1 lowest_positive_int = 1 hashed = md5() hashed.update(("ckczppom" + str(lowest_positive_int)).encode()) digest = hashed.hexdigest() # While the first 6 characters are not all 0s, increment the counter and generate a new hash while digest[:6] != "000000": lowest_positive_int += 1 hashed = md5() hashed.update((PUZZLE_INPUT + str(lowest_positive_int)).encode()) digest = hashed.hexdigest()
def play_round(): p1 = first_hand.pop(0) p2 = second_hand.pop(0) if p1 > p2: first_hand.append(p1) first_hand.append(p2) else: second_hand.append(p2) second_hand.append(p1) while first_hand and second_hand: play_round() aoc.p1(calculate_score(first_hand if first_hand else second_hand)) # Part 2 first_hand = hands[0][:] second_hand = hands[1][:] def play_game(first_hand, second_hand): previous_deck_orders = set() while first_hand and second_hand: tuple_first = tuple(first_hand) tuple_second = tuple(second_hand) if (tuple_first, tuple_second) in previous_deck_orders: return 1 previous_deck_orders.add((tuple_first, tuple_second))
aoc = AOC(year=2020, day=4) data = aoc.load() passport_properties = set(["byr", "iyr", "eyr", "hgt", "hcl", "ecl", "pid"]) passports = [" ".join(c.splitlines()) for c in data.contents().split("\n\n")] passports = [dict(x.split(":") for x in p.split(" ")) for p in passports] # Part 1 def passes_basic_validation(passport): return (set(passport.keys()) - set(["cid"])) == passport_properties aoc.p1(len([p for p in passports if passes_basic_validation(p)])) # Part 2 validations = [ lambda p: passes_basic_validation(p), lambda p: 1920 <= int(p["byr"]) <= 2002, lambda p: 2010 <= int(p["iyr"]) <= 2020, lambda p: 2020 <= int(p["eyr"]) <= 2030, lambda p: (p["hgt"][3:] == "cm" and 150 <= int(p["hgt"][0:3]) <= 193) or (p["hgt"][2:] == "in" and 59 <= int(p["hgt"][0:2]) <= 76), lambda p: re.search(r"^#[0-9a-fA-F]{6}$", p["hcl"]), lambda p: p["ecl"] in ["amb", "blu", "brn", "gry", "grn", "hzl", "oth"], lambda p: re.search(r"^[0-9]{9}$", p["pid"]), ]
"children": children, "metadata": metadata, } root_node = build_node(data.numbers_by_line()[0]) def sum_metadata(node): total = sum(node["metadata"]) for child in node["children"]: total += sum_metadata(child) return total aoc.p1(sum_metadata(root_node)) ## Part 2 def build_node(raw_node): header = { "children": raw_node[0], "metadata": raw_node[1], } node_length = 2 children = [] for _ in range(header["children"]): child = build_node(raw_node[node_length:-header["metadata"]]) node_length += child["length"]
next_battle.player_turn(spell_names[index]) if next_battle.check_game_over(): if next_battle.boss.hp <= 0: minimum_mana = ( min(minimum_mana, next_battle.total_cost) if minimum_mana != -1 else next_battle.total_cost ) continue next_battle.boss_turn() battles.append(next_battle) aoc.p1(minimum_mana) ## Part 2 # Initial stats of the player and the boss boss_base_stats = {"hp": 58, "damage": 9} player_base_stats = {"hp": 50, "mana": 500} hard_mode_enabled = True class Player: def __init__(self): self.hp = player_base_stats["hp"] self.mana = player_base_stats["mana"]
aoc = AOC(year=2020, day=25) data = aoc.load() public_keys = [int(d) for d in data.lines()] subs = [7, 7] loop_sizes = [] def transform(sub, loop_size): value = 1 for _ in range(loop_size): value = single_transform(value, sub) return value def single_transform(value, sub): value = value * sub return value % 20201227 for sub, key in zip(subs, public_keys): value = 1 loops = 0 while value != key: value = single_transform(value, sub) loops += 1 loop_sizes.append(loops) aoc.p1(transform(public_keys[0], loop_sizes[1]))
best = max(get_best_score(updated_amounts, total_used + c), best) return best return 0 # Pick an ingredient to start and recursively get the best score best_score = 0 for item in ingredients: for count in range(101): score = get_best_score({item: count}, count) if score > best_score: best_score = score break aoc.p1(best_score) ## Part 2 # Array indices capacity = 0 durability = 1 flavor = 2 texture = 3 calorie = 4 # Regular expression to extract information about the ingredients regex_ingredients = re.compile( r"(\w+): capacity ([-]?\d+), durability ([-]?\d+), flavor ([-]?\d+), texture ([-]?\d+), calories ([-]?\d+)" ) ingredients = {}
range(int(m[3]), int(m[4]) + 1)] for m in chunks[0] } # Part 1 all_rule_values = flatten(rules.values()) def is_valid_for_some_field(v): return any(v in x for x in all_rule_values) aoc.p1( sum( sum(0 if is_valid_for_some_field(v) else v for v in t) for t in tickets)) # Part 2 tickets = [t for t in tickets if all(is_valid_for_some_field(v) for v in t)] field_indices = {r: set(range(len(tickets[0]))) for r in rules.keys()} confirmed_fields = set() for t in tickets: for i, v in enumerate(t): for r in rules: if not any(v in x for x in rules[r]): field_indices[r].remove(i) while True:
hill = [list(line) for line in data.lines()] # Part 1 def count_trees(slope): position = Position(0, 0) trees = 0 while position.y < len(hill): if hill[position.y][position.x % len(hill[0])] == "#": trees += 1 position = Position(position.x + slope.x, position.y + slope.y) return trees aoc.p1(count_trees(Position(3, 1))) # Part 2 aoc.p2( math.prod( [ count_trees(slope) for slope in [ Position(1, 1), Position(3, 1), Position(5, 1), Position(7, 1), Position(1, 2), ] ]
elif containers_total + remaining_containers[i] > target_eggnog: break # If the total is too small, keep iterating over the remaining containers elif i > 0: arrangements += get_arrangements( containers_total + remaining_containers[i], remaining_containers[:i]) # Return the total number of arrangements made from this iteration return arrangements # Gets the number of total container arrangements total_arrangements = get_arrangements(0, PUZZLE_INPUT) aoc.p1(total_arrangements) ## Part 2 # Initialize and sort the puzzle input PUZZLE_INPUT = [ 33, 14, 18, 20, 45, 35, 16, 35, 1, 13,
from aoc import AOC aoc = AOC(year=2018, day=23) data = aoc.load() ## Part 1 nanobots = [((vals[0], vals[1], vals[2]), vals[3]) for vals in data.numbers_by_line()] nanobots.sort(key=lambda tup: tup[1], reverse=True) strongest = nanobots[0] def manhattan(first, second): return (abs(first[0][0] - second[0][0]) + abs(first[0][1] - second[0][1]) + abs(first[0][2] - second[0][2])) in_range = sum( [1 if manhattan(x, strongest) <= strongest[1] else 0 for x in nanobots]) aoc.p1(in_range)