Exemple #1
0
def main():
    puzzle_input = load_input(__file__)
    # puzzle_input = load_input(__file__, 'example')

    def evaluate(expression) -> int:
        if '(' not in expression:
            return evaluate_operation(expression)
        match = re.search(r'\(([^()]+)\)', expression)
        number = evaluate_operation(match.group(1))
        return evaluate(expression[0:match.start()] + str(number) + expression[match.end():])

    def evaluate_operation(expression) -> int:
        if re.match(r'^\d+$', expression):
            return int(expression)
        match = re.match(r'(.*) ([+*]) (\d+)', expression)
        l_num = evaluate_operation(match.group(1))
        operator = match.group(2)
        r_num = int(match.group(3))
        if operator == '+':
            return l_num + r_num
        elif operator == '*':
            return l_num * r_num

    print(evaluate_operation('1 + 2 * 3 + 4 * 5 + 6'))
    print(evaluate('1 + (2 * 3) + (4 * (5 + 6))'))
    print(evaluate('2 * 3 + (4 * 5)'))
    print(evaluate('5 + (8 * 3 + 9 + 3 * 4 * 3)'))
    print(evaluate('5 * 9 * (7 * 3 * 3 + 9 * 3 + (8 + 6 * 4))'))
    print(evaluate('((2 + 4 * 9) * (6 + 9 * 8 + 6) + 6) + 2 + 4 * 2'))

    sum = 0
    for expression in puzzle_input:
        sum += evaluate(expression)
    print('Homework sum:', sum)
Exemple #2
0
def main():
    puzzle_input = load_input(__file__)
    puzzle_input = load_input(__file__, 'example')

    puzzle_input.append('')
    tiles = []

    all_input = '\n'.join(puzzle_input)
    for tile_match in re.finditer(r'Tile (\d+):\n([.#\n]*[^\n])\n', all_input):
        print('Tile', tile_match.group(1), ':')
        tile = Tile(int(tile_match.group(1)), tile_match.group(2).split('\n'))
        tiles.append(tile)

    for y in [0 1 2]:
        for x in [0 1 2]:
            pass
Exemple #3
0
def main():
    puzzle_input = load_input(__file__)
    # puzzle_input = load_input(__file__, 'example')

    starting_numbers = [int(n) for n in puzzle_input[0].split(',')]

    history = {}
    turn = 1
    for number in starting_numbers:
        history[number] = [turn]
        turn += 1

    last_number = starting_numbers[-1]
    while turn <= 2020:
        if len(history[last_number]) == 1:
            new_number = 0
            print('turn {}  last number {}  it was new, say {}'.format(
                turn, last_number, new_number))
        else:
            new_number = history[last_number][-1] - history[last_number][-2]
            print('turn {}  last number {}  happened on {} then {}, say {}'.
                  format(turn, last_number, history[last_number][-2],
                         history[last_number][-1], new_number))

        if new_number not in history.keys():
            history[new_number] = []
        history[new_number].append(turn)
        last_number = new_number

        # print(last_number)
        turn += 1

    print('last_number:', last_number)
