from functools import reduce from typing import Callable from aoc import utils inputdata = utils.get_day_data(16, test_data=False) TYPES: dict[int, Callable[..., int]] = { 0: sum, 1: lambda *args: reduce(lambda x, y: x * y, *args, 1), # type: ignore 2: min, 3: max, 5: lambda x: int(x[0] > x[0]), 6: lambda x: int(x[0] < x[1]), 7: lambda x: int(x[0] == x[1]), } VERSION_NUMBERS = [] def get_bin_data() -> str: data = f"{int(inputdata[0], 16):08b}" data = ("0" * (4 - num) + data) if (num := len(data) % 4) else data return data def literal_value(packet: str) -> tuple[int, str]: value = "" while packet[0] == "1": value += packet[1:5] packet = packet[5:]
from itertools import permutations from typing import Union from aoc import utils inputdata = list(map(int, utils.get_day_data(9))) PREAMBLE = 25 def get_invalid() -> int: for i in range(PREAMBLE, len(inputdata)): solutions = [ a + b for (a, b) in permutations(inputdata[i - PREAMBLE : i], 2) ] if inputdata[i] not in solutions: return int(inputdata[i]) return 0 def find_contiguous( start: int, check: int ) -> Union[list[int], bool]: # noqa pylint: disable=unsubscriptable-object total = 0 for i in range(start, len(inputdata)): total += inputdata[i] if total == check: return inputdata[start : (i + 1)] elif total > check: return False
from collections import Counter from aoc import utils inputlist = utils.get_day_data(6, split_delimiter="\n\n") def part1() -> int: total: int = 0 for group in [x.replace("\n", "") for x in inputlist]: total += len(Counter(group).keys()) return total def part2() -> int: total: int = 0 for group in inputlist: letter_counter = Counter(group) people_count = len(group.split("\n")) answered_by_each_person = [ x == people_count for x in letter_counter.values() ] total += sum(answered_by_each_person) return total utils.print_result(f"Part 1 answer: {part1()}") utils.print_result(f"Part 2 answer: {part2()}")
from aoc import utils inputdata = utils.get_day_data(2) # mypy doesn't yet support match statements :-( # https://github.com/python/mypy/pull/10191 def part1() -> int: horizontal, depth = 0, 0 for course_command in inputdata: direction, str_amount = course_command.split(" ") amount = int(str_amount) if direction == "forward": horizontal += amount elif direction == "down": depth += amount else: depth -= amount return horizontal * depth def part2() -> int: horizontal, depth, aim = 0, 0, 0 for course_command in inputdata: direction, str_amount = course_command.split(" ") amount = int(str_amount) if direction == "forward":
from __future__ import annotations import json import math from copy import deepcopy from itertools import permutations from typing import Any from typing import Optional from aoc import utils inputdata: list[Any] = utils.get_day_data(18, test_data=False, line_format_func=json.loads) class SnailfishNumber: left: Optional[SnailfishNumber] = None right: Optional[SnailfishNumber] = None value: Optional[int] = None parent: Optional[SnailfishNumber] def __init__(self, value: Any, parent: SnailfishNumber | None = None) -> None: self.parent = parent if isinstance(value, list): self.left = SnailfishNumber(value[0], self) self.right = SnailfishNumber(value[1], self) self.value = None else:
import numpy as np from aoc import utils input_data = utils.get_day_data(9, test_data=False) heightmap = np.array([list(line) for line in input_data], dtype=int) low_adj = [] low_points = [] def part1() -> int: for i in range(heightmap.shape[0]): for j in range(heightmap.shape[1]): if get_window_max([i, j], heightmap): low_adj.append(heightmap[i][j] + 1) low_points.append([i, j]) return sum(low_adj) def part2() -> int: basins = [] for loc_min in low_points: data = heightmap.copy() flood_fill(data, loc_min[0], loc_min[1]) basin = data == 10 basin_size = len(data[basin]) basins.append(basin_size) top_3_basins = sorted(basins)[-3:]
import numpy as np from aoc import utils inputdata = utils.get_day_data(7, test_data=False, split_delimiter=",") def part1() -> int: crabs = list(map(int, inputdata.copy())) median = np.round(np.median(crabs)) return int(sum([np.abs(median - x) for x in crabs])) def part2() -> int: data = list(map(int, inputdata.copy())) mean = int(np.mean(data)) return int( sum(np.abs(x - mean) * (np.abs(x - mean) + 1) // 2 for x in data)) utils.print_result(f"Part 1 answer: {part1()}") utils.print_result(f"Part 2 answer: {part2()}")
from itertools import permutations from aoc import utils inputdata = utils.get_day_data( 8, test_data=False, line_format_func=lambda x: x.split(" | ") ) combinations: dict[str, int] = { "abcefg": 0, "cf": 1, "acdeg": 2, "acdfg": 3, "bcdf": 4, "abdfg": 5, "abdefg": 6, "acf": 7, "abcdefg": 8, "abcdfg": 9, } def part1() -> int: data = inputdata.copy() return_no = 0 for line in data: _, output_data = line[0].split(" "), line[1].split(" ") return_no += sum(len(code) in [2, 3, 4, 7] for code in output_data)
def format_input(input_data: list[str]) -> list[tuple[int, int, str, str]]: outlist: list[tuple[int, int, str, str]] = [] part_re = re.compile("([0-9]+)-([0-9]+) ([a-zA-Z]): ([a-zA-Z]+)") for line in input_data: split = part_re.match(line) if split: outlist.append(( int(split.group(1)), int(split.group(2)), str(split.group(3)), str(split.group(4)), )) return outlist input_list: list[str] = utils.get_day_data(2) formatted_input = format_input(input_list) def part1() -> int: valid_passwords: int = 0 for tup in formatted_input: password_min, password_max, password_char, password = tup charcount: int = password.count(password_char) if password_max >= charcount >= password_min: valid_passwords += 1 return valid_passwords def part2() -> int:
from re import sub from typing import Union import numpy as np from aoc import utils inputdata = utils.get_day_data(4, split_delimiter="\n") def part1() -> int: drawn_numbers, cards = process_and_get_input() for drawn in drawn_numbers: cards = np.where(cards == drawn, np.nan, cards) states = np.array([does_board_have_a_winner(board) for board in cards]) if np.any(states): return int(np.nansum(cards[np.argmax(states)]) * drawn) return 0 def part2() -> int: drawn_numbers, cards = process_and_get_input() last = -1 for drawn in drawn_numbers: cards = np.where(cards == drawn, np.nan, cards) states = np.array([does_board_have_a_winner(board) for board in cards]) if np.count_nonzero(~states) == 1: last = int(np.argmin(states))
"iyr": (lambda n: int(n) and len(n) == 4 and 2010 <= int(n) <= 2020), "eyr": (lambda n: int(n) and len(n) == 4 and 2020 <= int(n) <= 2030), "hgt": ( lambda n: int(n[:-2]) and n[-2:] in ["cm", "in"] and ( (n[-2:] == "cm" and 150 <= int(n[:-2]) <= 193) or (n[-2:] == "in" and 59 <= int(n[:-2]) <= 76) ) ), "hcl": (lambda n: len(n) == 7 and match("#[0-9a-f]{6}", n)), "ecl": (lambda n: n in ["amb", "blu", "brn", "gry", "grn", "hzl", "oth"]), "pid": (lambda n: int(n) and len(n) == 9), } input_as_list = utils.get_day_data(4, split_delimiter="\n\n") def get_passports_dict_from_list() -> list[dict[str, str]]: passport_list: list[dict[str, str]] = [] for line in input_as_list: line = line.replace("\n", " ").replace(" ", ",") sections = line.split(",") keyval = [section.split(":") for section in sections] passport_list.append({key: value for (key, value) in keyval}) return passport_list def has_all_required_fields(passport: dict[str, str]) -> bool: return all(field in passport for field in REQUIRED_FIELDS)
import re from aoc import utils BAG_RULE = re.compile(r"(\d+) ([\w ]+) bag") BAG_COLOR = "shiny gold" inputlist: list[str] = utils.get_day_data(7) def get_tree(data: list[str]) -> dict[str, list[tuple[int, str]]]: rules = {} for line in data: color, contents = line.split(" bags contain ") bag_content = BAG_RULE.findall(contents) rules[color] = [(int(n), inner) for n, inner in bag_content] return rules def count_bags_of(target, tree) -> int: count = 0 for color in tree: if has_bag(color, target, tree): count += 1 return count def has_bag(color, target, tree) -> bool: contents = tree[color]
from aoc import utils input_list: list[int] = utils.convert_list_to_ints(utils.get_day_data(1)) def part1() -> int: for i in input_list: for j in input_list: if i + j == 2020: return i * j return 0 def part2() -> int: for i in input_list: for j in input_list: for k in input_list: if i + j + k == 2020: return i * j * k return 0 utils.print_result(f"Part 1 answer: {part1()}") utils.print_result(f"Part 2 answer: {part2()}")
) for line in file.read().split("\n") ] def format_input_to_seat_bin_list(data: list[str]) -> list[int]: return [ int( seat.replace("F", "0").replace("B", "1").replace("L", "0").replace("R", "1"), base=2, ) for seat in data ] seat_list_raw: list[str] = utils.get_day_data(5) seat_list: list[int] = format_input_to_seat_bin_list(seat_list_raw) def part1() -> int: return max(seat_list) def part2() -> int: seat = 0 for i in range(min(seat_list), max(seat_list)): if (i not in seat_list and (i - 1) in seat_list and (i + 1) in seat_list): seat = i break
from dataclasses import dataclass from types import SimpleNamespace import numpy as np from aoc import utils inputdata = utils.get_day_data(5, line_format_func=lambda x: x.split(" -> ")) @dataclass class Vent: x_1: int y_1: int x_2: int y_2: int @property def is_horizontal(self) -> bool: return self.y_1 == self.y_2 @property def is_vertical(self) -> bool: return self.x_1 == self.x_2 def line_coords(self): vec = np.sign([self.x_2 - self.x_1, self.y_2 - self.y_1]) coords = SimpleNamespace(**{"x": [self.x_1], "y": [self.y_1]}) while coords.x[-1] != self.x_2 or coords.y[-1] != self.y_2: coords.x.append(coords.x[-1] + vec[0])