Ejemplo n.º 1
0
    visited.append(person)

    # If all of the people are in the list, add the total change in happiness to the possibilities
    if len(visited) == len(happiness):
        total_so_far += (happiness[first_person][person] +
                         happiness[person][first_person])
        possibilities.append(total_so_far)

    # For each person the person can sit beside
    for neighbor in happiness[person]:
        # If they're already in the list, skip them
        if neighbor in visited:
            continue

        # Get all the possibilities of the next person's neighbor
        calc_possibilities(
            first_person,
            neighbor,
            visited,
            total_so_far + happiness[neighbor][person] +
            happiness[person][neighbor],
        )


# Start with each person and go around the table, trying every combination
for p in happiness:
    for n in happiness[p]:
        calc_possibilities(p, n, [p], happiness[p][n] + happiness[n][p])

aoc.p2(max(possibilities))
Ejemplo n.º 2
0
    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]
        break

    bingo_cards = unfinished_cards

unmarked = sum(filter(None, flatten(bingo)))
aoc.p2(unmarked * last_drawn)
Ejemplo n.º 3
0
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

aoc.p2(sum(fish))
Ejemplo n.º 4
0
    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))

        p1 = first_hand.pop(0)
        p2 = second_hand.pop(0)

        if len(first_hand) >= p1 and len(second_hand) >= p2:
            winner = play_game(first_hand[:p1], second_hand[:p2])
        else:
            winner = 1 if p1 > p2 else 2

        if winner == 1:
            first_hand.append(p1)
            first_hand.append(p2)
        else:
            second_hand.append(p2)
            second_hand.append(p1)

    return 1 if first_hand else 2


while first_hand and second_hand:
    play_game(first_hand, second_hand)

aoc.p2(calculate_score(first_hand if first_hand else second_hand))
Ejemplo n.º 5
0
    return {
        "header": header,
        "length": node_length,
        "children": children,
        "metadata": metadata,
    }


root_node = build_node(data.numbers_by_line()[0])


def value_of_node(node):
    if not node["children"]:
        return sum(node["metadata"])

    meta_cache = {}
    value = 0
    for metadata in node["metadata"]:
        index = metadata - 1
        if index == -1:
            continue

        if index < node["header"]["children"]:
            if index not in meta_cache:
                meta_cache[index] = value_of_node(node["children"][index])
            value += meta_cache[index]
    return value


aoc.p2(value_of_node(root_node))
Ejemplo n.º 6
0
]

# Initial position (number 5)
start = (2, 0)
code = []

keypad_height = len(layout)
keypad_width = len(layout[0])


def is_valid(y, x):
    # Returns true if a position is valid on the keypad
    return (x in range(0, keypad_width) and y in range(0, keypad_height)
            and layout[y][x] != -1)


for line in data.lines():
    for char in line:
        if char == "L" and is_valid(start[0], start[1] - 1):
            start = (start[0], start[1] - 1)
        if char == "U" and is_valid(start[0] - 1, start[1]):
            start = (start[0] - 1, start[1])
        if char == "R" and is_valid(start[0], start[1] + 1):
            start = (start[0], start[1] + 1)
        if char == "D" and is_valid(start[0] + 1, start[1]):
            start = (start[0] + 1, start[1])

    code.append(layout[start[0]][start[1]])

aoc.p2("".join([str(c) for c in code]))
Ejemplo n.º 7
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)
Ejemplo n.º 8
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)
Ejemplo n.º 9
0

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
                 for i in range(1, 5)]) + ")")
        return ""

    @lru_cache
    def resolver(r):
        if type(rules[r][0]) is str:
            return rules[r][0]
        else:
            return ("(" + "|".join([
                "".join([
                    manual_resolution(y) if y in [8, 11] else resolver(y)
                    for y in x
                ]) for x in rules[r]
            ]) + ")")

    return "^" + resolver(r) + "$"


rule_zero = resolve_infinite_rule(0)
aoc.p2(len([1 for x in chunks[1] if re.match(rule_zero, x)]))
Ejemplo n.º 10
0
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))
    reduced = [b for b in bitstrings if b[position] == comparator]
    if len(reduced) == 1:
        return int(reduced[0], 2)
    else:
        return reduce(reduced, position + 1, bit_extractor)


oxygen = reduce(data.lines(), 0, most_common_bit)
co = reduce(data.lines(), 0, least_common_bit)

aoc.p2(oxygen * co)
Ejemplo n.º 11
0
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)
Ejemplo n.º 12
0

# 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"])
Ejemplo n.º 13
0
## 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()
    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()

    if digest[5].isdigit() and int(digest[5]) < 8 and code[int(digest[5])] is None:
        values_found += 1
        code[int(digest[5])] = digest[6]

# Print the password
aoc.p2("".join(code))
Ejemplo n.º 14
0
def in_target_range(position):
    return position.x in target_x and position.y in target_y


[minX, maxX, minY, maxY] = data.nums()
target_x = range(minX, maxX + 1)
target_y = range(minY, maxY + 1)

total_max_height = 0
total_valid_velocities = 0

for dx in range(max(target_x) + 1):
    for dy in range(min(target_y) - 1, 200):
        position = Position(0, 0)
        velocity = Position(dx, dy)
        max_height = 0

        while not overshot(position) and not in_target_range(position):
            step(position, velocity)
            max_height = max(position.y, max_height)
            if velocity.x == 0 and position.x < min(target_x):
                break

        if in_target_range(position):
            total_max_height = max(total_max_height, max_height)
            total_valid_velocities += 1