Exemple #4
0
def main():
    puzzle_input = load_input(__file__)

    # puzzle_input = load_input(__file__, 'example')

    def evaluate(expression) -> int:
        if '(' not in expression:
            return evaluate_operations(expression)
        match = re.search(r'\(([^()]+)\)', expression)
        number = evaluate_operations(match.group(1))
        return int(
            evaluate(expression[0:match.start()] + str(number) +
                     expression[match.end():]))

    def evaluate_operations(expression) -> int:
        additions_done = evaluate_additions(expression)
        return int(evaluate_multiplications(additions_done))

    def evaluate_additions(expression) -> str:
        match = re.search(r'(\d+) \+ (\d+)', expression)
        evaluated = expression
        if match:
            number = int(match.group(1)) + int(match.group(2))
            evaluated = evaluate_additions(expression[0:match.start()] +
                                           str(number) +
                                           expression[match.end():])
        return evaluated

    def evaluate_multiplications(expression) -> str:
        match = re.search(r'(\d+) \* (\d+)', expression)
        evaluated = expression
        if match:
            number = int(match.group(1)) * int(match.group(2))
            evaluated = evaluate_multiplications(expression[0:match.start()] +
                                                 str(number) +
                                                 expression[match.end():])
        return evaluated

    print(evaluate('1 + 2 * 3 + 4 * 5 + 6'))
    print(evaluate('1 + (2 * 3) + (4 * (5 + 6))'))
    print(evaluate('2 * 3 + (4 * 5)'))
    print(evaluate('5 + (8 * 3 + 9 + 3 * 4 * 3)'))
    print(evaluate('5 * 9 * (7 * 3 * 3 + 9 * 3 + (8 + 6 * 4))'))
    print(evaluate('((2 + 4 * 9) * (6 + 9 * 8 + 6) + 6) + 2 + 4 * 2'))

    sum = 0
    for expression in puzzle_input:
        sum += evaluate(expression)
    print('Homework sum:', sum)
Exemple #5
0
def main():
    puzzle_input = load_input(__file__)
    # puzzle_input = load_input(__file__, 'example')

    parsing_rules = True
    parsing_mine = False
    parsing_nearby = False

    rules = []
    nearby_tickets = []

    for line in puzzle_input:
        if line == 'your ticket:':
            parsing_mine = True
            continue
        elif line == 'nearby tickets:':
            parsing_nearby = True
            continue
        elif line == '':
            parsing_rules = False
            parsing_mine = False
            parsing_nearby = False
            continue

        if parsing_nearby:
            nearby_tickets.append([int(n) for n in line.split(',')])
        elif parsing_mine:
            pass
        elif parsing_rules:
            match = re.match(r'^(.*): (.*)$', line)
            name = match.group(1)
            ranges = []
            range_strings = match.group(2).split(' or ')
            for range_string in range_strings:
                ranges.append([int(n) for n in range_string.split('-')])
            rules.append(Rule(name, ranges))

    error_rate = 0
    for nearby_ticket in nearby_tickets:
        for number in nearby_ticket:
            valid_for_any = False
            for rule in rules:
                if rule.accepts(number):
                    valid_for_any = True
                    break
            if not valid_for_any:
                error_rate += number

    print('error rate:', error_rate)
Exemple #6
0
def main():
    puzzle_input = load_input(__file__)
    # puzzle_input = load_input(__file__, 'example')

    unresolved_rules = {}
    rules = {}
    messages = []
    for line in puzzle_input:
        match = re.match(r'^(\d+): (.*)', line)
        if match:
            rule_number = int(match.group(1))
            rule_text = match.group(2)
            if '"' in rule_text:
                rules[rule_number] = rule_text[1]
            else:
                unresolved_rules[rule_number] = rule_text
        elif line == '':
            pass
        else:
            messages.append(line)

    while len(unresolved_rules) > 0:
        for unresolved_rule_number, unresolved_rule in unresolved_rules.items():
            updated_rule = unresolved_rule
            for rule_number, rule_regex in rules.items():
                if len(rule_regex) == 1:
                    rule = rule_regex
                else:
                    rule = '({})'.format(rule_regex)
                updated_rule = re.sub(r'\b{}\b'.format(rule_number), rule, updated_rule)
            if not re.search(r'\d', updated_rule):
                rules[unresolved_rule_number] = updated_rule.replace(' ', '')

        for rule_number in rules.keys():
            unresolved_rules.pop(rule_number, None)
        print(unresolved_rules)

    print('Rules:')
    for number, rule in sorted(rules.items()):
        print(number, ':', rule)

    matches = 0
    for message in messages:
        if re.fullmatch(rules[0], message):
            matches += 1
    print('matches:', matches)
