示例#1
0
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
示例#2
0
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
示例#3
0
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
示例#4
0
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)
示例#5
0
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
示例#6
0
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],
示例#7
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))
示例#8
0
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'],
示例#9
0
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)
示例#10
0
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"
示例#11
0
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
示例#12
0
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)
示例#13
0
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):
示例#14
0
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)
示例#15
0
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))
示例#16
0
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):
示例#17
0
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
示例#18
0
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='')
示例#19
0
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)
示例#20
0
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
示例#21
0
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:
示例#22
0
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"],
示例#23
0
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,
    }
示例#24
0
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
示例#25
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)
示例#26
0
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)),
    ]
示例#27
0
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)
示例#28
0
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),
示例#29
0
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)
示例#30
0
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