diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | day1.py | 17 | ||||
-rw-r--r-- | day10.py | 12 | ||||
-rw-r--r-- | day11.py | 42 | ||||
-rw-r--r-- | day12.py | 28 | ||||
-rw-r--r-- | day13.py | 24 | ||||
-rw-r--r-- | day14.py | 29 | ||||
-rw-r--r-- | day15.py | 31 | ||||
-rw-r--r-- | day16.py | 44 | ||||
-rw-r--r-- | day17.py | 25 | ||||
-rw-r--r-- | day18.py | 33 | ||||
-rw-r--r-- | day19.py | 28 | ||||
-rw-r--r-- | day2.py | 23 | ||||
-rw-r--r-- | day20.py | 21 | ||||
-rw-r--r-- | day21.py | 59 | ||||
-rw-r--r-- | day22.fsx | 72 | ||||
-rw-r--r-- | day23.py | 44 | ||||
-rw-r--r-- | day24.fsx | 19 | ||||
-rw-r--r-- | day25.py | 10 | ||||
-rw-r--r-- | day3.py | 53 | ||||
-rw-r--r-- | day4.py | 21 | ||||
-rw-r--r-- | day5.py | 34 | ||||
-rw-r--r-- | day6.py | 39 | ||||
-rw-r--r-- | day7.py | 49 | ||||
-rw-r--r-- | day8.py | 34 | ||||
-rw-r--r-- | day9.py | 29 |
26 files changed, 821 insertions, 0 deletions
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) |