Exemple #7
0
def main():
    puzzle_input = load_input(__file__)
    # puzzle_input = load_input(__file__, 'example')

    # puzzle_input = []
    # puzzle_input.append('')
    # puzzle_input.append('7,13,x,x,59,x,31,19')
    # puzzle_input.append('17,x,13,19')
    # puzzle_input.append('67,7,59,61')
    # puzzle_input.append('67,x,7,59,61')
    # puzzle_input.append('67,7,x,59,61')
    # puzzle_input.append('1789,37,47,1889')

    buses = {}
    route_time_strings = puzzle_input[1].split(',')
    for i in range(len(route_time_strings)):
        route_time_string = route_time_strings[i]
        if route_time_string == 'x':
            continue
        buses[i] = int(route_time_string)
        print(buses[i], 'position', i)

    first_bus = buses[0]
    lcm = first_bus
    if first_bus in buses.keys():
        lcm = math.lcm(first_bus, buses[first_bus])
        print('buses[0] ({}) lcm with buses[{}] ({}) = {}'.format(first_bus, first_bus, buses[first_bus], lcm))

    found = False
    time = lcm - 7
    while not found:
        offsets = list(buses.keys())
        found = True
        for offset in offsets:
            if found and (time + offset) % buses[offset] != 0:
                found = False
        if found:
            print('timestamp:', time)
        else:
            time += lcm
Exemple #8
0
def main():
    puzzle_input = load_input(__file__)
    # puzzle_input = load_input(__file__, 'example')

    cups: dict[int, Cup] = {}

    labels = [int(n) for n in puzzle_input[0]]
    for label in labels:
        cup = Cup(label)
        cups[label] = cup
    for pos, label in enumerate(labels):
        cups[label].next = cups[labels[(pos + 1) % len(labels)]]
        cups[label].prev = cups[labels[pos - 1]]

    current_cup = cups[labels[0]]

    for move in range(100):
        removed = [
            current_cup.next.remove(),
            current_cup.next.remove(),
            current_cup.next.remove()
        ]
        removed_labels = set([cup.label for cup in removed])

        labels_in_circle = sorted(list(set(labels) - removed_labels))
        destination_label = labels_in_circle[
            labels_in_circle.index(current_cup.label) - 1]
        dest_cup = cups[destination_label]

        dest_cup.append(removed[0]).append(removed[1]).append(removed[2])
        current_cup = current_cup.next

    end_state = ''
    cup = cups[1]
    for i in range(len(cups) - 1):
        cup = cup.next
        end_state += str(cup.label)
    print('End state:', end_state)
Exemple #9
0
def main():
    puzzle_input = load_input(__file__)
    # puzzle_input = load_input(__file__, 'example')

    cups: dict[int, Cup] = {}

    labels = [int(n) for n in puzzle_input[0]]

    MAX_LABEL = 1_000_000
    labels += list(range(len(labels) + 1, MAX_LABEL + 1))

    for label in labels:
        cup = Cup(label)
        cups[label] = cup
    for pos, label in enumerate(labels):
        cups[label].next = cups[labels[(pos + 1) % len(labels)]]
        cups[label].prev = cups[labels[pos - 1]]

    current_cup = cups[labels[0]]

    for move in range(10_000_000):
        removed = [
            current_cup.next.remove(),
            current_cup.next.remove(),
            current_cup.next.remove()]
        removed_labels = [cup.label for cup in removed]

        destination_label = current_cup.label - 1
        while destination_label in removed_labels or destination_label == 0:
            if destination_label == 0:
                destination_label = MAX_LABEL
            else:
                destination_label -= 1

        dest_cup = cups[destination_label]

        dest_cup.append(removed[0]).append(removed[1]).append(removed[2])
        current_cup = current_cup.next
