''' Created on 4 Dec 2020 @author: Luke ''' ''' Created on 4 Dec 2020 @author: Luke ''' from AOC.config import config config.set_wd(4) import re required_fields = ["byr", "iyr", "eyr", "hgt", "hcl", "ecl", "pid"] def get_field(keyval): regex = r"([a-z]{3}):(.*)" m = re.match(regex, keyval) if m is None: raise RuntimeError(f"No match for {keyval}") return list(map(str.strip, m.groups())) def validate(key, val): if key in ["byr", "iyr", "eyr"]: lims = {"byr":[1920, 2002], "iyr":[2010,2020], "eyr":[2020,2030]} if re.match(r"^[0-9]{4}$", val) is None: return False val = int(val)
''' Created on 4 Dec 2020 @author: Luke ''' from AOC.config import config config.set_wd(1) with open("input.txt") as f: data = list(map(int, f.readlines())) for i in data: for j in data: for k in data: if i + j + k == 2020: print(f"{i} * {j} * {k} = {i*j*k}")
''' Created on 12 Dec 2020 @author: Luke ''' from AOC.config import config config.set_wd(12) import numpy as np import re def get_input(input_type): if input_type == "test": ret = ["F10", "N3", "F7", "R90", "F11"] else: with open("input.txt") as f: ret = list(map(str.strip, f.readlines())) return ret class Ship(object): compass_to_direction = { "E": np.array([1, 0]), "N": np.array([0, 1]), "W": np.array([-1, 0]), "S": np.array([0, -1]) } direction_to_compass = {
''' Created on 10 Dec 2020 @author: Luke ''' from AOC.config import config config.set_wd(10) test_input_1 = [16, 10, 15, 5, 1, 11, 7, 19, 6, 12, 4] test_input_2 = [ 28, 33, 18, 42, 31, 14, 46, 20, 48, 47, 24, 23, 49, 45, 19, 38, 39, 11, 1, 32, 25, 35, 8, 17, 7, 9, 4, 2, 34, 10, 3 ] def run(input): input = sorted(input) diffs = [] for i in range(len(input) - 1): diffs.append(input[i + 1] - input[i]) diff_nos = {} for i in set(diffs): diff_nos[i] = len([d for d in diffs if d == i]) # Add initial diff (smallest values) and finall diff (3) diff_nos[min(input)] += 1 diff_nos[3] += 1 return diff_nos
''' Created on 19 Dec 2020 @author: Luke ''' from AOC.config import config config.set_wd(19) import re def get_input(input_type): if input_type == "test1": rules = ['0: 1 2', '1: "a"', '2: 1 3 | 3 1', '3: "b"'] messages = [] elif input_type == "test2": rules = [ '0: 4 1 5', '1: 2 3 | 3 2', '2: 4 4 | 5 5', '3: 4 5 | 5 4', '4: "a"', '5: "b"' ] messages = ["ababbb", "bababa", "abbbab", "aaabbb", "aaaabbb"] else: rules = [] messages = [] with open("input.txt") as f: for line in f: line = line.strip() if line == "": break rules.append(line) for line in f: messages.append(line.strip())
''' Created on 18 Dec 2020 @author: Luke ''' import re from AOC.config import config config.set_wd(18) import operator def get_input(input_type): if input_type == "test1": return ["1 + 2 * 3 + 4 * 5 + 6"] elif input_type == "test2": return ["1 + (2 * 3) + (4 * (5 + 6))"] elif input_type == "test3": return ["2 * 3 + (4 * 5)"] elif input_type == "test4": return ["5 + (8 * 3 + 9 + 3 * 4 * 3)"] elif input_type == "test5": return ["5 * 9 * (7 * 3 * 3 + 9 * 3 + (8 + 6 * 4))"] elif input_type == "test6": return ["((2 + 4 * 9) * (6 + 9 * 8 + 6) + 6) + 2 + 4 * 2"] else: with open("input.txt") as f: return list(map(str.strip, f.readlines()))
''' Created on 16 Dec 2020 @author: Luke ''' from AOC.config import config config.set_wd(16) import re import numpy as np def get_input(input_type): if input_type == "test": ret = { "rules": ["class: 0-1 or 4-19", "row: 0-5 or 8-19", "seat: 0-13 or 16-19"], "your_ticket": [11, 12, 13], "other_tickets": [[3, 9, 18], [15, 1, 5], [5, 14, 9]] } else: def get_rules(fh): rules = [] for line in fh: if line.strip() == "": return rules rules.append(line.strip()) def get_your_ticket(fh): header = next(fh).strip()
''' Created on 24 Dec 2020 @author: Luke ''' from AOC.config import config config.set_wd(24) import numpy as np class Tile(object): _tile_map = {} WHITE = 0 BLACK = 1 dir_map = {"e":np.array((1,0)), "ne":np.array((0,1)), "nw":np.array((-1,1)), "w":np.array((-1,0)), "sw":np.array((0,-1)), "se":np.array((1,-1))} @staticmethod def parse_directions(directions): ret = [] def get_next_direction(directions): for d in ["se", "sw", "ne", "nw", "w", "e"]: if directions.startswith(d): directions = directions.replace(d, "", 1) return(d, directions)
''' Created on 17 Dec 2020 @author: Luke ''' from AOC.config import config config.set_wd(17) import numpy as np def get_input(input_type): if input_type == "test": arr =[[".","#","."], [".",".","#"], ["#","#","#"]] else: arr = [] with open("input.txt") as f: for line in f: arr.append(list(line.strip())) return np.array(arr, dtype="S1") class ConwayCube(object): # Need to use byte form as numpy turns S1 into byte array and can only do element-wise # comparisons if comparing with byte object active = b"#" inactive = b"." def __init__(self, initial_state):
''' Created on 7 Dec 2020 @author: Luke ''' from AOC.config import config config.set_wd(7) import re bag_list = {} class Bag(object): def __init__(self, description): global bag_list self.description = description self.parse_description() if self.colour in bag_list: raise RuntimeError(f"{self.colour} already in list") bag_list[self.colour] = self self.parents = [] self.ancestors = [] def parse_description(self): regex = r'([a-z]+ [a-z]+) bags contain (.*)\.' match = re.match(regex, self.description) if match is None: raise RuntimeError(f"Cannot parse description {self.description}") self.colour = match.group(1)
''' Created on 21 Dec 2020 @author: Luke ''' from AOC.config import config config.set_wd(21) import re def get_input(input_type): if input_type == "test": ret = [ "mxmxvkd kfcds sqjhc nhms (contains dairy, fish)", "trh fvjkl sbzzf mxmxvkd (contains dairy)", "sqjhc fvjkl (contains soy)", "sqjhc mxmxvkd sbzzf (contains fish)" ] else: ret = [] with open("input.txt") as f: for line in f: ret.append(line.strip()) return ret # For each allergen git a list of all foods that contain it # Then intersection of foods are possible allergens def get_allergen_foods(_input): allergen_d = {}
''' Created on 8 Dec 2020 @author: Luke ''' from AOC.config import config config.set_wd(8) import copy class Boot(object): def __init__(self): self.accumulator = 0 self.index = 0 self.original_instructions = [] self.instructions = [] self.run_indexes = [] def parse_instructions(self, instructions): for i in instructions: (op, arg) = map(str.strip, i.split()) arg = int(arg) self.original_instructions.append((op, arg)) self.instructions = copy.copy(self.original_instructions) def can_switch_instruction(self, index): return self.original_instructions[index][0] in ("nop", "jmp") def switch_instruction(self, index):
''' Created on 9 Dec 2020 @author: Luke ''' from AOC.config import config config.set_wd(9) import itertools import numpy as np class NumberChecker(object): def __init__(self, input_filename, N): self.N = N self.file_obj = open(input_filename) self.data = [] self.all_data = [] for _ in range(N): next_val = int(self.file_obj.readline().strip()) self.data.append(next_val) self.all_data.append(next_val) def find_weakness(self): """ Find contiguous sum in all data that sums to invalid number. Return sum of smallest and largest value """ for start in itertools.count(start=0): for length in itertools.count(start=2): subset = self.all_data[start:start + length]
''' Created on 23 Dec 2020 @author: Luke ''' from AOC.config import config config.set_wd(23) class Cups(object): def __init__(self, cups): self.cups = cups self.current_cup = cups[0] self.picked_three = None self.destinaion = None def set_three(self): three = [] #print(f"Picking three - cups = {self.cups}, current cup = {self.current_cup}") for _ in range(3): three.append( self.cups.pop( (self.cups.index(self.current_cup) + 1) % len(self.cups))) self.picked_three = three #print(f"Three = {self.picked_three}, cups = {self.cups}") def select_destination(self): #print("Selecting destination") dest = self.current_cup while True:
''' Created on 5 Dec 2020 @author: Luke ''' ''' Created on 5 Dec 2020 @author: Luke ''' from AOC.config import config config.set_wd(5) import math def get_partitioner(length, low_char, high_char): def get_row(char_str): if not length == (2**len(char_str)): raise RuntimeError( f"Expected character string of length {int(math.log2(length))}" ) row_low = 0 row_high = length for c in char_str: if c == low_char: row_high = (row_low + row_high) // 2 elif c == high_char: row_low = (row_low + row_high) // 2 else:
''' Created on 20 Dec 2020 @author: Luke ''' from AOC.config import config config.set_wd(20) import numpy as np import re class Tile(object): LEFT = 0 UP = 1 RIGHT = 2 DOWN = 3 def __init__(self, _id, tile): self.id = _id self.tile = tile self.unordered_borders = [ tile[0, :], tile[-1, :], tile[:, 0], tile[:, -1] ] self.ordered_borders = [None] * 4 self.unordered_neighbours = [] self.ordered_neighbours = [None] * 4 self.position = None
''' Created on 13 Dec 2020 @author: Luke ''' ''' Created on 13 Dec 2020 @author: Luke ''' from AOC.config import config config.set_wd(13) import numpy as np def get_input(input_type): if input_type == "test1": ret = {"earliest_t": 939, "buses": "7,13,x,x,59,x,31,19".split(",")} elif input_type == "test2": ret = {"earliest_t": None, "buses": "17,x,13,19".split(",")} elif input_type == "test3": ret = {"earliest_t": None, "buses": "67,7,59,61".split(",")} elif input_type == "test4": ret = {"earliest_t": None, "buses": "67,x,7,59,61".split(",")} elif input_type == "test5": ret = {"earliest_t": None, "buses": "67,7,x,59,61".split(",")} elif input_type == "test6": ret = {"earliest_t": None, "buses": "1789,37,47,1889".split(",")} else:
''' Created on 14 Dec 2020 @author: Luke ''' from AOC.config import config config.set_wd(14) import re import numpy as np class Memory(object): def __init__(self): self.bitmask = None self.memory = {} def set_bitmask(self, bitmask): self.bitmask = list(bitmask) def set_memory(self, address, value): for a in self.get_addresses(address): self.memory[a] = value def int_to_list(self, value, bitlen=36): """ Create 36-bit list of 0s and 1s """ ret = []
''' Created on 22 Dec 2020 @author: Luke ''' from AOC.config import config config.set_wd(22) class Player(object): def __init__(self, _id, deck): # Index 0 = top of deck self.deck = deck self.id = _id def can_play(self): return bool(self.deck) def get_next(self): if self.deck: return self.deck.pop(0) else: return None def add_cards(self, cards): """ Add back to deck - highest card on top """
''' Created on 15 Dec 2020 @author: Luke ''' from AOC.config import config config.set_wd(15) def get_input(input_type): if input_type == "test1": return [0, 3, 6] elif input_type == "test2": return [1, 3, 2] elif input_type == "test3": return [2, 1, 3] elif input_type == "test4": return [1, 2, 3] elif input_type == "test5": return [2, 3, 1] elif input_type == "test6": return [3, 2, 1] elif input_type == "test7": return [3, 1, 2] else: return [9, 3, 1, 0, 8, 4] class MemoryGame(object): def __init__(self, starting_nums):
''' Created on 6 Dec 2020 @author: Luke ''' from AOC.config import config config.set_wd(6) import numpy as np def num_all_yes(group): s = set(group[0]) for ns in group[1:]: s = s.intersection(ns) return len(s) group_any_yes = [] with open("input.txt") as f: group = [] for line in f.readlines(): line = line.rstrip() if line == "": group_any_yes.append(num_all_yes(group)) group = [] else: group.append(line) # Need to add final group! group_any_yes.append(num_all_yes(group))
''' Created on 4 Dec 2020 @author: Luke ''' ''' Created on 4 Dec 2020 @author: Luke ''' from AOC.config import config config.set_wd(3) def run(x_increment, y_increment): def process_line(x_coord, trees_encountered, line): if line[x_coord - 1] == "#": trees_encountered += 1 x_coord += x_increment if x_coord > len(line): x_coord = x_coord % len(line) return (x_coord, trees_encountered) x_coord = 1 trees_encountered = 0 lineno = 0 with open("input.txt") as f: for line in f.readlines(): if (lineno % y_increment) == 0: (x_coord,