from aoc import AOC aoc = AOC(year=2020, day=23) data = aoc.load() def get_cups(extended): cups = [int(c) for c in data.contents().strip()] if extended: cups.extend([x for x in range(max(cups), 1_000_001)]) cups = { c: {"v": c, "n": cups[i + 1] if i + 1 < len(cups) else cups[0]} for i, c in enumerate(cups) } return cups def step(): global current, cups head = cups[current] picked_up = ( cups[head["n"]]["v"], cups[cups[head["n"]]["n"]]["v"], cups[cups[cups[head["n"]]["n"]]["n"]]["v"], ) head["n"] = cups[cups[cups[cups[head["n"]]["n"]]["n"]]["n"]]["v"] destination = head["v"] - 1 if head["v"] > lowest else highest while destination in picked_up: destination = destination - 1 if destination > lowest else highest
from aoc import AOC, chunk, flatten, transpose aoc = AOC(year=2021, day=4) data = aoc.load() finished_line = [None] * 5 numbers_drawn = data.numbers_by_line()[0] bingo_cards = chunk(5, list(filter(None, data.numbers_by_line()[1:]))) # Part 1 # Check if a card has any finished rows or columns def is_bingo(card): cols = transpose(card) return any(row == finished_line for row in card) or any( col == finished_line for col in cols ) 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
from aoc import AOC aoc = AOC(year=2021, day=6) 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
from aoc import AOC, Position, griditer aoc = AOC(year=2021, day=11) data = aoc.load() Position.set_limits(x=range(10), y=range(10)) octos = data.digits_by_line() def clear_flashed(octos): # Reset octos that have flashed to 0 for x, y in griditer(octos): if octos[y][x] > 9: octos[y][x] = 0 def step(octos): # Start with incremeneting every octo needs_increment = [(x, y) for x, y in griditer(octos)] flashed = set() while needs_increment: x, y = needs_increment.pop() octos[y][x] += 1 if octos[y][x] > 9 and (x, y) not in flashed: # When an octo's energy exceeds 9, it flashes and increments those surrounding 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)
from aoc import AOC, numbers_from aoc = AOC(year=2018, day=17) data = aoc.load() ## Part 1 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]) for y in range(values[1], values[2] + 1): clay.add((x, y)) else: y = values[0] left = min(left, values[1]) right = max(right, values[2]) min_height = min(min_height, y) max_height = max(max_height, y) for x in range(values[1], values[2] + 1): clay.add((x, y)) left = left - 1
from aoc import AOC, Position, Direction aoc = AOC(year=2020, day=12) data = aoc.load() # Part 1 def turn_ship(dir, ins, val): directions = [d.name for d in Direction] directions = list(reversed(directions)) if ins == "L" else directions rotations = directions.index(dir) return (directions[rotations:] + directions[:rotations])[val // 90] def move_ship(ship, offset, val): return Position(ship.x + offset.x * val, ship.y + offset.y * val) 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],
from aoc import AOC, Position from heapq import heappush, heappop aoc = AOC(year=2021, day=15) data = aoc.load() def least_risk_path(grid): Position.set_limits(range(len(grid[0])), range(len(grid))) start = Position(0, 0) end = Position(len(grid[0]) - 1, len(grid) - 1) q = [(0, start)] 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))
from aoc import AOC, Regex, Drop, String import re from functools import lru_cache aoc = AOC(year=2020, day=19) data = aoc.load() chunks = data.chunk([ Regex(r"^(\d.*)$"), Drop(1), String(), ]) rules = { int(m[0][:m[0].find(":")]): [ s[1] if '"' in s else [int(x) for x in s.split(" ")] for s in m[0][m[0].find(" ") + 1:].split(" | ") ] for m in chunks[0] } """ Given input: 0: 1 2 1: "a" 2: 1 3 | 3 1 3: "b" `rules` will be: { 0: [[1, 2]], 1: ['a'],
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)
from aoc import AOC import re ## Part 1 aoc = AOC(year=2015, day=10) # Input from the site puzzle_input = "1113122113" # 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"
from aoc import AOC import re 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
from aoc import AOC 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)
from aoc import AOC aoc = AOC(year=2021, day=3) data = aoc.load() # Part 1 def most_common_bit(bitstrings, position): bits = {0: 0, 1: 0} 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):
from aoc import AOC def get_solution(inp: str): mass_data = [int(item) for item in inp.split('\n')] fuel_data = [] for mass in mass_data: # fuel required for the module fuel = get_fuel(mass) # fuel required for the fuel itself while fuel > 0: fuel_data.append(fuel) fuel = get_fuel(fuel) total_fuel = sum(fuel_data) return total_fuel def get_fuel(mass: int): return mass // 3 - 2 aoc_data = AOC.get_data(1) result = get_solution(aoc_data) print(result) # AOC.submit(data=result, part='b', day=1)
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))
from aoc import AOC, Computer aoc = AOC(year=2020, day=8) data = aoc.load() # 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):
from aoc import AOC aoc = AOC(year=2016, day=2) data = aoc.load() ## Part 1 # Keypad layout layout = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] # Initial position (number 5) start = (1, 1) 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
from aoc import AOC aoc = AOC(year=2018, day=10) data = aoc.load() ## Part 1 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) def print_lights(lights): pass # pxs = [x[0] for x in lights.keys()] # pys = [x[0] for x in lights.keys()] # pleft, pwidth = min(pxs), max(pxs) # ptop, pheight = min(pys), max(pys) # for py in range(ptop, pheight + 1): # for px in range(pleft, pwidth + 1): # if (px, py) in lights: # print('#', end='')
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)
from aoc import AOC from hashlib import md5 aoc = AOC(year=2015, day=4) ## Part 1 # Input from the site PUZZLE_INPUT = "ckczppom" # 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
from aoc import AOC, Drop, Numbers aoc = AOC(year=2020, day=22) data = aoc.load() hands = data.chunk([ Drop(1), Numbers(), Drop(2), Numbers(), ]) first_hand = hands[0][:] second_hand = hands[1][:] def calculate_score(hand): return sum([(i + 1) * h for i, h in enumerate(reversed(hand))]) # Part 1 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:
from aoc import AOC import re 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"],
from aoc import AOC aoc = AOC(year=2018, day=8) data = aoc.load() ## Part 1 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"] children.append(child) metadata = raw_node[node_length:node_length + header["metadata"]] node_length += header["metadata"] return { "header": header, "length": node_length, "children": children, "metadata": metadata, }
from aoc import AOC aoc = AOC(year=2015, day=22) ## Part 1 # 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 = False class Player: def __init__(self): self.hp = player_base_stats["hp"] self.mana = player_base_stats["mana"] class Boss: def __init__(self): self.hp = boss_base_stats["hp"] self.damage = boss_base_stats["damage"] class Battle: def __init__(self): self.player = Player() self.boss = Boss() self.turns = [] self.poison_turns = 0 self.shield_turns = 0
from aoc import AOC 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)
from aoc import AOC import re aoc = AOC(year=2015, day=15) data = aoc.load() ## Part 1 # Array indices capacity = 0 durability = 1 flavor = 2 texture = 3 # Regular expression to extract information about the ingredients regex_ingredients = re.compile( r"(\w+): capacity ([-]?\d+), durability ([-]?\d+), flavor ([-]?\d+), texture ([-]?\d+)" ) ingredients = {} # For each line in the input for line in data.lines(): # Add the ingredient to the list stats = re.search(regex_ingredients, line) ingredients[stats.group(1)] = [ int(stats.group(2)), int(stats.group(3)), int(stats.group(4)), int(stats.group(5)), ]
from aoc import AOC, flatten, Regex, Drop, Numbers from math import prod aoc = AOC(year=2020, day=16) data = aoc.load() chunks = data.chunk([ Regex(r"^(.*): (\d+)-(\d+) or (\d+)-(\d+)"), Drop(2), Numbers(1), Drop(2), Numbers(), ]) your_ticket = chunks[1][0] tickets = chunks[2] rules = { m[0]: [range(int(m[1]), int(m[2]) + 1), 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)
from aoc import AOC, Position import math aoc = AOC(year=2020, day=3) data = aoc.load() 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),
from aoc import AOC aoc = AOC(year=2015, day=17) ## Part 1 # Initialize and sort the puzzle input PUZZLE_INPUT = [ 33, 14, 18, 20, 45, 35, 16, 35, 1, 13, 18, 13, 50, 44, 48, 6, 24, 41, 30, 42, ] container_sizes = PUZZLE_INPUT container_sizes.sort(reverse=True)
from aoc import AOC from hashlib import md5 aoc = AOC(year=2016, day=5) ## Part 1 # Door ID which prefixes hashed value door_id = "reyedfim" # Starting value to hash hashed_integer = -1 code = [] while len(code) < 8: 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