Example #1
0
from misc import init_day

init_day(__file__, 15)


def get_numbers(numbers_list):
    return [int(n) for n in numbers_list.split(',')]


def solve(numbers_list, position):
    numbers = get_numbers(numbers_list)
    count = len(numbers)

    track = dict((number, index + 1) for index, number in enumerate(numbers))

    while count <= position:
        last_number = numbers[count - 1]
        tracked = track.get(last_number)
        if tracked:
            number = count - tracked
        else:
            number = 0
        track[last_number] = count
        numbers.append(number)
        count += 1
    return last_number


assert solve('0,3,6', 2020) == 436
assert solve('1,3,2', 2020) == 1
assert solve('2,1,3', 2020) == 10
Example #2
0
import math

from misc import init_day
from input import get_file_lines_with_no_blanks

init_day(__file__, 3)

forest = get_file_lines_with_no_blanks('./input.txt')


def get_tree_count(slope_horizontal, slope_vertical):
    tree_count = 0
    pos = 0
    for index, row in enumerate(forest):
        if index % slope_vertical == 0:
            if row[pos] == '#':
                tree_count += 1
            pos = (pos + slope_horizontal) % len(row)
    return tree_count


result_1 = get_tree_count(3, 1)
result_2 = math.prod(
    get_tree_count(h, v)
    for (h, v) in [(1, 1), (3, 1), (5, 1), (7, 1), (1, 2)])

print(f'Part 1: {result_1}')
print(f'Part 2: {result_2}')
Example #3
0
from misc import init_day
from input import get_file_lines_with_no_blanks

init_day(__file__, 8)


class Machine:
    def __init__(self):
        self.pc = 0
        self.acc = 0
        self.memory = []
        self.looped = False

    def parse(self, mnemonics):
        self.memory = []
        for line in mnemonics:
            operation, argument = line.split(' ')
            argument = int(argument)
            self.memory.append((operation, argument))

    def run(self):
        self.pc = 0
        self.acc = 0
        self.looped = False
        pc_already_executed = {}
        memory_size = len(self.memory)
        while self.pc < memory_size:
            already_executed = pc_already_executed.get(self.pc, False)
            if already_executed:
                self.looped = True
                return
Example #4
0
import re
from functools import reduce
from collections import Counter

from misc import init_day
from input import get_file_lines_with_no_blanks

init_day(__file__, 24)

MOVEMENT_DELTAS = {
    'e': (1, 0),
    'w': (-1, 0),
    'ne': (0, 1),
    'sw': (0, -1),
    'se': (1, -1),
    'nw': (-1, 1),
}


def sum_coords(coord_1, coord_2):
    return (coord_1[0] + coord_2[0], coord_1[1] + coord_2[1])


def sum_all_coords(coords_list):
    return reduce(sum_coords, coords_list, (0, 0))