Exemple #10
0
def main():
    puzzle_input = load_input(__file__)
    # puzzle_input = load_input(__file__, 'example')

    puzzle_input.append('')

    player_decks: list[list[int]] = []
    all_input = '\n'.join(puzzle_input)
    for player_deck_match in re.finditer(r'Player (\d+):\n([\d\n]*[^\n])\n',
                                         all_input):
        deck = [int(card) for card in player_deck_match.group(2).split('\n')]
        player_decks.append(deck)

    number_of_cards = sum([len(deck) for deck in player_decks])

    while max([len(deck) for deck in player_decks]) != number_of_cards:
        p1 = player_decks[0].pop(0)
        p2 = player_decks[1].pop(0)

        if p1 > p2:
            player_decks[0].append(p1)
            player_decks[0].append(p2)
        elif p2 > p1:
            player_decks[1].append(p2)
            player_decks[1].append(p1)
        else:
            pass  # Rules don't say what happens if the cards are the same

    winning_deck = player_decks[0]
    if len(player_decks[1]) > 0:
        winning_deck = player_decks[1]

    score = sum([
        card * pos
        for card, pos in zip(winning_deck, range(number_of_cards, 0, -1))
    ])
    print(score)
Exemple #11
0
def main():
    puzzle_input = load_input(__file__)

    # puzzle_input = load_input(__file__, 'example')

    def decode_coord(vectors_string):
        nw = vectors_string.count('nw')
        sw = vectors_string.count('sw')
        ne = vectors_string.count('ne')
        se = vectors_string.count('se')
        ew_string = re.sub(r'[ns][ew]', '', vectors_string)
        e = ew_string.count('e')
        w = ew_string.count('w')
        return ne + se + e * 2 - nw - sw - w * 2, ne + nw - se - sw

    tiles = {}
    for line in puzzle_input:
        tile = decode_coord(line)
        if tile not in tiles:
            tiles[tile] = False
        tiles[tile] = not tiles[tile]

    flipped = len([tile for tile in tiles.values() if tile])
    print(flipped)
Exemple #12
0
def main():
    puzzle_input = load_input(__file__)
    # puzzle_input = load_input(__file__, 'example')

    parsing_rules = True
    parsing_mine = False
    parsing_nearby = False

    rules = []
    nearby_tickets = []

    for line in puzzle_input:
        if line == 'your ticket:':
            parsing_mine = True
            continue
        elif line == 'nearby tickets:':
            parsing_nearby = True
            continue
        elif line == '':
            parsing_rules = False
            parsing_mine = False
            parsing_nearby = False
            continue

        if parsing_nearby:
            nearby_tickets.append([int(n) for n in line.split(',')])
        elif parsing_mine:
            my_ticket = [int(n) for n in line.split(',')]
        elif parsing_rules:
            match = re.match(r'^(.*): (.*)$', line)
            name = match.group(1)
            ranges = []
            range_strings = match.group(2).split(' or ')
            for range_string in range_strings:
                ranges.append([int(n) for n in range_string.split('-')])
            rules.append(Rule(name, ranges))

    valid_tickets = []

    positions = len(nearby_tickets[0])

    for nearby_ticket in nearby_tickets:
        ticket_is_valid = True
        for position in range(positions):
            number = nearby_ticket[position]
            valid_for_any_rule = False
            for rule in rules:
                if rule.accepts(number):
                    valid_for_any_rule = True
                    break
            if not valid_for_any_rule:
                ticket_is_valid = False
                break
        if ticket_is_valid:
            valid_tickets.append(nearby_ticket)

    positional_rules = []
    for position in range(positions):
        positional_rules.append(set(rules))

    for nearby_ticket in valid_tickets:
        for position in range(positions):
            number = nearby_ticket[position]
            for rule in rules:
                if not rule.accepts(number):
                    positional_rules[position].discard(rule)

    determined_allocation = {}

    pos_names = []
    for rules in positional_rules:
        pos_names.append(list([rule.name for rule in rules]))

    def remove_rule(name):
        for names in pos_names:
            if len(names) > 1 and name in names:
                names.remove(name)

    while sum([len(names) for names in pos_names]) > positions:
        for names in pos_names:
            if len(names) == 1:
                remove_rule(names[0])
        for position in range(positions):
            names = pos_names[position]
            print('position {} rules: {}'.format(position, names))
        print()

    answer = 1
    for position, names in enumerate(pos_names):
        if names[0].startswith('departure '):
            answer *= my_ticket[position]

    print('answer:', answer)
