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)
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
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)
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)
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)
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)
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
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)
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
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)
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)
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)
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))
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)
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)
def main(): puzzle_input = load_input(__file__) puzzle_input = load_input(__file__, 'example')