My Advent of Code solutions in Python. kevinyap.ca/2019/12/going-fast-in-advent-of-code/
advent-of-code python
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Dump solutions to 2015 problems

+1083
+17
2015/day01.py
··· 1 + import fileinput 2 + 3 + floor = 0 4 + basement = None 5 + 6 + for line in fileinput.input(): 7 + for i, char in enumerate(line, start=1): 8 + if char == "(": 9 + floor += 1 10 + elif char == ")": 11 + floor -= 1 12 + 13 + if floor == -1 and basement is None: 14 + basement = i 15 + 16 + print "Santa's floor: %d" % floor 17 + print "Entered basement at character %d" % basement
+14
2015/day02.py
··· 1 + import fileinput 2 + 3 + paper = 0 4 + ribbon = 0 5 + 6 + for line in fileinput.input(): 7 + l, w, h = [int(n) for n in line.strip().split('x')] 8 + a, b, c = l*w, w*h, h*l 9 + 10 + paper += 2*(a+b+c) + min(a, b, c) 11 + ribbon += (l*w*h) + 2*(l+w+h - max(l, w, h)) 12 + 13 + print "ft^2 of paper: %d" % paper 14 + print "Feet of ribbon: %d" % ribbon
+23
2015/day03.py
··· 1 + import fileinput 2 + from collections import namedtuple 3 + 4 + DIRS = { 5 + "^": (0, 1), 6 + ">": (1, 0), 7 + "v": (0, -1), 8 + "<": (-1, 0), 9 + } 10 + 11 + def visit_houses(path): 12 + house = (0, 0) 13 + for c in path: 14 + yield house 15 + house = tuple(map(sum, zip(house, DIRS[c]))) 16 + 17 + path = fileinput.input()[0].strip()) 18 + 19 + year_1_houses = set(visit_houses(path)) 20 + year_2_houses = set(visit_houses(path[::2])) | set(visit_houses(path[1::2])) 21 + 22 + print "Houses visited in year 1: %d" % len(year_1_houses) 23 + print "Houses visited in year 2: %d" % len(year_2_houses)
+28
2015/day04.py
··· 1 + import fileinput 2 + import hashlib 3 + 4 + secret_key = fileinput.input()[0].strip() 5 + i = 1 6 + 7 + hash1 = None 8 + hash2 = None 9 + 10 + print "Searching for hashes..." 11 + 12 + while True: 13 + s = secret_key + str(i) 14 + h = hashlib.md5(s).hexdigest() 15 + 16 + if h[:5] == "00000": 17 + hash1 = (s, h) if hash1 == None else hash1 18 + 19 + if h[:6] == "000000": 20 + hash2 = (s, h) if hash2 == None else hash2 21 + 22 + if hash1 and hash2: 23 + break 24 + else: 25 + i += 1 26 + 27 + print "Hashed %s to %s" % hash1 28 + print "Hashed %s to %s" % hash2
+51
2015/day05.py
··· 1 + import fileinput 2 + import re 3 + 4 + VOWELS = {'a', 'e', 'i', 'o', 'u'} 5 + NAUGHTY = {'ab', 'cd', 'pq', 'xy'} 6 + 7 + nice_count_1 = 0 8 + nice_count_2 = 0 9 + 10 + for string in fileinput.input(): 11 + # Ruleset 1 12 + num_letters = len(string) 13 + num_vowels = 0 14 + double_letter = False 15 + 16 + for i, char in enumerate(string): 17 + if char in VOWELS: 18 + num_vowels += 1 19 + 20 + if i < num_letters - 1: 21 + if char + string[i+1] in NAUGHTY: 22 + break 23 + 24 + if char == string[i+1]: 25 + double_letter = True 26 + 27 + else: 28 + if double_letter and (num_vowels >= 3): 29 + nice_count_1 += 1 30 + 31 + 32 + # Ruleset 2 33 + overlap = False 34 + is_pair = False 35 + 36 + for i, char in enumerate(string): 37 + if i < len(string) - 2: 38 + if char == string[i+2]: 39 + overlap = True 40 + break 41 + 42 + if re.search(r'(..).*\1', string): 43 + is_pair = True 44 + 45 + 46 + if overlap and is_pair: 47 + nice_count_2 += 1 48 + 49 + 50 + print "Number of nice strings (first ruleset): %d" % nice_count_1 51 + print "Number of nice strings (second ruleset): %d" % nice_count_2
+26
2015/day06.py
··· 1 + import fileinput 2 + import re 3 + 4 + lights = [[0 for x in range(1000)] for x in range(1000)] 5 + 6 + for line in fileinput.input(): 7 + cmd, x1, y1, x2, y2 = re.match(r'(.+) (\d+),(\d+) through (\d+),(\d+)', line).groups() 8 + x1 = int(x1) 9 + x2 = int(x2) 10 + y1 = int(y1) 11 + y2 = int(y2) 12 + 13 + for x in range(x1, x2+1): 14 + for y in range(y1, y2+1): 15 + if cmd == "turn off": 16 + # lights[x][y] = 0 17 + lights[x][y] = max(0, lights[x][y] - 1) 18 + elif cmd == "turn on": 19 + # lights[x][y] = 1 20 + lights[x][y] += 1 21 + elif cmd == "toggle": 22 + # lights[x][y] ^= 1 23 + lights[x][y] += 2 24 + 25 + # print "Lights on: %d" % sum([sum(x) for x in lights]) 26 + print "Total brightness: %d" % sum([sum(x) for x in lights])
+57
2015/day07.py
··· 1 + import fileinput 2 + import re 3 + 4 + connections = {} 5 + 6 + # Set up dictionary of connections 7 + for line in fileinput.input(): 8 + rule, wire = re.search(r'(.*) -> (.*)', line).groups() 9 + value = None 10 + 11 + if len(rule.split()) == 1: 12 + value = (rule,) 13 + elif 'NOT' in rule: 14 + value = ('NOT', rule.split()[1]) 15 + else: 16 + value = (rule.split()[1], rule.split()[0], rule.split()[2]) 17 + 18 + connections[wire] = value 19 + 20 + connections2 = connections.copy() 21 + 22 + def follow(wire, c): 23 + rule = c[wire] 24 + val = None 25 + 26 + # Base case 27 + if len(rule) == 1: 28 + if rule[0].isdigit(): 29 + return int(rule[0]) 30 + else: 31 + return follow(rule[0], c) 32 + 33 + elif len(rule) == 2: 34 + return ~follow(rule[1], c) 35 + 36 + else: 37 + if rule[0] == 'AND': 38 + val = (int(rule[1]) if rule[1].isdigit() else follow(rule[1], c)) & (int(rule[2]) if rule[2].isdigit() else follow(rule[2], c)) 39 + elif rule[0] == 'OR': 40 + val = (int(rule[1]) if rule[1].isdigit() else follow(rule[1], c)) | (int(rule[2]) if rule[2].isdigit() else follow(rule[2], c)) 41 + elif rule[0] == 'LSHIFT': 42 + val = follow(rule[1], c) << int(rule[2]) 43 + elif rule[0] == 'RSHIFT': 44 + val = follow(rule[1], c) >> int(rule[2]) 45 + 46 + if type(val) is int: 47 + c[wire] = (str(val),) 48 + 49 + return val 50 + 51 + s = follow('a', connections) 52 + 53 + print "Signal to wire a: %d" % s 54 + 55 + connections2['b'] = (str(s), ) 56 + 57 + print "After overriding b to %s, signal to a is %d" % (s, follow('a', connections2))
+17
2015/day08.py
··· 1 + import fileinput 2 + import ast 3 + import re 4 + 5 + chars = 0 6 + lits = 0 7 + encoded_chars = 0 8 + 9 + for line in fileinput.input(): 10 + line = line.strip() # strip newline 11 + 12 + chars += len(line) 13 + lits += len(ast.literal_eval(line)) 14 + encoded_chars += (len(re.escape(line)) + 2 ) 15 + 16 + print "chars - literals = %d" % (chars - lits) 17 + print "encoded_chars - chars = %d" % (encoded_chars - chars)
+33
2015/day09.py
··· 1 + #!/usr/bin/env python 2 + import fileinput 3 + import itertools 4 + import re 5 + 6 + def parse_line(line): 7 + departure, arrival, distance = re.match(r'(\w+) to (\w+) = (\d+)', line).groups() 8 + return departure, arrival, int(distance) 9 + 10 + def sum_of_paths(d): 11 + # Yield sum of all possible routes 12 + for p in itertools.permutations(locations, len(locations)): 13 + yield sum(d[p[i]][p[i+1]] for i in range(len(p) - 1)) 14 + 15 + distances = {} 16 + 17 + # Parse input 18 + for line in fileinput.input(): 19 + frm, to, dist = parse_line(line) 20 + if distances.get(frm): 21 + distances[frm][to] = dist 22 + else: 23 + distances[frm] = {to: dist} 24 + 25 + if distances.get(to): 26 + distances[to][frm] = dist 27 + else: 28 + distances[to] = {frm: dist} 29 + 30 + locations = distances.keys() 31 + 32 + print "Shortest route: %d" % min(sum_of_paths(distances)) 33 + print "Longest route: %d" % max(sum_of_paths(distances))
+28
2015/day10.py
··· 1 + import fileinput 2 + 3 + string = fileinput.input()[0].strip() 4 + 5 + def look_and_say(s): 6 + occurences = 0 7 + number = s[0] 8 + ret = '' 9 + 10 + for n in s: 11 + if number == n: 12 + occurences += 1 13 + else: 14 + ret += str(occurences) + number 15 + number = n 16 + occurences = 1 17 + 18 + return ret + str(occurences) + number 19 + 20 + for _ in range(40): 21 + string = look_and_say(string) 22 + 23 + print "Length after 40 iterations: %d" % len(string) 24 + 25 + for _ in range(10): 26 + string = look_and_say(string) 27 + 28 + print "Length after 50 iterations: %d" % len(string)
+40
2015/day11.py
··· 1 + import fileinput 2 + 3 + password = fileinput.input()[0].strip() 4 + 5 + def is_valid(p): 6 + if any(x in p for x in ['i', 'o', 'l']): 7 + return False 8 + 9 + straight = False 10 + pairs = set() 11 + 12 + for i in range(len(p) - 2): 13 + if ord(p[i]) == ord(p[i+1]) - 1 == ord(p[i+2]) - 2: 14 + straight = True 15 + if p[i] == p[i+1]: 16 + pairs.add(p[i]) 17 + 18 + if p[-1] == p[-2]: 19 + pairs.add(p[-1]) 20 + 21 + return straight and (len(pairs) >= 2) 22 + 23 + def next_pass(p): 24 + # Recursion is fantastic. 25 + if p == 'z': 26 + return 'a' 27 + elif p[-1] == 'z': 28 + return next_pass(p[:-1]) + 'a' 29 + else: 30 + return p[:-1] + chr(ord(p[-1]) + 1) 31 + 32 + 33 + print "Santa's current password is %s" % password 34 + 35 + for _ in range(2): 36 + while True: 37 + password = next_pass(password) 38 + if is_valid(password): 39 + print "His next password should be %s" % password 40 + break
+31
2015/day12.py
··· 1 + import fileinput 2 + import re 3 + import json 4 + 5 + def no_red_sum(x): 6 + s = 0 7 + 8 + if type(x) == list: 9 + for y in x: 10 + if type(y) == int: 11 + s += y 12 + else: 13 + s += no_red_sum(y) 14 + elif type(x) == dict: 15 + for k in x.keys(): 16 + if x[k] == 'red': 17 + return 0 18 + s += no_red_sum(x[k]) 19 + elif type(x) == int: 20 + return x 21 + 22 + return s 23 + 24 + 25 + document = fileinput.input()[0] 26 + 27 + print "Sum of all numbers: %d" % sum(int(x) for x in re.findall('-?\d+', document)) 28 + 29 + j = json.loads(document) 30 + 31 + print "Sum of non-reds items: %d" % no_red_sum(j)
+40
2015/day13.py
··· 1 + #!/usr/bin/env python 2 + import fileinput 3 + import itertools 4 + import re 5 + 6 + def parse_line(line): 7 + p1, gl, diff, p2 = re.search(r'(\w+) .* (gain|lose) (\d+) .* (\w+).', line).groups() 8 + return p1, p2, int(diff) * (-1 if gl == 'lose' else 1) 9 + 10 + def happiness_deltas(d): 11 + # Not optimal because it includes cyclic permutations and reversals 12 + for p in itertools.permutations(d.keys(), len(d.keys())): 13 + total = 0 14 + for i in range(len(p)): 15 + total += d[p[i]][p[(i+1) % len(p)]] 16 + total += d[p[(i+1) % len(p)]][p[i]] 17 + 18 + yield total 19 + 20 + 21 + people = {} 22 + 23 + # Parse input 24 + for line in fileinput.input(): 25 + p1, p2, diff = parse_line(line) 26 + if p1 in people: 27 + people[p1][p2] = diff 28 + else: 29 + people[p1] = {p2: diff} 30 + 31 + 32 + print "Largest total change in happiness: %d" % max(happiness_deltas(people)) 33 + 34 + # Add self into list of keys for second part of problem 35 + for person in people.keys(): 36 + people[person]["iKevinY"] = 0 37 + 38 + people["iKevinY"] = {person: 0 for person in people.keys()} 39 + 40 + print "After adding me: %d" % max(happiness_deltas(people))
+26
2015/day14.py
··· 1 + import fileinput 2 + import re 3 + 4 + def fly(time, speed, fly_for, rest_for): 5 + cycle = fly_for + rest_for 6 + return speed * ( 7 + ((time // cycle) * fly_for) + # flying time * full cycles completed 8 + min(fly_for, time % cycle) # remaining incomplete flying time 9 + ) 10 + 11 + TOTAL_TIME = 2503 12 + 13 + reindeer = [[int(n) for n in re.findall(r'\d+', line)] for line in fileinput.input()] 14 + 15 + print "Winning reindeer distance: %d" % max(fly(TOTAL_TIME, *r) for r in reindeer) 16 + 17 + points = [0] * len(reindeer) 18 + 19 + for t in range(1, TOTAL_TIME + 1): 20 + positions = [fly(t, *r) for r in reindeer] 21 + for i, pos in enumerate(positions): 22 + if pos == max(positions): 23 + points[i] += 1 24 + 25 + print "Highest point total: %d" % max(points) 26 +
+19
2015/day15.py
··· 1 + import fileinput 2 + import itertools 3 + import re 4 + 5 + def mul(vals): 6 + ret = 1 7 + for x in vals: 8 + ret *= x 9 + return ret 10 + 11 + ingredients = [[int(n) for n in re.findall(r'\w+ (-?\d+)', line)] for line in fileinput.input()] 12 + 13 + cookies = [] 14 + 15 + for combo in itertools.combinations_with_replacement(ingredients, 100): # 100 teaspoons of ingredients 16 + cookies.append([max(0, sum(c)) for c in tuple(zip(*combo))]) 17 + 18 + print "Best cookie total: %d" % max(mul(c[:-1]) for c in cookies) 19 + print "Best 500 calories: %d" % max(mul(c[:-1]) for c in cookies if c[-1] == 500)
+37
2015/day16.py
··· 1 + import fileinput 2 + import re 3 + 4 + TICKER = { 5 + 'children': 3, 6 + 'cats': 7, 7 + 'samoyeds': 2, 8 + 'pomeranians': 3, 9 + 'akitas': 0, 10 + 'vizslas': 0, 11 + 'goldfish': 5, 12 + 'trees': 3, 13 + 'cars': 2, 14 + 'perfumes': 1, 15 + } 16 + 17 + sues = [{k: int(v) for k, v in re.findall(r'(\w+): (\d+)', line)} for line in fileinput.input()] 18 + 19 + def which_sue(sues, outdated=False): 20 + for i, sue in enumerate(sues, start=1): 21 + for key in sue: 22 + if outdated and (key in ('cats', 'trees')): 23 + if not (sue[key] > TICKER[key]): 24 + break 25 + elif outdated and (key in ('pomeranians', 'goldfish')): 26 + if not (sue[key] < TICKER[key]): 27 + break 28 + 29 + elif TICKER[key] != sue[key]: 30 + break 31 + else: 32 + return i 33 + else: 34 + return -1 35 + 36 + print "The gift was from Sue #%d." % which_sue(sues) 37 + print "With ranges gives Sue #%d." % which_sue(sues, outdated=True)
+21
2015/day17.py
··· 1 + import fileinput 2 + import itertools 3 + import re 4 + 5 + LITRES = 150 6 + 7 + def permute_containers(containers, litres): 8 + for i in range(1, len(containers) + 1): 9 + for c in itertools.combinations(containers, i): 10 + if sum(c) == litres: 11 + yield c 12 + 13 + containers = [int(n) for n in fileinput.input()] 14 + permutations = list(permute_containers(containers, LITRES)) 15 + min_perm_len = min(len(p) for p in permutations) 16 + 17 + print "Containers: {}".format(containers) 18 + print "Litres of eggnog: %d" % LITRES 19 + 20 + print "Valid combinations: %d" % len(permutations) 21 + print "Minimal combinations: %d" % len([p for p in permutations if len(p) == min_perm_len])
+62
2015/day18.py
··· 1 + import fileinput 2 + import itertools 3 + import re 4 + 5 + def get_neighbours(x, y): 6 + square = [(a, b) 7 + for a in range(max(0, x-1), min(x+2, WIDTH)) 8 + for b in range(max(0, y-1), min(y+2, HEIGHT))] 9 + square.remove((x, y)) 10 + return square 11 + 12 + def next_state(grid, x, y, sticky_corners=False): 13 + if sticky_corners: 14 + if (x in (0, WIDTH-1)) and (y in (0, HEIGHT-1)): 15 + return True 16 + 17 + neighbours = get_neighbours(x, y) 18 + on_neighbours = sum(grid[b][a] for a, b in neighbours) 19 + 20 + if grid[y][x]: 21 + return int(on_neighbours in (2, 3)) 22 + else: 23 + return int(on_neighbours == 3) 24 + 25 + def next_grid(grid, sticky_corners=False): 26 + return [[next_state(grid, x, y, sticky_corners) for x in range(WIDTH)] for y in range(HEIGHT)] 27 + 28 + def count_lights(grid): 29 + return sum(sum(x for x in row) for row in grid) 30 + 31 + def print_grid(grid): 32 + for row in grid: 33 + print ''.join('#' if c else '.' for c in row) 34 + 35 + 36 + # Parse input 37 + rows = [line for line in fileinput.input()] 38 + LIGHTS = [[1 if c == '#' else 0 for c in row.strip()] for row in rows] 39 + 40 + # Define constants 41 + WIDTH = len(LIGHTS[0]) 42 + HEIGHT = len(LIGHTS) 43 + ITERATIONS = 100 44 + 45 + print "Simulating {}x{} grid of lights.".format(WIDTH, HEIGHT) 46 + 47 + grid = LIGHTS 48 + for _ in range(ITERATIONS): 49 + grid = next_grid(grid) 50 + 51 + print "Lights on after {} iterations: {}".format(ITERATIONS, count_lights(grid)) 52 + 53 + # Stick corners initially 54 + grid = LIGHTS 55 + for x in (0, WIDTH-1): 56 + for y in (0, HEIGHT-1): 57 + grid[y][x] = 1 58 + 59 + for _ in range(ITERATIONS): 60 + grid = next_grid(grid, sticky_corners=True) 61 + 62 + print "Lights on with stuck corners: {}".format(count_lights(grid))
+107
2015/day19.py
··· 1 + import fileinput 2 + import itertools 3 + import re 4 + import sys 5 + 6 + REPLACEMENTS = [] 7 + ELECTRONS = [] 8 + MOLECULE = "" 9 + MOL_LEN = len(MOLECULE) 10 + 11 + for line in fileinput.input(): 12 + if "=>" in line: 13 + m = re.findall(r'\w+', line) 14 + if m[0] == "e": 15 + ELECTRONS.append(m[1]) 16 + else: 17 + REPLACEMENTS.append(m) 18 + else: 19 + MOLECULE = line.strip() 20 + 21 + print "Elements:", len(re.findall(r'[A-Z]', MOLECULE)) 22 + print "Rn/Ar:", len(re.findall(r'Rn', MOLECULE)), "* 2" 23 + print "Y:", len(re.findall(r'Y', MOLECULE)) 24 + 25 + # molecule = MOLECULE 26 + 27 + # reps = {m[1][::-1]: m[0][::-1] 28 + # for m in REPLACEMENTS} 29 + 30 + # def rep(x): 31 + # return reps[x.group()] 32 + 33 + # count = 0 34 + # while molecule != 'e': 35 + # molecule = re.sub('|'.join(reps.keys()), rep, molecule, 1) 36 + # count += 1 37 + 38 + # print(count) 39 + 40 + sys.exit() 41 + 42 + 43 + outs = set() 44 + 45 + def molefy(mole): 46 + for old, new in REPLACEMENTS: 47 + for pos in (m.start() for m in re.finditer(old, mole)): 48 + new_string = mole[:pos] + new + mole[pos+len(old):] 49 + yield new_string 50 + 51 + def delete(mole): 52 + for old, new in REPLACEMENTS: 53 + for pos in (m.start() for m in re.finditer(new, mole)): 54 + new_string = mole[:pos] + old + mole[pos+len(new):] 55 + yield new_string 56 + 57 + def replace(old, new, s): 58 + for pos in (m.start() for m in re.finditer(new, s)): 59 + yield "{}{}{}".format(s[:pos], old, s[pos+len(new):]) 60 + 61 + 62 + LOWEST = 100000 63 + 64 + def submol(m, i=1,): 65 + if m in ELECTRONS: 66 + LOWEST = min(LOWEST, i) 67 + 68 + for r in REPLACEMENTS: 69 + if r[1] in m: 70 + for x in replace(r[0], r[1], m): 71 + submol(x, i+1) 72 + 73 + 74 + submol(MOLECULE) 75 + print LOWEST 76 + 77 + sys.exit() 78 + 79 + iterations = 1 80 + mols = set() 81 + mols.add(MOLECULE) 82 + new_mols = set() 83 + # seen = set() 84 + 85 + while True: 86 + iterations += 1 87 + for m in mols: 88 + for r in REPLACEMENTS: 89 + if r[1] in m: 90 + for x in replace(r[0], r[1], m): 91 + if x in ELECTRONS: 92 + print iterations 93 + sys.exit() 94 + 95 + # if x not in seen: 96 + new_mols.add(x) 97 + # seen.add(x) 98 + 99 + if len(new_mols) == 0: 100 + sys.exit("BLALALLA") 101 + 102 + mols = new_mols 103 + new_mols = set() 104 + 105 + 106 + 107 + # print len(outs)
+23
2015/day20.py
··· 1 + import fileinput 2 + 3 + PRESENTS = int(fileinput.input()[0].strip()) 4 + 5 + def factors(n): 6 + return set(x for tup in ([i, n//i] 7 + for i in range(1, int(n**0.5)+1) if n % i == 0) for x in tup) 8 + 9 + # house = 1 10 + 11 + # while True: 12 + # if sum(factors(house)) * 10 >= PRESENTS: 13 + # print house 14 + # break 15 + # house += 1 16 + 17 + house = 1 18 + 19 + while True: 20 + if sum(x for x in factors(house) if (x * 50 >= house)) * 11 >= PRESENTS: 21 + print house 22 + break 23 + house += 1
+76
2015/day21.py
··· 1 + import fileinput 2 + import itertools 3 + from collections import namedtuple 4 + 5 + Item = namedtuple('Item', ['name', 'cost', 'dmg', 'arm']) 6 + Char = namedtuple('Character', ['hp', 'dmg', 'arm']) 7 + 8 + # Cost, Damage, Armour 9 + WEAPONS = [ 10 + Item("Dagger", 8, 4, 0), 11 + Item("Shortsword", 10, 5, 0), 12 + Item("Warhammer", 25, 6, 0), 13 + Item("Longsword", 40, 7, 0), 14 + Item("Greataxe", 74, 8, 0), 15 + ] 16 + 17 + ARMOUR = [ 18 + Item("Leather", 13, 0, 1), 19 + Item("Chainmail", 31, 0, 2), 20 + Item("Splintmail", 53, 0, 3), 21 + Item("Bandedmail", 75, 0, 4), 22 + Item("Platemail", 102, 0, 5), 23 + ] 24 + 25 + RINGS = [ 26 + Item("Damage +1", 25, 1, 0), 27 + Item("Damage +2", 50, 2, 0), 28 + Item("Damage +3", 100, 3, 0), 29 + Item("Defense +1", 20, 0, 1), 30 + Item("Defense +2", 40, 0, 2), 31 + Item("Defense +3", 80, 0, 3), 32 + ] 33 + 34 + # HP, Damage, Armour 35 + boss_data = [] 36 + 37 + for line in fileinput.input(): 38 + boss_data.append(int(line.split(': ')[1])) 39 + 40 + BOSS = Char(*boss_data) 41 + HP = 100 42 + 43 + def boss_fight(player_hp, boss, weapon, armour, rings): 44 + player_dmg = weapon.dmg + sum(r.dmg for r in rings) 45 + player_arm = sum(a.arm for a in armour) + sum(r.arm for r in rings) 46 + player = Char(player_hp, player_dmg, player_arm) 47 + 48 + loadout = ', '.join((x for x in (weapon.name, ', '.join(a.name for a in armour), ', '.join(r.name for r in rings)) if x)) 49 + loadout_cost = weapon.cost + sum(a.cost for a in armour) + sum(r.cost for r in rings) 50 + 51 + player_hurt = boss_hurt = 0 52 + 53 + while player.hp - player_hurt > 0: 54 + boss_hurt += max(1, player.dmg - boss.arm) 55 + 56 + if boss.hp - boss_hurt <= 0: 57 + return True, loadout_cost, loadout 58 + 59 + player_hurt += max(1, boss.dmg - player.arm) 60 + 61 + return False, loadout_cost, loadout 62 + 63 + 64 + loadouts = [] 65 + 66 + # 1 weapon, 0-1 armour, 0-2 rings 67 + for weapon in WEAPONS: 68 + for num_a in range(2): 69 + for armour in itertools.combinations(ARMOUR, num_a): 70 + for num_r in range(3): 71 + for rings in itertools.combinations(RINGS, num_r): 72 + loadouts.append((weapon, armour, rings)) 73 + 74 + 75 + print "Optimal spend to win: {}g <{}>".format(*min((c, l) for win, c, l in (boss_fight(HP, BOSS, *l) for l in loadouts) if win)) 76 + print "Worst spend and lose: {}g <{}>".format(*max((c, l) for win, c, l in (boss_fight(HP, BOSS, *l) for l in loadouts) if not win))
+183
2015/day22.py
··· 1 + import fileinput 2 + import itertools 3 + import sys 4 + from collections import namedtuple 5 + 6 + class Character: 7 + def __init__(self, name, hp, mp, dmg, armour): 8 + # Stats 9 + self.name = name 10 + self.hp = hp 11 + self.mp = mp 12 + self.dmg = dmg 13 + self.armour = armour 14 + 15 + # Status Effects 16 + self.shielded = 0 17 + self.poisoned = 0 18 + self.recharging = 0 19 + 20 + def print_player(self): 21 + print '{} has {} hit points, {} mana, {} armour'.format(self.name, self.hp, self.mp, self.armour) 22 + 23 + def print_boss(self): 24 + print '{} has {} hit points'.format(self.name, self.hp) 25 + 26 + def process_status(self): 27 + if self.shielded: 28 + self.shielded -= 1 29 + self.armour = 7 30 + # print "Shield's timer is now {}.".format(self.shielded) 31 + if self.shielded == 0: 32 + self.armour = 0 33 + # print 'Shield wears off.' 34 + 35 + if self.poisoned: 36 + self.poisoned -= 1 37 + self.hp -= 3 38 + # print 'Poison deals 3 damage; its timer is now {}.'.format(self.poisoned) 39 + if self.poisoned == 0: 40 + pass 41 + # print 'Poison effects expired.' 42 + 43 + if self.recharging: 44 + self.recharging -= 1 45 + self.mp += 101 46 + # print 'Recharge provides 101 mana; its timer is now {}.'.format(self.recharging) 47 + if self.recharging == 0: 48 + pass 49 + # print 'Recharge wears off.' 50 + 51 + 52 + Spell = namedtuple('Spell', ['name', 'cost', 'dmg', 'heal', 'effect', 'duration']) 53 + 54 + # Magic Missile costs 53 mana. It instantly does 4 damage. 55 + # Drain costs 73 mana. It instantly does 2 damage and heals you for 2 hit points. 56 + # Shield costs 113 mana. It starts an effect that lasts for 6 turns. While it is active, your armor is increased by 7. 57 + # Poison costs 173 mana. It starts an effect that lasts for 6 turns. At the start of each turn while it is active, it deals the boss 3 damage. 58 + # Recharge costs 229 mana. It starts an effect that lasts for 5 turns. At the start of each turn while it is active, it gives you 101 new mana. 59 + SPELLS = [ 60 + Spell('Missl', 53, 4, 0, None, None), 61 + Spell('Drain', 73, 2, 2, None, None), 62 + Spell('Shild', 113, 0, 0, 'shielded', 6), 63 + Spell('Poisn', 173, 0, 0, 'poisoned', 6), 64 + Spell('Rchrg', 229, 0, 0, 'recharge', 5), 65 + ] 66 + 67 + 68 + def boss_fight(player, boss, spell_order, hard_mode=False): 69 + spent_mana = 0 70 + effects = [] 71 + # print ', '.join(s.name for s in spell_order) 72 + 73 + for spell in spell_order: 74 + if hard_mode: 75 + player.hp -= 1 76 + if player.hp <= 0: 77 + # print 'Player died. Game over. :(' 78 + return sys.maxint 79 + 80 + # print '-- Player Turn --' 81 + # player.print_player() 82 + # boss.print_boss() 83 + 84 + player.process_status() 85 + boss.process_status() 86 + 87 + 88 + # Pick a spell to use 89 + if spell.cost > player.mp: 90 + # print 'Not enough mana to cast.' 91 + return sys.maxint 92 + 93 + # Disallow repeated effects 94 + if spell.effect == 'shielded' and player.shielded: 95 + return sys.maxint 96 + elif spell.effect == 'poisoned' and boss.poisoned: 97 + return sys.maxint 98 + elif spell.effect == 'recharge' and player.recharging: 99 + return sys.maxint 100 + 101 + # print '> iKevinY casted {}!'.format(spell.name) 102 + spent_mana += spell.cost 103 + player.mp -= spell.cost 104 + boss.hp -= spell.dmg 105 + 106 + 107 + if spell.heal: 108 + # print 'iKevinY healed for {} hit points.'.format(spell.heal) 109 + player.hp += spell.heal 110 + 111 + if spell.effect == 'shielded': 112 + player.shielded = spell.duration 113 + elif spell.effect == 'poisoned': 114 + boss.poisoned = spell.duration 115 + elif spell.effect == 'recharge': 116 + player.recharging = spell.duration 117 + 118 + if boss.hp <= 0: 119 + # print 'Boss died. You win! :)' 120 + print spent_mana, '@', ' '.join(s.name for s in spell_order) 121 + return spent_mana 122 + 123 + # print '-- Boss Turn --' 124 + # player.print_player() 125 + # boss.print_boss() 126 + player.process_status() 127 + boss.process_status() 128 + 129 + if boss.hp <= 0: 130 + # print 'Boss died. You win! :)' 131 + print spent_mana, '@', ' '.join(s.name for s in spell_order) 132 + return spent_mana 133 + 134 + # print '> Boss attacks for {} damage!'.format(max(1, boss.dmg - player.armour)) 135 + player.hp -= max(1, boss.dmg - player.armour) 136 + if player.hp <= 0: 137 + # print 'Player died. Game over. :(' 138 + return sys.maxint 139 + 140 + # print 141 + 142 + else: 143 + # Fight did not terminate 144 + return sys.maxint 145 + 146 + return spent_mana 147 + 148 + 149 + # # HP, Damage 150 + # boss_data = [] 151 + 152 + # for line in fileinput.input(): 153 + # boss_data.append(int(line.split(': ')[1])) 154 + 155 + MOST_EFFICIENT = sys.maxint 156 + 157 + # boss = Character('BOSS', 13, 0, 8, 0) 158 + # player = Character('iKevinY', 10, 250, 0, 0) # 50HP, 500MP 159 + # boss_fight(player, boss, [SPELLS[3], SPELLS[0]]) 160 + 161 + # sys.exit() 162 + 163 + for n in range(9, 12): 164 + found = False 165 + for so in itertools.product(SPELLS, repeat=n): 166 + if sum(s.cost for s in so) > MOST_EFFICIENT: 167 + continue 168 + 169 + boss = Character('BOSS', 55, 0, 8, 0) # 55hp, 8dmg INPUT 170 + player = Character('iKevinY', 50, 500, 0, 0) # 50HP, 500MP 171 + 172 + # boss = Character('BOSS', 13, 0, 8, 0) 173 + # player = Character('iKevinY', 10, 250, 0, 0) # 50HP, 500MP 174 + a = boss_fight(player, boss, so, hard_mode=True) 175 + 176 + if a < MOST_EFFICIENT: 177 + MOST_EFFICIENT = a 178 + found = True 179 + 180 + if found: 181 + break 182 + 183 + print 'Most efficient MP: %i' % MOST_EFFICIENT
+67
2015/day23.py
··· 1 + import fileinput 2 + import re 3 + 4 + INS = [] 5 + 6 + for line in fileinput.input(): 7 + INS.append(line.strip()) 8 + 9 + pc = 0 10 + ra = 1 11 + rb = 0 12 + 13 + while 0 <= pc < len(INS): 14 + ins = INS[pc] 15 + print '{0: <16} a: {1}, b: {2}'.format(ins, ra, rb) 16 + 17 + if 'hlf' in ins: 18 + if 'a' in ins: 19 + ra /= 2 20 + else: 21 + rb /= 2 22 + 23 + elif 'tpl' in ins: 24 + if 'a' in ins: 25 + ra *= 3 26 + else: 27 + rb *= 3 28 + 29 + elif 'inc' in ins: 30 + if 'a' in ins: 31 + ra += 1 32 + else: 33 + rb += 1 34 + 35 + elif 'jmp' in ins: 36 + offset = int(re.findall('\d+', ins)[0]) * (-1 if '-' in ins else 1) 37 + pc += offset 38 + pc -= 1 # compensate for pc++ at end 39 + 40 + elif 'jie' in ins: 41 + offset = int(re.findall('\d+', ins)[0]) * (-1 if '-' in ins else 1) 42 + if 'a' in ins: 43 + if ra % 2 == 0: 44 + pc += offset 45 + pc -= 1 46 + else: 47 + if rb % 2 == 0: 48 + pc += offset 49 + pc -= 1 50 + 51 + elif 'jio' in ins: 52 + offset = int(re.findall('\d+', ins)[0]) * (-1 if '-' in ins else 1) 53 + if 'a' in ins: 54 + if ra == 1: 55 + pc += offset 56 + pc -= 1 57 + else: 58 + if rb == 1: 59 + pc += offset 60 + pc -= 1 61 + 62 + 63 + pc += 1 64 + 65 + print "halt\n--" 66 + print "Register A: %d" % ra 67 + print "Register B: %d" % rb
+31
2015/day24.py
··· 1 + import fileinput 2 + import itertools 3 + from tqdm import tqdm 4 + 5 + PACKAGES = [] 6 + 7 + for line in fileinput.input(): 8 + PACKAGES.append(int(line)) 9 + 10 + PACKAGES.reverse() 11 + 12 + def quantum_package(packages, target_weight): 13 + for n in range(1, len(PACKAGES) - 2): 14 + found = False 15 + for p in itertools.combinations(PACKAGES, n): 16 + if sum(p) == target_weight: 17 + # print p 18 + yield p 19 + found = True 20 + 21 + if found: 22 + return 23 + 24 + def prod(it): 25 + p = 1 26 + for i in it: 27 + p *= i 28 + return p 29 + 30 + print "3-compartment package QE:", min(prod(c) for c in quantum_package(PACKAGES, sum(PACKAGES) / 3)) 31 + print "4-compartment package QE:", min(prod(c) for c in quantum_package(PACKAGES, sum(PACKAGES) / 4))
+26
2015/day25.py
··· 1 + import fileinput 2 + import re 3 + 4 + # ROW, COL = (int(x) for x in re.findall(r'\d+', fileinput.input()[0])) 5 + ROW, COL = 3010, 3019 6 + 7 + 8 + def next_code(c): 9 + return (c * 252533) % 33554393 10 + 11 + def code_no(row, col): 12 + # s = 1 13 + # for x in range(row + col - 1): 14 + # s += x 15 + 16 + # return s + col - 1 17 + 18 + return (((row + col - 1) * (row + col - 2)) / 2) + col 19 + 20 + code = 20151125 21 + 22 + # for x in range(code_no(ROW, COL) - 1): 23 + # code = next_code(code) 24 + 25 + 26 + print (code * pow(252533, code_no(ROW, COL) - 1, 33554393)) % 33554393