Exemple #13
0
def main():
    puzzle_input = load_input(__file__)
    # puzzle_input = load_input(__file__, 'example')

    initial_state = {}
    x_bounds = {min: 0, max: 0}
    y_bounds = {min: 0, max: 0}
    z_bounds = {min: 0, max: 0}
    w_bounds = {min: 0, max: 0}

    def update_bounds(pos):
        x_bounds[min] = min(x_bounds[min], pos.x)
        x_bounds[max] = max(x_bounds[max], pos.x)
        y_bounds[min] = min(y_bounds[min], pos.y)
        y_bounds[max] = max(y_bounds[max], pos.y)
        z_bounds[min] = min(z_bounds[min], pos.z)
        z_bounds[max] = max(z_bounds[max], pos.z)
        w_bounds[min] = min(w_bounds[min], pos.w)
        w_bounds[max] = max(w_bounds[max], pos.w)

    def store_state(pos, state_store, value):
        update_bounds(pos)
        state_store[pos] = value

    def get_state(pos, state_store):
        if pos not in state_store:
            return False
        return state_store[pos]

    for y, line in enumerate(puzzle_input):
        z = 0
        w = 0
        for x, state in enumerate(line):
            pos = Pos(x, y, z, w)
            if state == '#':
                store_state(pos, initial_state, True)

    print(initial_state)

    def get_neighbours(pos):
        positions = set()
        for x in pos.x - 1, pos.x, pos.x + 1:
            for y in pos.y - 1, pos.y, pos.y + 1:
                for z in pos.z - 1, pos.z, pos.z + 1:
                    for w in pos.w - 1, pos.w, pos.w + 1:
                        positions.add(Pos(x, y, z, w))
        positions.remove(pos)
        return positions

    def count_active_neighbours(pos, state_store):
        active = 0
        for neighbouring_pos in get_neighbours(pos):
            if get_state(neighbouring_pos, state_store):
                active += 1
        return active

    def all_positions():
        positions = []
        for x in range(x_bounds[min] - 1, x_bounds[max] + 2):
            for y in range(y_bounds[min] - 1, y_bounds[max] + 2):
                for z in range(z_bounds[min] - 1, z_bounds[max] + 2):
                    for w in range(w_bounds[min] - 1, w_bounds[max] + 2):
                        positions.append(Pos(x, y, z, w))
        return positions

    current_states = initial_state
    for cycle in range(6):
        new_states = {}
        for pos in all_positions():
            active = get_state(pos, current_states)
            active_neighbours = count_active_neighbours(pos, current_states)
            if active and active_neighbours in [2, 3] \
                    or (not active and active_neighbours == 3):
                store_state(pos, new_states, True)
        current_states = new_states

    print('total active:', len(current_states))