def get_blacks_for_movements(input):
    movements = [re.findall(r'se|sw|nw|ne|e|w', line) for line in input]
    flips = Counter(
        sum_all_coords([MOVEMENT_DELTAS[direction] for direction in movement])
Example #5
0
from functools import cache

from misc import init_day
from input import get_file_numbers

init_day(__file__, 10)


def solve_part_1(part1_input):
    adaptors = sorted(part1_input[:])
    adaptors.append(max(adaptors) + 3)
    joltage = 0
    previous_joltage = 0
    diff_count = {1: 0, 3: 0}
    while len(adaptors) > 0:
        next_adaptor = next(adaptor for adaptor in adaptors
                            if (adaptor - joltage) in [1, 3])
        adaptors.remove(next_adaptor)
        joltage = next_adaptor
        diff_count[joltage - previous_joltage] += 1
        previous_joltage = joltage
    return diff_count[1] * diff_count[3]


def solve_part_2(part2_input):
    @cache
    def get_combinations(joltage, candidate_adaptors, ignore_adaptor=-1):
        next_adaptors = tuple([
            adaptor for adaptor in candidate_adaptors
            if (1 <= adaptor - joltage <= 3) and (adaptor != ignore_adaptor)
        ])
Example #6
0
from math import prod

from misc import init_day
from input import get_file_lines_with_no_blanks

init_day(__file__, 13)


def solve_part_1(timestamp, ids_text):
    ids = [int(id) for id in ids_text.split(',') if id != 'x']
    ids_with_remainders = [(id, id - (timestamp % id)) for id in ids]
    good_id, good_wait_time = min(ids_with_remainders, key=lambda item: item[1])
    return good_id * good_wait_time


def solve_part_2(ids_text):
    def chinese_remainder(divisors, remainders):
        #  Chinese remainder will give a number that when divided by some divisors leaves certain remainders.
        product = prod(divisors)
        return sum(r * pow(product // d, -1, d) * (product // d) for d, r in zip(divisors, remainders)) % product

    ids = [0 if id == 'x' else int(id) for id in ids_text.split(',')]
    buses = [(index, id) for index, id in enumerate(ids) if id != 0]
    actual_ids = [id for id in ids if id != 0]
    bus_offsets = [timestamp - index for index, timestamp in buses]
    return chinese_remainder(actual_ids, bus_offsets)


test_input = get_file_lines_with_no_blanks('test.txt')
test_timestamp, test_ids_text = int(test_input[0]), test_input[1]
Example #7
0
import re
from math import prod

from misc import init_day
from input import get_file_lines_with_no_blanks

init_day(__file__, 16)


def parse_input(problem_input):
    def parse_int_list(number_list):
        return [int(number) for number in number_list.split(',')]

    tickets = []
    fields = []
    for line in problem_input:
        if re.match(r'^[\d,]+$', line):
            tickets.append(line)
        else:
            field_matches = re.match(r'^(.+)\s*:', line)
            if field_matches:
                field = field_matches.group(1)
                ranges = [(int(range_min), int(range_max))
                          for range_min, range_max in re.findall(
                              r'(\d+)\-(\d+)', line)]
                fields.append((field, ranges))
    my_ticket = parse_int_list(tickets[0])
    nearby_tickets = [parse_int_list(ticket) for ticket in tickets[1:]]
    return my_ticket, nearby_tickets, fields

Example #8
0
from misc import init_day
from input import get_file_numbers
from indexes import unique_index_pairs, unique_index_triplets

init_day(__file__, 1)

numbers = get_file_numbers('./input.txt')
number_count = len(numbers)

for i, j in unique_index_pairs(number_count):
    if numbers[i] + numbers[j] == 2020:
        print(f'Part 1: {numbers[i] * numbers[j]}')
        break

for i, j, k in unique_index_triplets(number_count):
    if numbers[i] + numbers[j] + numbers[k] == 2020:
        print(f'Part 2: {numbers[i] * numbers[j] * numbers[k]}')
        break
Example #9
0
from misc import init_day

init_day(__file__, 25)


def transform_subject_number(subject_number, loop_size):
    value = 1
    for _ in range(loop_size):
        value *= subject_number
        value = value % 20201227
    return value


def find_loop_size(public_key, subject_number):
    loop_size = 1
    calculated_publick_key = 1
    while True:
        calculated_publick_key *= subject_number
        calculated_publick_key = calculated_publick_key % 20201227
        if calculated_publick_key == public_key:
            return loop_size
        loop_size += 1


def solve_part_1(card_public_key, door_public_key, check_key_match):
    card_loop_size = find_loop_size(card_public_key, 7)
    door_loop_size = find_loop_size(door_public_key, 7)
    encryption_key = transform_subject_number(card_public_key, door_loop_size)
    if check_key_match:
        encryption_key_2 = transform_subject_number(door_public_key,
                                                    card_loop_size)
Example #10
0
import re
from collections import defaultdict

from misc import init_day
from input import get_file_lines_with_no_blanks

init_day(__file__, 14)


def transform_mask(mask, transform_map):
    return ''.join([transform_map.get(char, char) for char in mask])


def solve_part_1(problem_input):
    mem = defaultdict(int)
    for line in problem_input:
        mask_matches = re.match(r'^mask\s*=\s*([X01]{36})$', line)
        if mask_matches:
            mask = mask_matches.group(1)
            and_mask = int(transform_mask(mask, {'X': '1', '1': '0', '0': '0'}), 2)
            or_mask = int(transform_mask(mask, {'X': '0', '1': '1', '0': '0'}), 2)
        else:
            assign_matches = re.match(r'^mem\[(\d+)\]\s*=\s*(\d+)$', line)
            if assign_matches:
                mem_pos = int(assign_matches.group(1))
                mem_value = int(assign_matches.group(2))
                mem[mem_pos] = (mem_value & and_mask) | or_mask
    return sum(mem.values())


def solve_part_2(problem_input):
Example #11
0
import re

from misc import init_day
from input import get_file_lines_with_no_blanks

init_day(__file__, 7)


def contains_bag(definitions, container, bag):
    if container == bag:
        return True
    container_definition = definitions[container]
    for number, contained_bag in container_definition:
        if contains_bag(definitions, contained_bag, bag):
            return True
    return False


def bag_contents_count(definitions, bag):
    def bag_contents_count_rec(definitions, bag):
        container_definition = definitions[bag]
        return 1 + sum(
            number * bag_contents_count_rec(definitions, contained_bag)
            for number, contained_bag in container_definition)

    return bag_contents_count_rec(definitions, bag) - 1


def get_bags_definition(input):
    result = {}
    for line in input:
Example #12
0
import re

from misc import init_day
from input import get_file_lines_with_no_blanks

init_day(__file__, 21)


def parse_input(input):
    all_ingredients = []
    allergen_reference = dict()
    index = 0
    for line in input:
        matches = re.match(r'^([a-z\s]+) \(contains ([a-z,\s]+)\)$', line)
        ingredients_list = matches.group(1).split()
        allergens = matches.group(2).split(', ')
        all_ingredients.append(ingredients_list)
        for allergen in allergens:
            allergen_reference[allergen] = allergen_reference.get(allergen, [])
            allergen_reference[allergen].append(index)
        index += 1
    allergen_candidates = {
        key: set.intersection(
            *[set(all_ingredients[k]) for k in allergen_reference[key]])
        for key, value in allergen_reference.items()
    }
    no_ingredient = set(ingredient for ingredients_list in all_ingredients
                        for ingredient in ingredients_list)
    no_ingredient = no_ingredient.difference(
        set.union(*(allergen_candidates.values())))
    return all_ingredients, no_ingredient, allergen_candidates
Example #13
0
from misc import init_day
from input import get_file_numbers

init_day(__file__, 9)


def solve_part_1(part1_input, preamble_length):
    def any_pair_sums(collection, target_sum):
        collection_length = len(collection)
        for i in range(0, collection_length - 1):
            for j in range(i + 1, collection_length):
                if collection[i] + collection[j] == target_sum:
                    return True
        return False

    for block_start in range(0, len(part1_input) - preamble_length):
        block = part1_input[block_start:block_start + preamble_length]
        number = part1_input[block_start + preamble_length]
        if not any_pair_sums(block, number):
            return number
    raise Exception('Part 1 is unsolvable!')


def solve_part_2(part1_input, target):
    input_length = len(part1_input)
    for block_size in range(2, input_length):
        for block_start in range(0, input_length - block_size + 1):
            block = part1_input[block_start:block_start + block_size]
            if sum(block) == target:
                return min(block) + max(block)
    raise Exception('Part 2 is unsolvable!')
Example #14
0
from collections import defaultdict

from misc import init_day
from input import get_file_lines_with_no_blanks

init_day(__file__, 17)


def parse_input(problem_input, dimensions):
    def complete_coords(coords):
        return coords + [0] * abs((len(coords) - dimensions))

    world = defaultdict(bool)
    for y, line in enumerate(problem_input):
        for x, char in enumerate(line):
            coords_list = complete_coords([x, y, 0])
            coord = tuple(coords_list)
            world[coord] = char == '#'
    return world


def get_world_limits_1(world):
    coords = world.keys()
    xs = [x for x, _, _ in coords]
    ys = [y for _, y, _ in coords]
    zs = [z for _, _, z in coords]
    min_x = min(xs)
    max_x = max(xs)
    min_y = min(ys)
    max_y = max(ys)
    min_z = min(zs)
Example #15
0
import re

from misc import init_day
from lists import list_difference
from input import get_file_lines_with_no_blanks

init_day(__file__, 5)


def get_boarding_pass_id(boarding_pass):
    return int(re.sub(r'[FL]', '0', re.sub(r'[BR]', '1', boarding_pass)), 2)


def solve_part_1(boarding_passes):
    return max(
        get_boarding_pass_id(boarding_pass)
        for boarding_pass in boarding_passes)


def solve_part_2(boarding_passes):
    boarding_pass_ids = [
        get_boarding_pass_id(boarding_pass)
        for boarding_pass in boarding_passes
    ]
    full_plane_ids = list(range(0, 2**len(boarding_passes[0])))
    missing_ids = list_difference(full_plane_ids, boarding_pass_ids)
    for boarding_pass_id in missing_ids:
        if (boarding_pass_id - 1) in boarding_pass_ids and (
                boarding_pass_id + 1) in boarding_pass_ids:
            return boarding_pass_id
Example #16
0
from misc import init_day

init_day(__file__, 23)


def cups_clockwise_map(input, num_cups, move_count):
    cups = [int(cup) for cup in list(str(input))]
    if num_cups >= 10:
        cups += [cup for cup in range(10, num_cups + 1)]
    cup_clockwise_from = {}
    for index in range(num_cups):
        if index == num_cups - 1:
            cup_clockwise_from[cups[index]] = cups[0]
        else:
            cup_clockwise_from[cups[index]] = cups[index + 1]
    current = cups[0]
    for _ in range(move_count):
        pick_1 = cup_clockwise_from[current]
        pick_2 = cup_clockwise_from[pick_1]
        pick_3 = cup_clockwise_from[pick_2]
        cup_clockwise_from[current] = cup_clockwise_from[pick_3]
        destination = current - 1
        while destination in [pick_1, pick_2, pick_3] or destination < 1:
            destination -= 1
            if destination < 1:
                destination = num_cups
        cup_clockwise_from[pick_3] = cup_clockwise_from[destination]
        cup_clockwise_from[destination] = pick_1
        current = cup_clockwise_from[current]
    return cup_clockwise_from
Example #17
0
import sys
import re

from misc import init_day
from input import get_file_lines_with_no_blanks

init_day(__file__, 2)

test_pattern = re.compile(r'^(\d+)-(\d+)\s+(\w)\s*:\s*(\w+)$')

lines = get_file_lines_with_no_blanks('./input.txt')
parsed_list = []
for line in lines:
    matches = test_pattern.match(line)
    if matches is None:
        print(f'Wrong test line: {line}')
        sys.exit(1)
    (num1, num2, letter, sequence) = matches.groups()
    parsed_list.append((int(num1), int(num2), letter, sequence))

valid_count_part1 = 0
for (num1, num2, letter, sequence) in parsed_list:
    count_letter = len([char for char in sequence if char == letter])
    if num1 <= count_letter <= num2:
        valid_count_part1 += 1
print(f'Part 1: {valid_count_part1}')

valid_count_part2 = 0
for (num1, num2, letter, sequence) in parsed_list:
    count_letter = len([char for char in sequence if char == letter])
    if (sequence[num1 - 1] == letter) != (sequence[num2 - 1] == letter):
Example #18
0
from misc import init_day
from lists import list_intersection
from input import get_file_lines

init_day(__file__, 6)


def solve_part_1(part_answers):
    def get_group_answers(answer_list):
        answer_list.append('')
        result = []
        chars = ''
        for line in answer_list:
            if line == '':
                result.append(''.join(set(chars)))
                chars = ''
            else:
                chars = chars + line
        return result

    group_answers = get_group_answers(part_answers)
    return sum(len(group) for group in group_answers)


def solve_part_2(part_answers):
    def get_group_answers(answer_list):
        answer_list.append('')
        result = []
        chars = ''
        for line in answer_list:
            if line == '':
Example #19
0
import re

from misc import init_day
from input import get_file_lines_with_no_blanks

init_day(__file__, 18)


class Stack:
    def __init__(self):
        self.items = []

    def is_empty(self):
        return self.count() == 0

    def not_empty(self):
        return not self.is_empty()

    def push(self, item):
        self.items.append(item)

    def pop(self):
        return self.items.pop()

    def peek(self):
        return self.items[-1]

    def count(self):
        return len(self.items)

    def __repr__(self):
Example #20
0
import math
import re
from collections import defaultdict

from misc import init_day
from input import get_file_contents

init_day(__file__, 20)


def parse_input(s):
    tiles = {}
    for block in s.split('\n\n'):
        lines = block.splitlines()
        matches = re.fullmatch(r'Tile (\d+):', lines[0])
        tile_id = int(matches.group(1))
        tiles[tile_id] = [list(line) for line in lines[1:]]
    return tiles


def tile_edges(tile):
    top_edge = tile[0]
    right_edge = ''.join(row[-1] for row in tile)
    bottom_edge = tile[-1]
    left_edge = ''.join(row[0] for row in tile)
    return (top_edge, right_edge, bottom_edge, left_edge)


def tile_variants(tile):
    result = []
    for item in sum(
Example #21
0
import re

from misc import init_day
from input import get_file_lines
from lists import list_intersection

init_day(__file__, 4)

passport_parts = re.compile(r'(\w{3}):(\S+)')


def no_validation(value, regexp_matches):
    return True


PASSPORT_VALIDATIONS = {
    'byr': (r'\d{4}', lambda v, m: 1920 <= int(v) <= 2002),
    'iyr': (r'\d{4}', lambda v, m: 2010 <= int(v) <= 2020),
    'eyr': (r'\d{4}', lambda v, m: 2020 <= int(v) <= 2030),
    'hgt': (r'(\d+)(cm|in)', lambda v, m: (150 <= int(m[0]) <= 193)
            if m[1] == 'cm' else (59 <= int(m[0]) <= 76)),
    'hcl': (r'#[a-f0-9]{6}', no_validation),
    'ecl': (r'(amb|blu|brn|gry|grn|hzl|oth)', no_validation),
    'pid': (r'\d{9}', no_validation),
}
REQUIRED_PASSPORT_KEYS = PASSPORT_VALIDATIONS.keys()
REQUIRED_PASSPORT_KEY_COUNT = len(REQUIRED_PASSPORT_KEYS)


def parse_passports(passport_input_lines):
    passport_input_lines.append('')
Example #22
0
from misc import init_day
from input import get_file_lines_with_no_blanks

init_day(__file__, 19)


def parse_input(input):
    rules = {}
    messages = []
    for line in input:
        if ':' in line:
            rule_id, rule_content = line.split(': ')
            rule_id = int(rule_id)
            if '"' in rule_content:
                rules[rule_id] = rule_content[1]
            else:
                elements = [part.strip() for part in rule_content.split('|')]
                rules[rule_id] = [[int(item) for item in element.split(' ')]
                                  for element in elements]
        else:
            messages.append(line)
    return rules, messages


def message_matches(message, rules, rules_list):
    if message == '' or rules_list == []:
        return message == '' and rules_list == []
    rule = rules[rules_list[0]]
    if isinstance(rule, str):
        if message[0] in rule:
            return message_matches(message[1:], rules, rules_list[1:])