From e7085453864431ace3ad8f3123b259ed0829ae74 Mon Sep 17 00:00:00 2001 From: Brian Chu Date: Thu, 30 Dec 2021 15:11:21 -0800 Subject: all solutions for 2015 --- .gitignore | 1 + day1.py | 17 +++++++++++++++ day10.py | 12 +++++++++++ day11.py | 42 ++++++++++++++++++++++++++++++++++++ day12.py | 28 ++++++++++++++++++++++++ day13.py | 24 +++++++++++++++++++++ day14.py | 29 +++++++++++++++++++++++++ day15.py | 31 +++++++++++++++++++++++++++ day16.py | 44 ++++++++++++++++++++++++++++++++++++++ day17.py | 25 ++++++++++++++++++++++ day18.py | 33 ++++++++++++++++++++++++++++ day19.py | 28 ++++++++++++++++++++++++ day2.py | 23 ++++++++++++++++++++ day20.py | 21 ++++++++++++++++++ day21.py | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++ day22.fsx | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ day23.py | 44 ++++++++++++++++++++++++++++++++++++++ day24.fsx | 19 +++++++++++++++++ day25.py | 10 +++++++++ day3.py | 53 +++++++++++++++++++++++++++++++++++++++++++++ day4.py | 21 ++++++++++++++++++ day5.py | 34 +++++++++++++++++++++++++++++ day6.py | 39 ++++++++++++++++++++++++++++++++++ day7.py | 49 ++++++++++++++++++++++++++++++++++++++++++ day8.py | 34 +++++++++++++++++++++++++++++ day9.py | 29 +++++++++++++++++++++++++ 26 files changed, 821 insertions(+) create mode 100644 .gitignore create mode 100644 day1.py create mode 100644 day10.py create mode 100644 day11.py create mode 100644 day12.py create mode 100644 day13.py create mode 100644 day14.py create mode 100644 day15.py create mode 100644 day16.py create mode 100644 day17.py create mode 100644 day18.py create mode 100644 day19.py create mode 100644 day2.py create mode 100644 day20.py create mode 100644 day21.py create mode 100644 day22.fsx create mode 100644 day23.py create mode 100644 day24.fsx create mode 100644 day25.py create mode 100644 day3.py create mode 100644 day4.py create mode 100644 day5.py create mode 100644 day6.py create mode 100644 day7.py create mode 100644 day8.py create mode 100644 day9.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2211df6 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.txt diff --git a/day1.py b/day1.py new file mode 100644 index 0000000..aa328b9 --- /dev/null +++ b/day1.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python + +with open('day1.txt') as data: + floors = data.read().strip() +# part 1 +print(sum(map(lambda x: 1 if x == '(' else -1, floors))) + +# part 2 +floor = 0 +for i, char in enumerate(floors): + if char == '(': + floor += 1 + else: + floor -= 1 + if floor == -1: + print(i+1) + break diff --git a/day10.py b/day10.py new file mode 100644 index 0000000..c135c81 --- /dev/null +++ b/day10.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python + +from itertools import groupby + +digits = "1113122113" + +for i in range(50): + digits = ''.join(str(len(list(g))) + str(k) for k, g in groupby(digits)) + if i == 39: + print(len(digits)) + +print(len(digits)) diff --git a/day11.py b/day11.py new file mode 100644 index 0000000..534b6c4 --- /dev/null +++ b/day11.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python + +from itertools import combinations + +def valid(password): + def windows(): + alphabet = 'abcdefghijklmnopqrstuvwxyz' + for i in range(24): + yield alphabet[i:i+3] + + def pairs(): + alphabet = 'abcdefghijklmnopqrstuvwxyz' + for combo in combinations(alphabet, 2): + yield combo + + if not any(seq in password for seq in windows()): + return False + if any(char in password for char in 'iol'): + return False + if not any(char1*2 in password and char2*2 in password for char1, char2 in pairs()): + return False + return True + + + +def passwords(): + password = list(map(lambda x: ord(x)-96, 'cqjxjnds')) + while not password[0] == 27: + yield ''.join(char for char in map(lambda x: chr(x+96), password)) + password[-1] += 1 + for i in range(7, 0, -1): + if password[i] == 27: + password[i] = 1 + password[i-1] += 1 + +count = 0 +for password in passwords(): + if valid(password): + print(password) + count += 1 + if count == 2: + break diff --git a/day12.py b/day12.py new file mode 100644 index 0000000..90722ac --- /dev/null +++ b/day12.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +import re +import json + +with open('day12.txt') as in_file: + data = in_file.read().strip() + nums = re.findall(r'-?\d+', data) + nums = map(int, nums) + # part 1 + print(sum(nums)) + +data = json.loads(data) + +def total_nums(obj): + if isinstance(obj, list): + return sum(map(total_nums, obj)) + elif isinstance(obj, dict): + if any(val == 'red' for val in obj.values()): + return 0 + else: + return sum(map(total_nums, obj.values())) + elif isinstance(obj, int): + return obj + else: + return 0 + +print(total_nums(data)) diff --git a/day13.py b/day13.py new file mode 100644 index 0000000..1f5bd23 --- /dev/null +++ b/day13.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python + +from collections import defaultdict +from itertools import permutations + +costs = defaultdict(lambda: defaultdict(int)) + +with open('day13.txt') as data: + for line in data: + line = line.strip('.\n').split() + costs[line[0]][line[-1]] = int(line[3]) * (-1 if line[2] == 'lose' else 1) + +def calculate_cost(ordering): + return sum(costs[ordering[i-1]][ordering[i]] + costs[ordering[i]][ordering[i-1]]for i in range(len(ordering))) + +# part 1 +print(max(calculate_cost(order) for order in permutations(costs.keys()))) + +# part 2 +for key in set(costs.keys()): + costs[key]['Myself'] = 0 + costs['Myself'][key] = 0 + +print(max(calculate_cost(order) for order in permutations(costs.keys()))) diff --git a/day14.py b/day14.py new file mode 100644 index 0000000..a377fa8 --- /dev/null +++ b/day14.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python + +from collections import defaultdict + +with open('day14.txt') as data: + racers = [tuple(map(int, (line[3], line[6], line[-2]))) + for reindeer in data if (line := reindeer.strip().split())] + +def distance(reindeer, time): + speed, dur, rest = reindeer + total_distance = speed * dur * (time // (dur + rest)) + remainder = time % (dur + rest) + total_distance += speed * remainder if remainder <= dur else speed * dur + return total_distance + +print(max(map(lambda x: distance(x, 2503), racers))) + +def points(reindeer, TOTAL_TIME): + scores = defaultdict(int) + + for second in range(1, TOTAL_TIME+1): + distances = {deer: distance(deer, second) for deer in reindeer} + max_dist = max(distances.values()) + for deer in distances: + if distances[deer] == max_dist: + scores[deer] += 1 + return max(scores.values()) + +print(points(racers, 2503)) diff --git a/day15.py b/day15.py new file mode 100644 index 0000000..33dc8d3 --- /dev/null +++ b/day15.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python + +import numpy as np + +properties = np.array([[3, 0, 0, -3, 2], + [-3, 3, 0, 0, 9], + [-1, 0, 4, 0, 1], + [0, 0, -2, 2, 8]]) + +def get_scores(count_calories=False): + max_score = 0 + for sugar in range(100): + for sprinkles in range(100-sugar): + for candy in range(100-sugar-sprinkles): + chocolate = 100-sugar-sprinkles-candy + counts = np.array([sugar, sprinkles, candy, chocolate]) + scores = properties.T @ counts + if count_calories and scores[-1] != 500: + continue + if np.any(scores[:-1] <= 0): + continue + else: + score = np.prod(scores[:-1]) + max_score = max(max_score, score) + return max_score + +# part 1 +print(get_scores()) + +# part 2 +print(get_scores(True)) diff --git a/day16.py b/day16.py new file mode 100644 index 0000000..45cc6d3 --- /dev/null +++ b/day16.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python + +from collections import defaultdict +import re + +target = { + 'children': 3, + 'cats': 7, + 'samoyeds': 2, + 'pomeranians': 3, + 'akitas': 0, + 'vizslas': 0, + 'goldfish': 5, + 'trees': 3, + 'cars': 2, + 'perfumes': 1 +} + +info_re = re.compile(r'(\w+): (\d)') + +# part 1 +with open('day16.txt') as data: + for num, line in enumerate(data): + current = dict(map(lambda x: (x[0], int(x[1])), info_re.findall(line))) + if all(target[key] == current[key] for key in current): + print(num+1) + break + +# part 2 +def valid(curr): + for key in curr: + if key == 'cats' or key == 'trees': + yield curr[key] > target[key] + elif key == 'pomeranians' or key == 'goldfish': + yield curr[key] < target[key] + else: + yield curr[key] == target[key] + +with open('day16.txt') as data: + for num, line in enumerate(data): + current = dict(map(lambda x: (x[0], int(x[1])), info_re.findall(line))) + if all(valid(current)): + print(num+1) + break diff --git a/day17.py b/day17.py new file mode 100644 index 0000000..4d70a06 --- /dev/null +++ b/day17.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python +from itertools import combinations + +with open('day17.txt') as data: + containers = list(map(int, (line.strip() for line in data))) + +# part 1 +count = 0 +for i in range(2, len(containers)+1): + for combo in combinations(containers, i): + if sum(combo) == 150: + count += 1 +print(count) + +# part 2 +count = 0 +for i in range(2, len(containers)+1): + min_count = False + for combo in combinations(containers, i): + if sum(combo) == 150: + min_count = True + count += 1 + if min_count: + break +print(count) diff --git a/day18.py b/day18.py new file mode 100644 index 0000000..033cc76 --- /dev/null +++ b/day18.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python + +import numpy as np +from scipy.ndimage import convolve + +with open('day18.txt') as data: + grid = np.stack([list(map(lambda x: x == '#', line.strip())) for line in data]).astype(int) + + +neighbors = [[1, 1, 1], + [1, 0, 1], + [1, 1, 1]] + +grid1 = np.copy(grid) +for i in range(100): + neighbor_counts = convolve(grid1.astype(int), neighbors, mode='constant', cval=0) + survive = grid1 * (neighbor_counts >= 2) * (neighbor_counts <= 3) + grid1 = np.where(neighbor_counts == 3, True, survive) + +print(np.count_nonzero(grid1)) + +grid2 = np.copy(grid) +for coord in {(0,0), (0, 99), (99, 0), (99, 99)}: + grid2[coord] = True + +for i in range(100): + neighbor_counts = convolve(grid2.astype(int), neighbors, mode='constant', cval=0) + survive = grid2 * (neighbor_counts >= 2) * (neighbor_counts <= 3) + grid2 = np.where(neighbor_counts == 3, True, survive) + for coord in {(0,0), (0, 99), (99, 0), (99, 99)}: + grid2[coord] = True + +print(np.count_nonzero(grid2)) diff --git a/day19.py b/day19.py new file mode 100644 index 0000000..d7b92ee --- /dev/null +++ b/day19.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +import re +from collections import defaultdict + +replacements = [] +starter = None +with open('day19.txt') as data: + for line in data: + groups = re.search(r'(\w+) => (\w+)', line) + if groups: + replacements.append(groups.groups((1, 2))) + else: + if line != '\n': + starter = line.strip() + +# part 1 +compounds = set() +for elem, repl in replacements: + for i in range(len(starter)): + if starter[i:i+len(elem)] == elem: + compound = starter[:i] + repl + starter[i+len(elem):] + compounds.add(compound) +print(len(compounds)) + +# part 2 +elements = [s for s in re.split(r'([A-Z][^A-Z]*)', starter) if s] +print(len(elements) - elements.count('Ar') - elements.count('Rn') - 2 * elements.count('Y') - 1) diff --git a/day2.py b/day2.py new file mode 100644 index 0000000..853e507 --- /dev/null +++ b/day2.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python +from itertools import combinations +from math import prod + +with open('day2.txt') as data: + dims = [tuple(map(int, line.strip().split('x'))) for line in data] + +# part 1 +total_area = 0 +for dim in dims: + areas = combinations(dim, 2) + areas = list(map(lambda x: prod(x), areas)) + total_area += 2 * sum(areas) + min(areas) + +print(total_area) + +# part 2 +total_length = 0 +for dim in dims: + wrap = 2 * sum(sorted(dim)[:2]) + bow = prod(dim) + total_length += wrap + bow +print(total_length) diff --git a/day20.py b/day20.py new file mode 100644 index 0000000..f8cf55d --- /dev/null +++ b/day20.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +from functools import reduce + +def divisors(n): + return set(reduce(list.__add__, + ([i, n//i] for i in range(1, int(n**0.5) + 1) if n%i==0 ))) + +n = 1 +total = 29000000 +while True: + if sum(divisors(n)) * 10 >= total: + print(n) + break + n += 1 + +while True: + if sum(d for d in divisors(n) if n / d <= 50) * 11 >= total: + print(n) + break + n += 1 diff --git a/day21.py b/day21.py new file mode 100644 index 0000000..c09cd9a --- /dev/null +++ b/day21.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python + +from itertools import product +from collections import namedtuple +from math import ceil + +Item = namedtuple('Item', ['cost', 'damage', 'armor']) +Character = namedtuple('Character', ['hp', 'damage', 'armor']) +weapons = [ + Item(8, 4, 0), + Item(10, 5, 0), + Item(25, 6, 0), + Item(40, 7, 0), + Item(74, 8, 0) +] + +armor = [ + Item(0, 0, 0), + Item(13, 0, 1), + Item(31, 0, 2), + Item(53, 0, 3), + Item(75, 0, 4), + Item(102, 0, 5) +] + +rings = [ + Item(25, 1, 0), + Item(50, 2, 0), + Item(100, 3, 0), + Item(20, 0, 1), + Item(40, 0, 2), + Item(80, 0, 3), + Item(0, 0, 0), + Item(0, 0, 0) +] + +def willWin(player, boss): + playerMoves = ceil(player.hp / max(boss.damage - player.armor, 1)) + bossMoves = ceil(boss.hp / max(player.damage - boss.armor, 1)) + return playerMoves >= bossMoves + +boss = Character(103, 9, 2) +winningCosts = [] +losingCosts = [] +for weapon, shield, ring1, ring2 in product(weapons, armor, rings, rings): + if ring1.cost == ring2.cost and ring1.cost != 0: continue + totalCost = weapon.cost + shield.cost + ring1.cost + ring2.cost + totalDamage = weapon.damage + shield.damage + ring1.damage + ring2.damage + totalArmor = weapon.armor + shield.armor + ring1.armor + ring2.armor + player = Character(100, totalDamage, totalArmor) + if willWin(player, boss): + winningCosts.append(totalCost) + else: + losingCosts.append(totalCost) + +# part 1 +print(min(winningCosts)) +# part 2 +print(max(losingCosts)) diff --git a/day22.fsx b/day22.fsx new file mode 100644 index 0000000..7944d25 --- /dev/null +++ b/day22.fsx @@ -0,0 +1,72 @@ +type Spell = + { Name : string + Cost : int + Damage : int + Heal : int + Armor : int + Mana : int + Duration : int } + +let allSpells = + [ { Name = "Magic Missile"; Cost = 53; Damage = 4; Heal = 0; Armor = 0; Mana = 0; Duration = 1} + { Name = "Drain"; Cost = 73; Damage = 2; Heal = 2; Armor = 0; Mana = 0; Duration = 1} + { Name = "Shield"; Cost = 113; Damage = 0; Heal = 0; Armor = 7; Mana = 0; Duration = 6} + { Name = "Poison"; Cost = 173; Damage = 3; Heal = 0; Armor = 0; Mana = 0; Duration = 6} + { Name = "Recharge"; Cost = 229; Damage = 0; Heal = 0; Armor = 0; Mana = 101; Duration = 5} ] + +let cheapestWin part (hp, mana) (bossHp, bossDamage) = + let rec play part myTurn best spent (hp, mana, spells) (bossHp, bossDamage) : int = + // if we have already spent more than the known cheapest win, bail + if spent >= best then best else + + // part 2, check if I die before any effects play out + if part = 2 && myTurn && hp = 1 then best else + + // apply effects + let mana = (spells |> List.sumBy (fun s -> s.Mana)) + mana + let damage = spells |> List.sumBy (fun s -> s.Damage) + let armor = spells |> List.sumBy (fun s -> s.Armor) + + // does the boss die from effects? + let bossHp = bossHp - damage + if bossHp <= 0 then spent else + + // decrement duration of all effects, and groom expired ones + let spells = + spells + |> List.map (fun s -> {s with Duration = s.Duration - 1}) + |> List.filter (fun s -> s.Duration > 0) + + if myTurn then + // part 2, I lose 1 HP on my turn + let hp = if part = 2 then hp - 1 else hp + + // what spells can I afford and don't already have running? + match allSpells |> List.filter (fun s -> (s.Cost <= mana) && not (spells |> List.exists (fun s' -> s.Name = s'.Name))) with + | [] -> best + | buyableSpells -> + // play out the rest of the game with each possible purchase + let mutable newBest = best + for s in buyableSpells do + let extraDamage, heal, spells = + if s.Duration = 1 then s.Damage, s.Heal, spells + else 0, 0, s :: spells + let spent = spent + s.Cost + let mana = mana - s.Cost + let hp = hp + heal + + let bossHp = bossHp - extraDamage + if bossHp <= 0 then newBest <- min newBest spent + else newBest <- min newBest (play part false newBest spent (hp, mana, spells) (bossHp, bossDamage)) + newBest + // boss's turn + else + let damage = max (bossDamage - armor) 1 + let hp = hp - damage + if hp <= 0 then best else + play part true best spent (hp, mana, spells) (bossHp, bossDamage) + + play part true 999999 0 (hp, mana, []) (bossHp, bossDamage) + +printfn "Part 1 - min to win: %d" (cheapestWin 1 (50, 500) (55, 8)) +printfn "Part 2 - min to win: %d" (cheapestWin 2 (50, 500) (55, 8)) diff --git a/day23.py b/day23.py new file mode 100644 index 0000000..5e0763b --- /dev/null +++ b/day23.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python + +with open('day23.txt') as data: + program = [tuple(line.strip().replace(',', '').split()) for line in data] + +def execute_program(insts, a_reg = 0): + registers = { + 'a': a_reg, + 'b': 0, + 'pc': 0 + } + + def exec_inst(inst): + match inst: + case 'hlf', reg: + registers[reg] /= 2 + registers['pc'] += 1 + case 'tpl', reg: + registers[reg] *= 3 + registers['pc'] += 1 + case 'inc', reg: + registers[reg] += 1 + registers['pc'] += 1 + case 'jmp', offset: + registers['pc'] += int(offset) + case 'jie', reg, offset: + if registers[reg] % 2 == 0: + registers['pc'] += int(offset) + else: + registers['pc'] += 1 + case 'jio', reg, offset: + if registers[reg] == 1: + registers['pc'] += int(offset) + else: + registers['pc'] += 1 + while True: + exec_inst(insts[registers['pc']]) + if registers['pc'] >= len(insts): + print(registers['b']) + break +# part 1 +execute_program(program, 0) +# part 2 +execute_program(program, 1) diff --git a/day24.fsx b/day24.fsx new file mode 100644 index 0000000..a328dd9 --- /dev/null +++ b/day24.fsx @@ -0,0 +1,19 @@ +open System + +let rec comb n l = + match n, l with + | 0, _ -> [[]] + | _, [] -> [] + | k, (x::xs) -> List.map ((@) [x]) (comb (k-1) xs) @ comb k xs + +let minQE (nums: int list) grps = + [2..(nums.Length/grps - 1)] + |> List.map (fun n -> comb n nums) |> List.concat + |> List.filter (fun cmb -> cmb |> List.sum = (nums |> List.sum) / grps) + |> Seq.groupBy (fun cmb -> cmb.Length) + |> Seq.minBy (fun (len,_) -> len) |> snd + |> Seq.map (fun cmb -> cmb |> List.map int64 |> List.reduce (*)) |> Seq.min + +let nums = IO.File.ReadAllLines "day24.txt" |> Array.map Int32.Parse |> Array.toList +minQE nums 3 |> printfn "3 Groups: %A" +minQE nums 4 |> printfn "4 Groups: %A" diff --git a/day25.py b/day25.py new file mode 100644 index 0000000..aa1c81c --- /dev/null +++ b/day25.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python + +pos_row, pos_col = 1, 1 +target_row, target_col = 2978, 3083 + +curr = 20151125 + +for i in range(sum(range(target_row + target_col - 1)) + target_col - 1): + curr = (curr * 252533) % 33554393 +print(curr) diff --git a/day3.py b/day3.py new file mode 100644 index 0000000..8d5854e --- /dev/null +++ b/day3.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python + +from collections import defaultdict +with open('day3.txt') as data: + directions = data.read().strip() +# part 1 +x, y = 0, 0 +houses = defaultdict(int) +houses[(x, y)] = 1 +for direction in directions: + match direction: + case '^': + y += 1 + case 'v': + y -= 1 + case '>': + x += 1 + case '<': + x -= 1 + houses[(x, y)] += 1 + +print(len(houses)) + +# part 2 +santaX, santaY, roboX, roboY = 0, 0, 0, 0 +houses = defaultdict(int) +houses[(0, 0)] = 2 +isSanta = True +for direction in directions: + if isSanta: + match direction: + case '^': + santaY += 1 + case 'v': + santaY -= 1 + case '>': + santaX += 1 + case '<': + santaX -= 1 + houses[(santaX, santaY)] += 1 + else: + match direction: + case '^': + roboY += 1 + case 'v': + roboY -= 1 + case '>': + roboX += 1 + case '<': + roboX -= 1 + houses[(roboX, roboY)] += 1 + isSanta = not isSanta +print(len(houses)) diff --git a/day4.py b/day4.py new file mode 100644 index 0000000..ab2bb88 --- /dev/null +++ b/day4.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +from hashlib import md5 + +secret_key = r'iwrupvqb' + +num = 1 +while True: + hashed = md5(str(secret_key + str(num)).encode('utf-8')).hexdigest() + if hashed[:5] == '00000': + print(num) + break + num += 1 + +num = 1 +while True: + hashed = md5(str(secret_key + str(num)).encode('utf-8')).hexdigest() + if hashed[:6] == '000000': + print(num) + break + num += 1 diff --git a/day5.py b/day5.py new file mode 100644 index 0000000..25c6bc4 --- /dev/null +++ b/day5.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python + +from collections import Counter +from itertools import product + +with open('day5.txt') as data: + strings = [line.strip() for line in data] + +def isNice(string): + count_letters = Counter(string) + if sum(count_letters[x] for x in 'aeiou') < 3: + return False + if any(map(lambda x: x in string, ['ab', 'cd', 'pq', 'xy'])): + return False + if not any(map(lambda x: x*2 in string, 'abcdefghijklmnopqrstuvwxyz')): + return False + return True + +nice_counts = Counter(map(isNice, strings)) +print(nice_counts[True]) + +def isNice2(string): + def windows(size): + for i in range(len(string) - size + 1): + yield string[i:i+size] + if not any(window[0] == window[2] for window in windows(3)): + return False + pairs = map(lambda x: x[0] + x[1], product('abcdefghijklmnopqrstuvwxyz', repeat=2)) + if not any(map(lambda x: string.count(x) >= 2, pairs)): + return False + return True + +nice2_counts = Counter(map(isNice2, strings)) +print(nice2_counts[True]) diff --git a/day6.py b/day6.py new file mode 100644 index 0000000..86c4abc --- /dev/null +++ b/day6.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python + +import numpy as np +import re + +num_parse = re.compile(r'(toggle|off|on) (\d+,\d+) through (\d+,\d+)$') + +with open('day6.txt') as data: + instructions = [num_parse.search(line).groups((1, 2, 3)) for line in data] + +lights = np.zeros((1000, 1000), dtype=bool) +for inst in instructions: + x1, y1 = map(int, inst[1].split(',')) + x2, y2 = map(int, inst[2].split(',')) + match inst[0]: + case 'toggle': + lights[x1:x2+1, y1:y2+1] = np.logical_not(lights[x1:x2+1, y1:y2+1]) + case 'on': + lights[x1:x2+1, y1:y2+1] = True + case 'off': + lights[x1:x2+1, y1:y2+1] = False + +print(np.count_nonzero(lights)) + +lights = np.zeros((1000, 1000), dtype=int) +for inst in instructions: + x1, y1 = map(int, inst[1].split(',')) + x2, y2 = map(int, inst[2].split(',')) + match inst[0]: + case 'toggle': + lights[x1:x2+1, y1:y2+1] += 2 + case 'on': + lights[x1:x2+1, y1:y2+1] += 1 + case 'off': + region = lights[x1:x2+1, y1:y2+1] + region -= 1 + region[region < 0] = 0 + +print(np.sum(lights)) diff --git a/day7.py b/day7.py new file mode 100644 index 0000000..3c7f0c3 --- /dev/null +++ b/day7.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python + +from functools import cache +from collections import defaultdict +import operator + +ops = { + 'EQ': lambda x: x, + 'NOT': lambda x: ~x & 0xffff, + 'AND': operator.iand, + 'OR': operator.ior, + 'RSHIFT': operator.rshift, + 'LSHIFT': operator.lshift +} + +wires = defaultdict(list) +with open('day7.txt') as data: + for line in data: + calc, target = line.strip().split(' -> ') + calc = calc.split() + if len(calc) == 1: + wires[target] = ('EQ', calc[0]) + elif len(calc) == 2: + wires[target] = (calc[0], calc[1]) + else: + wires[target] = (calc[1], calc[0], calc[2]) + +@cache +def get_value(key): + try: + return int(key) + except ValueError: + pass + + op, *value = wires[key] + + if len(value) == 1: + return ops[op](get_value(value[0])) + else: + return ops[op](get_value(value[0]), get_value(value[1])) + +# part 1 +print(get_value('a')) + +# part 2 +get_value.cache_clear() + +wires['b'] = ('EQ', 3176) +print(get_value('a')) diff --git a/day8.py b/day8.py new file mode 100644 index 0000000..7533ea4 --- /dev/null +++ b/day8.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python + +import re + +with open('day8.txt') as data: + strings = [line.strip() for line in data] + +# part 1 +total = 0 +for string in strings: + orig_len = len(string) + eval_len = len(eval(string)) + total += orig_len - eval_len + +print(total) + +# part 2 +def encode(s): + result = '' + for c in s: + if c == '"': + result += '\\\"' + elif c == '\\': + result += '\\\\' + else: + result += c + return '"' + result + '"' + +total = 0 +for string in strings: + encoded = encode(string) + total += len(encoded) - len(string) + +print(total) diff --git a/day9.py b/day9.py new file mode 100644 index 0000000..4ce3f69 --- /dev/null +++ b/day9.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python + +from collections import defaultdict +from itertools import permutations + +graph = defaultdict(lambda: defaultdict(int)) +with open('day9.txt') as data: + for line in data: + pos1, _, pos2, _, dist = line.strip().split() + graph[pos1][pos2] = int(dist) + graph[pos2][pos1] = int(dist) + +routes = list(permutations(graph.keys())) + +def route_dist(route): + dist = 0 + for i in range(len(route)-1): + dist += graph[route[i]][route[i+1]] + return dist + +minDist, maxDist = float('inf'), 0 +for route in routes: + dist = route_dist(route) + if dist < minDist: + minDist = dist + if dist > maxDist: + maxDist = dist +print(minDist) +print(maxDist) -- cgit 1.4.1-2-gfad0