Exemple #14
0
def main():
    puzzle_input = load_input(__file__)
    # puzzle_input = load_input(__file__, 'example')
    # puzzle_input = load_input(__file__, 'example2')

    unresolved_rules = {}
    rules = {}
    messages = []
    for line in puzzle_input:
        match = re.match(r'^(\d+): (.*)', line)
        if match:
            rule_number = int(match.group(1))
            rule_text = match.group(2)
            if '"' in rule_text:
                rules[rule_number] = rule_text[1]
            else:
                unresolved_rules[rule_number] = rule_text
        elif line == '':
            pass
        else:
            messages.append(line)

    while len(unresolved_rules) > 0:
        for unresolved_rule_number, unresolved_rule in unresolved_rules.items():
            updated_rule = unresolved_rule
            for rule_number, rule_regex in rules.items():
                if '|' in rule_regex:
                    rule = '(' + rule_regex + ')'
                else:
                    rule = rule_regex
                updated_rule = re.sub(r'\b{}\b'.format(rule_number), rule, updated_rule)
            if not re.search(r'\d', updated_rule):
                rules[unresolved_rule_number] = updated_rule.replace(' ', '')

        for rule_number in rules.keys():
            unresolved_rules.pop(rule_number, None)
        print(unresolved_rules)

    print('Rules:')
    for number, rule in sorted(rules.items()):
        print(number, ':', rule)

    def rule0(text):
        # Use named groups because the inserted rules will be full of groups too
        even_42_31 = r'(?P<r42>{})(?P<middle>.+)(?P<r31>{})'.format(rules[42], rules[31])

        # Must have at least one top-level match of this pattern for rule 0 to be valid
        rule0_match = re.fullmatch(even_42_31, text)
        if rule0_match:
            # This inner rule could potentially only match rule 42
            return rule0_inner(rule0_match.group('middle'))
        return False

    def rule0_inner(text):
        # Use named groups because the inserted rules will be full of groups too
        even_42_31 = r'(?P<r42>{})(?P<middle>.+)(?P<r31>{})'.format(rules[42], rules[31])

        rule0_match = re.fullmatch(even_42_31, text)
        if rule0_match:
            return rule0_inner(rule0_match.group('middle'))
        if re.fullmatch(r'({})+'.format(rules[42]), text):
            return True
        return False

    matches = 0
    for message in messages:
        if rule0(message):
            matches += 1

    print('matches:', matches)
Exemple #15
0
def main():
    puzzle_input = load_input(__file__)
    # puzzle_input = load_input(__file__, 'example')

    WHITE = True
    BLACK = False

    def decode_coord(vectors_string):
        nw = vectors_string.count('nw')
        sw = vectors_string.count('sw')
        ne = vectors_string.count('ne')
        se = vectors_string.count('se')
        ew_string = re.sub(r'[ns][ew]', '', vectors_string)
        e = ew_string.count('e')
        w = ew_string.count('w')
        return ne + se + e * 2 - nw - sw - w * 2, ne + nw - se - sw

    tiles = {}
    for line in puzzle_input:
        tile = decode_coord(line)
        if tile not in tiles:
            tiles[tile] = WHITE
        tiles[tile] = not tiles[tile]

    def get_tile(coord, arrangement) -> bool:
        if coord in arrangement:
            return arrangement[coord]
        return WHITE

    def get_adjacent_coords(coord):
        x, y = coord
        #                    e       w        ne      nw       se       sw
        adjacent_vectors = [(2, 0), (-2, 0), (1, 1), (-1, 1), (1, -1),
                            (-1, -1)]
        return [(x + long, y + lat) for long, lat in adjacent_vectors]

    prev_state = tiles.copy()
    for day in range(100):

        for coord in [
                coord for coord, state in prev_state.items() if state is BLACK
        ]:
            for adjacent_coord in get_adjacent_coords(coord):
                if adjacent_coord not in prev_state:
                    prev_state[adjacent_coord] = WHITE
        new_state = prev_state.copy()

        for coord in new_state:
            adjacent_states = [
                get_tile(adjacent, prev_state)
                for adjacent in get_adjacent_coords(coord)
            ]
            adjacent_black = adjacent_states.count(BLACK)

            if prev_state[coord] == BLACK:
                if adjacent_black == 0 or adjacent_black > 2:
                    new_state[coord] = WHITE
            elif adjacent_black == 2:
                new_state[coord] = BLACK

        prev_state = new_state

        blacks = list(new_state.values()).count(BLACK)
        print('Day', day + 1, ': ', blacks)
Exemple #16
0
def main():
    puzzle_input = load_input(__file__)
    puzzle_input = load_input(__file__, 'example')