aoc.p1(total_max_height)
aoc.p2(total_valid_velocities)
Ejemplo n.º 15
0

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),
            ]
        ]
    )
)
Ejemplo n.º 16
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)
Ejemplo n.º 17
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))
Ejemplo n.º 18
0
# 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)
Ejemplo n.º 19
0
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):
    if ins in ["L", "R"]:
        return ship, rotate_waypoint(waypoint, ins, val)
    elif ins == "F":
        return move_ship(ship, waypoint, val), waypoint
    else:
        return ship, move_ship(waypoint, Direction[ins].position, val)


ship, waypoint = Position(0, 0), Position(10, -1)
for instruction in data.parse_lines(r"(\w)(\d+)"):
    ship, waypoint = command_waypoint(ship, waypoint, instruction[0],
                                      int(instruction[1]))

aoc.p2(abs(ship.x) + abs(ship.y))
Ejemplo n.º 20
0
count = 10
seconds = 0
while True:
    if should_print:
        print_lights(points)
    updated_points = {}
    for point in points:
        x, y = point
        for velocity in points[point]:
            xx, yy = velocity
            new_point = (x + xx, y + yy)
            if new_point in updated_points:
                updated_points[new_point].append((xx, yy))
            else:
                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.p2(10081)
Ejemplo n.º 21
0
        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

aoc.p2(least_risk_path(full_grid))
Ejemplo n.º 22
0
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()

# Print the lowest valid positive integer
aoc.p2(lowest_positive_int)
Ejemplo n.º 23
0
            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)
Ejemplo n.º 24
0
# 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"]),
]


def passes_validation(passport):
    return all(v(passport) for v in validations)


aoc.p2(len([p for p in passports if passes_validation(p)]))
Ejemplo n.º 25
0
    if in_range(water):
        touched.add(water)

    for x in range(left_edge[0], right_edge[0] + 1):
        cell = (x, water[1])
        if left_enclosed and right_enclosed:
            settled_water.add(cell)
        if in_range(cell):
            touched.add(cell)

    under_left = under(left_edge)
    if not left_enclosed:
        if in_range(under_left):
            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.p2(settled_water)
Ejemplo n.º 26
0
    if battle.check_game_over():
        if battle.boss.hp <= 0:
            minimum_mana = (
                min(minimum_mana, battle.total_cost)
                if minimum_mana != -1
                else battle.total_cost
            )
        continue

    next_battles = [battle.duplicate() for x in spells]
    for index, next_battle in enumerate(next_battles):
        if not next_battle.can_cast(spell_names[index]):
            continue

        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.p2(minimum_mana)
Ejemplo n.º 27
0
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:
    nf = next(
        (f for f in field_indices
         if f not in confirmed_fields and len(field_indices[f]) == 1),
        None,
    )
    if not nf:
        break

    idx, next_field = next(iter(field_indices[nf])), nf
    for f in field_indices:
        if f == next_field or idx not in field_indices[f]:
            continue
        field_indices[f].remove(idx)
    confirmed_fields.add(next_field)

aoc.p2(
    prod([
        your_ticket[next(iter(field_indices[x]))] for x in field_indices
        if "departure " in x
    ]))
Ejemplo n.º 28
0
        # Only update the score if the calorie count is 500
        if calorie_score == 500:
            # Return the product of all scores, replacing the score with 0 if it is less than 0
            return (max(capacity_score, 0) * max(durability_score, 0) *
                    max(flavor_score, 0) * max(texture_score, 0))
        return 0
    # Find the next unused ingredient and try all the combination of ingredients to get the best score
    for ingredient in ingredients:
        if not ingredient in amounts:
            best = 0
            for c in range(101 - total_used):
                updated_amounts = amounts.copy()
                updated_amounts[ingredient] = c
                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.p2(best_score)
Ejemplo n.º 29
0
    if len(remaining_containers) == 0:
        return

    # Get the total size of all the containers
    containers_total = sum(containers_used)

    # Iterate over each of the remaining containers
    for i in range(len(remaining_containers) - 1, -1, -1):
        # If the target eggnog amount is met, then check if the number of containers is the minimum
        if containers_total + remaining_containers[i] == target_eggnog:
            if (minimum_containers == -1
                    or len(containers_used) + 1 < minimum_containers):
                minimum_containers = len(containers_used) + 1
                minimum_arrangements = 1
            elif len(containers_used) + 1 == minimum_containers:
                minimum_arrangements += 1
        # If the total is too large, then only larger containers are remaining
        elif containers_total + remaining_containers[i] > target_eggnog:
            break
        # If the total is too small, keep iterating over the remaining containers
        elif i > 0:
            updated_containers_used = containers_used[:]
            updated_containers_used.append(remaining_containers[i])
            get_arrangements(updated_containers_used, remaining_containers[:i])


# Gets the number of total container arrangements
get_arrangements([], PUZZLE_INPUT)

aoc.p2(minimum_arrangements)
Ejemplo n.º 30
0
from aoc import AOC

aoc = AOC(year=2021, day=7)
data = aoc.load()
crabs = data.nums()

# Part 1

aoc.p1(min(sum(abs(c - i) for c in crabs) for i in range(max(crabs))))

# Part 2

aoc.p2(
    min(
        sum((abs(c - i) * (abs(c - i) + 1)) // 2 for c in crabs)
        for i in range(max(crabs))
    )
)