Last active
December 11, 2022 07:23
-
-
Save StevenJL/4a4a7c1d2b321f1be3bbc518e7f00e1f to your computer and use it in GitHub Desktop.
aoc2022
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# _____ _ _ _____ ______ ______ ______ ______ ______ ______ ______ _______ | |
# / ____| | | |_ _| ____| ____| ____| ____| ____| ____| ____|__ __| | |
# | (___ | |__| | | | | |__ | |__ | |__ | |__ | |__ | |__ | |__ | | | |
# \___ \| __ | | | | __| | __| | __| | __| | __| | __| | __| | | | |
# ____) | | | |_| |_| |____| |____| |____| |____| |____| |____| |____ | | | |
# |_____/|_| |_|_____|______|______|______|______|______|______|______| |_| | |
######################## Day 1 ################################ | |
with open('./input') as f: | |
lines = [line.rstrip() for line in f] | |
reduced = [] | |
current_cal = 0 | |
for line in lines: | |
if line == '': | |
reduced.append(current_cal) | |
current_cal = 0 | |
else: | |
current_cal = current_cal + int(line) | |
length = len(reduced) | |
print(sorted(reduced)[length-3:length]) | |
######################## Day 2 ################################ | |
outcome_dict = { 'A X': 3, 'A Y': 6, 'A Z': 0, 'B X': 0, 'B Y': 3, 'B Z': 6, 'C X': 6, 'C Y': 0, 'C Z': 3} | |
shape_score = { 'X': 1, 'Y': 2, 'Z': 3} | |
score = 0 | |
with open('./input') as f: | |
lines = [line.rstrip() for line in f] | |
for line in lines: | |
score = score + outcome_dict[line] + shape_score[line[2]] | |
print(score) | |
######################## Day 2 - pt 2 ################################ | |
outcome_dict = { 'X': 0, 'Y': 3, 'Z': 6} | |
shape_dict = { 'A X': 3, 'A Y': 1, 'A Z': 2, 'B X': 1, 'B Y': 2, 'B Z': 3, 'C X': 2, 'C Y': 3, 'C Z': 1 } | |
score = 0 | |
with open('./input') as f: | |
lines = [line.rstrip() for line in f] | |
for line in lines: | |
score = score + outcome_dict[line[2]] + shape_dict[line] | |
print(score) | |
######################## Day 3 ################################ | |
with open('./input') as f: | |
lines = [line.rstrip() for line in f] | |
def messed_up_item(comp1, comp2): | |
for item in comp1: | |
if comp2.find(item) != -1: | |
return item | |
def priority(item): | |
# lower case | |
if ord(item) > 96: | |
return ord(item) - 96 | |
else: | |
return ord(item) - 38 | |
priority_sum = 0 | |
for line in lines: | |
mp = int(len(line)/2) | |
comp1 = line[0:mp] | |
comp2 = line[mp:] | |
priority_sum = priority_sum + priority(messed_up_item(comp1, comp2)) | |
print(priority_sum) | |
######################## Day 3 - pt 2 ################################ | |
def priority(item): | |
# lower case | |
if ord(item) > 96: | |
return ord(item) - 96 | |
else: | |
return ord(item) - 38 | |
with open('./input') as f: | |
lines = [line.rstrip() for line in f] | |
lines_grouped_three = [] | |
lines_len = len(lines) | |
for i in range(0, lines_len, 3): | |
lines_grouped_three.append(lines[i:i+3]) | |
priority_sum = 0 | |
for group in lines_grouped_three: | |
set_with_badge = set(group[0]) & set(group[1]) & set(group[2]) | |
for badge in set_with_badge: | |
priority_sum = priority_sum + priority(badge) | |
print(priority_sum) | |
######################## Day 4 ################################ | |
with open('./input') as f: | |
lines = [line.rstrip() for line in f] | |
fully_contained_count = 0 | |
for line in lines: | |
parsed_line = list(map(lambda x: x.split('-'), line.split(','))) | |
ass1 = parsed_line[0] | |
ass2 = parsed_line[1] | |
if int(ass1[0]) == int(ass2[0]): | |
fully_contained_count = fully_contained_count + 1 | |
elif int(ass1[1]) == int(ass2[1]): | |
fully_contained_count = fully_contained_count + 1 | |
elif int(ass1[0]) < int(ass2[0]): | |
if int(ass1[1]) >= int(ass2[1]): | |
fully_contained_count = fully_contained_count + 1 | |
else: | |
continue | |
else: | |
if int(ass1[1]) <= int(ass2[1]): | |
fully_contained_count = fully_contained_count + 1 | |
else: | |
continue | |
print(fully_contained_count) | |
######################## Day 4 - part 2 ################################ | |
with open('./input') as f: | |
lines = [line.rstrip() for line in f] | |
overlap_atall_count = 0 | |
for line in lines: | |
parsed_line = list(map(lambda x: x.split('-'), line.split(','))) | |
ass1 = parsed_line[0] | |
ass2 = parsed_line[1] | |
if (int(ass1[1]) >= int(ass2[0])) and not (int(ass2[1]) <= int(ass1[0])): | |
overlap_atall_count = overlap_atall_count + 1 77 def knot_follow(follower, leader) | |
elif (int(ass2[1]) >= int(ass1[0])) and not (int(ass1[1]) <= int(ass2[0])): | |
overlap_atall_count = overlap_atall_count + 1 | |
else: | |
continue | |
print(overlap_atall_count) | |
######################## Day 5 ############################# | |
with open('initial_config') as f: | |
ic_lines = [line.rstrip() for line in f] | |
stack_indices = ic_lines.pop().split() | |
stack_indices_int = list(map(lambda x: int(x), stack_indices)) | |
config_dict = { } | |
for si in stack_indices_int: | |
config_dict[si] = [] | |
ic_lines.reverse() | |
for ic_line in ic_lines: | |
row = ic_line.replace('[','').replace(']','').split() | |
for si in stack_indices_int: | |
if row[si-1] == '*': | |
continue | |
else: | |
config_dict[si].append(row[si-1]) | |
with open('steps') as f: | |
stp_lines = [line.rstrip() for line in f] | |
for sl in stp_lines: | |
sl_list = sl.split() | |
moves_count = int(sl_list[1]) | |
stack_source = int(sl_list[3]) | |
stack_target = int(sl_list[5]) | |
for i in range(moves_count): | |
crate = config_dict[stack_source].pop() | |
config_dict[stack_target].append(crate) | |
######################## Day 5 - pt 2 ############################# | |
with open('initial_config') as f: | |
ic_lines = [line.rstrip() for line in f] | |
stack_indices = ic_lines.pop().split() | |
stack_indices_int = list(map(lambda x: int(x), stack_indices)) | |
config_dict = { } | |
for si in stack_indices_int: | |
config_dict[si] = [] | |
ic_lines.reverse() | |
for ic_line in ic_lines: | |
row = ic_line.replace('[','').replace(']','').split() | |
for si in stack_indices_int: | |
if row[si-1] == '*': | |
continue | |
else: | |
config_dict[si].append(row[si-1]) | |
with open('steps') as f: | |
stp_lines = [line.rstrip() for line in f] | |
for sl in stp_lines: | |
sl_list = sl.split() | |
moves_count = int(sl_list[1]) | |
stack_source = int(sl_list[3]) | |
stack_target = int(sl_list[5]) | |
buffer = [] | |
for i in range(moves_count): | |
buffer.append(config_dict[stack_source].pop()) | |
buffer.reverse() | |
for crate in buffer : | |
config_dict[stack_target].append(crate) | |
######################## Day 6 ############################# | |
with open('input') as f: | |
signal = [line.rstrip() for line in f][0] | |
signal_len = len(signal) | |
for i in range(signal_len): | |
j = i + 14 | |
if len(set(signal[i:j])) == 14: | |
print(j) | |
break | |
###################### Day 7 ################################ | |
# Don't know how regexes work in Python... didn't wanna learn it on the fly, so switched to Ruby | |
file_contents = open('./input') { |f| f.read }.split("\n") | |
directory_bytes = Hash.new(0) | |
directory_stack = ["root"] | |
file_contents[2..-1].each do |line| | |
if (mo = /^(\d+)\s.*/.match(line)) | |
directory_stack.each_with_index do |_dir, indx| | |
full_path = directory_stack[0..indx].join("/") | |
directory_bytes[full_path] = directory_bytes[full_path] + mo[1].to_i | |
end | |
elsif line == "$ cd .." | |
directory_stack.pop | |
elsif (mo = /^\$\scd\s(.*)/.match(line)) | |
directory_stack << mo[1] | |
end | |
end | |
# Part 1 | |
puts directory_bytes.select { |k,v| v <= 100000 }.values.sum | |
# Part 2 | |
current_unused_space = 70000000 - directory_bytes["root"] | |
delta = 30000000 - current_unused_space | |
puts directory_bytes.select { |k,v| v >= delta }.sort_by { |k, v| v }[0] | |
################# Day 8 - part 1 ################################# | |
# Getting too hard, just gonna do em in Ruby going forward now lol | |
@trees = open('input') { |f| f.read }.split("\n").map { |row| row.split('') } | |
@i_last_index = @trees.count - 1 | |
@j_last_index = @trees.first.count - 1 | |
def is_visible?(coord) | |
is_visible_from_left?(coord[0], coord[1]) || | |
is_visible_from_right?(coord[0], coord[1]) || | |
is_visible_from_top?(coord[0], coord[1]) || | |
is_visible_from_bottom?(coord[0], coord[1]) | |
end | |
def is_visible_from_left?(i,j) | |
value = @trees[i][j].to_i | |
(0..(j -1)).each do |j_inner| | |
if @trees[i][j_inner].to_i >= value | |
return false | |
end | |
end | |
true | |
end | |
def is_visible_from_right?(i,j) | |
value = @trees[i][j].to_i | |
((j+1)..(@j_last_index)).each do |j_inner| | |
if @trees[i][j_inner].to_i >= value | |
return false | |
end | |
end | |
true | |
end | |
def is_visible_from_top?(i,j) | |
value = @trees[i][j].to_i | |
(0..(i-1)).each do |i_inner| | |
if @trees[i_inner][j].to_i >= value | |
return false | |
end | |
end | |
true | |
end | |
def is_visible_from_bottom?(i,j) | |
value = @trees[i][j].to_i | |
((i+1)..(@i_last_index)).each do |i_inner| | |
if @trees[i_inner][j].to_i >= value | |
return false | |
end | |
end | |
true | |
end | |
all_coords = [] | |
(0..@i_last_index).each do |i| | |
(0..@j_last_index).each do |j| | |
all_coords << [i,j] | |
end | |
end | |
puts all_coords.select { |coord| is_visible?(coord) }.count | |
###################### Day 8 - part 2 ################ | |
@trees = open('input') { |f| f.read }.split("\n").map { |row| row.split('') } | |
@i_last_index = @trees.count - 1 | |
@j_last_index = @trees.first.count - 1 | |
def is_visible?(coord) | |
is_visible_from_left?(coord[0], coord[1]) || | |
is_visible_from_right?(coord[0], coord[1]) || | |
is_visible_from_top?(coord[0], coord[1]) || | |
is_visible_from_bottom?(coord[0], coord[1]) | |
end | |
def is_visible_from_left?(i,j) | |
value = @trees[i][j].to_i | |
(0..(j -1)).each do |j_inner| | |
if @trees[i][j_inner].to_i >= value | |
return false | |
end | |
end | |
true | |
end | |
def is_visible_from_right?(i,j) | |
value = @trees[i][j].to_i | |
((j+1)..(@j_last_index)).each do |j_inner| | |
if @trees[i][j_inner].to_i >= value | |
return false | |
end | |
end | |
true | |
end | |
def is_visible_from_top?(i,j) | |
value = @trees[i][j].to_i | |
(0..(i-1)).each do |i_inner| | |
if @trees[i_inner][j].to_i >= value | |
return false | |
end | |
end | |
true | |
end | |
def is_visible_from_bottom?(i,j) | |
value = @trees[i][j].to_i | |
((i+1)..(@i_last_index)).each do |i_inner| | |
if @trees[i_inner][j].to_i >= value | |
return false | |
end | |
end | |
true | |
end | |
all_coords = [] | |
(0..@i_last_index).each do |i| | |
(0..@j_last_index).each do |j| | |
all_coords << [i,j] | |
end | |
end | |
puts all_coords.select { |coord| is_visible?(coord) }.count | |
############################ Day 9 ################################ | |
require "set" | |
file_contents = open('input.txt') { |f| f.read }.split("\n") | |
@tail_visited_coords = Set.new([0,0]) | |
@tail_coord = [4,0] | |
@head_coord = [4,0] | |
def move_head(direction) | |
x,y = @head_coord | |
case direction | |
when "R" | |
@head_coord = [x, y + 1] | |
when "U" | |
@head_coord = [x - 1, y] | |
when "L" | |
@head_coord = [x, y - 1] | |
when "D" | |
@head_coord = [x + 1, y] | |
end | |
end | |
# https://en.wikipedia.org/wiki/Taxicab_geometry | |
def taxi_cab_dist | |
(@head_coord[0] - @tail_coord[0]).abs + (@head_coord[1] - @tail_coord[1]).abs | |
end | |
def adjacent? | |
return true if taxi_cab_dist <= 1 | |
if taxi_cab_dist == 2 | |
if @head_coord[0] == @tail_coord[0] || @head_coord[1] == @tail_coord[1] | |
# two steps away along same axis | |
false | |
else | |
# diagonal | |
true | |
end | |
end | |
end | |
def tail_follow | |
unless adjacent? | |
tx, ty = @tail_coord | |
hx, hy = @head_coord | |
if hx == tx && (ty - hy).abs == 2 | |
@tail_coord = [tx, (ty + hy) / 2] | |
elsif ty == hy && (hx - tx).abs == 2 | |
@tail_coord = [(tx + hx) / 2, ty] | |
else | |
if (hy - ty).abs == 2 && (hx - tx).abs == 1 | |
@tail_coord = [hx, (hy + ty)/2] | |
elsif (hx - tx).abs == 2 && (hy - ty).abs == 1 | |
@tail_coord = [(hx + tx)/2, hy] | |
else | |
raise "you dun' goofed, kid" | |
end | |
end | |
@tail_visited_coords.add(@tail_coord) | |
end | |
end | |
file_contents.each do |line| | |
puts line | |
mo = /^([A-Z])\s(\d+)/.match(line) | |
direction = mo[1] | |
steps = mo[2].to_i | |
steps.times do | |
move_head(direction) | |
tail_follow | |
end | |
end | |
############### Day 9 - pt 2 ########################### | |
require "set" | |
file_contents = open('test') { |f| f.read }.split("\n") | |
@tail_visited_coords = Set.new([0,0]) | |
# Switched to Structs which are always pass-by-reference. | |
# Was worried (incorrectly) that arrays were pass-by-value in Ruby. | |
Coordinate = Struct.new(:x, :y, :name) do | |
def xy | |
[x,y] | |
end | |
end | |
@head_coord = Coordinate.new(40,40, 'H') | |
@one_coord = Coordinate.new(40,40, '1') | |
@two_coord = Coordinate.new(40,40, '2') | |
@three_coord = Coordinate.new(40,40, '3') | |
@four_coord = Coordinate.new(40,40, '4') | |
@five_coord = Coordinate.new(40,40, '5') | |
@six_coord = Coordinate.new(40,40, '6') | |
@seven_coord = Coordinate.new(40,40, '7') | |
@eight_coord = Coordinate.new(40,40, '8') | |
@nine_coord = Coordinate.new(40,40, '9') | |
@followers = [@one_coord, @two_coord, @three_coord, @four_coord, @five_coord, @six_coord, @seven_coord, @eight_coord, @nine_coord] | |
@leaders = [@head_coord, @one_coord, @two_coord, @three_coord, @four_coord, @five_coord, @six_coord, @seven_coord, @eight_coord] | |
def print_tail | |
world = [] | |
80.times do | |
world << ('.' * 80).split('') | |
end | |
@tail_visited_coords.each do |coord| | |
world[coord[0]][coord[1]] = '#' | |
end | |
world.each do |line| | |
line.each do |c| | |
print(c) | |
end | |
print("\n") | |
end | |
end | |
def move_head(direction) | |
case direction | |
when "R" | |
@head_coord.y = @head_coord.y + 1 | |
when "U" | |
@head_coord.x = @head_coord.x - 1 | |
when "L" | |
@head_coord.y = @head_coord.y - 1 | |
when "D" | |
@head_coord.x = @head_coord.x + 1 | |
end | |
end | |
def taxi_cab_dist(coord1, coord2) | |
(coord1.x - coord2.x).abs + (coord1.y - coord2.y).abs | |
end | |
def adjacent?(coord1, coord2) | |
return true if taxi_cab_dist(coord1, coord2) <= 1 | |
if taxi_cab_dist(coord1, coord2) == 2 | |
if coord1.x == coord2.x || coord1.y == coord2.y | |
# two steps away along same axis | |
false | |
else | |
# diagonal | |
true | |
end | |
end | |
end | |
def knot_follow(follower, leader) | |
tx, ty = follower.xy | |
hx, hy = leader.xy | |
if hx == tx && (ty - hy).abs == 2 | |
[tx, (ty + hy) / 2] | |
elsif ty == hy && (hx - tx).abs == 2 | |
[(tx + hx) / 2, ty] | |
else | |
if (hy - ty).abs == 2 && (hx - tx).abs == 1 | |
[hx, (hy + ty)/2] | |
elsif (hx - tx).abs == 2 && (hy - ty).abs == 1 | |
[(hx + tx)/2, hy] | |
else | |
[(tx + hx)/2, (ty + hy)/2] | |
end | |
end | |
end | |
file_contents.each do |line| | |
mo = /^([A-Z])\s(\d+)/.match(line) | |
direction = mo[1] | |
steps = mo[2].to_i | |
steps.times do | |
move_head(direction) | |
@followers.each_with_index do |follower, index| | |
leader = @leaders[index] | |
unless adjacent?(follower, leader) | |
puts "MOVING #{follower.name}" | |
new_x, new_y = knot_follow(follower, leader) | |
follower.x = new_x | |
follower.y = new_y | |
tail_coordinates = [@nine_coord.x, @nine_coord.y] | |
@tail_visited_coords.add(tail_coordinates) | |
end | |
end | |
end | |
end | |
#################### Day 11 ########################## | |
Monkey = Struct.new(:times_inspected, :items, :worry_op, :test_div_num, :test_true_monkey, :test_false_monkey) do | |
def catch_item(item) | |
self.items.append(item) | |
end | |
def take_turn | |
tosses = [] | |
items.each do |item| | |
new_worry = worry_op.call(item) | |
self.times_inspected = times_inspected + 1 | |
new_worry = new_worry / 3 | |
if new_worry % test_div_num == 0 | |
tosses << [new_worry, test_true_monkey] | |
else | |
tosses << [new_worry, test_false_monkey] | |
end | |
end | |
self.items = [] | |
tosses | |
end | |
end | |
# @param [String] | |
# Ex: "old * 11" | |
# Ex: "old + 4" | |
def lambdaize_worry(operation) | |
eval("-> (old) { #{operation} }") | |
end | |
@monkeys = [] | |
open('input.txt') { |f| f.read }.split("\n\n").each do |monkey_serialized| | |
_monkey_number, starting_items, worry_operation, test_div, test_true, test_false = monkey_serialized.split("\n").map(&:strip) | |
starting_items_parsed = starting_items[15..-1].split(', ').map(&:to_i) | |
worry_operation_lambda = lambdaize_worry(worry_operation[17..-1]) | |
test_div_num = test_div[19..-1].to_i | |
test_true_monkey = test_true[25..-1].to_i | |
test_false_monkey = test_false[26..-1].to_i | |
@monkeys << Monkey.new(0, starting_items_parsed, worry_operation_lambda, test_div_num, test_true_monkey, test_false_monkey) | |
end | |
def run_round | |
@monkeys.each do |monkey| | |
tosses = monkey.take_turn | |
tosses.each do |toss| | |
item, monkey_index = toss | |
target_monkey = @monkeys[monkey_index] | |
target_monkey.catch_item(item) | |
end | |
end | |
end | |
20.times do | |
run_round | |
end | |
max_times_counted = @monkeys.map { |monkey| monkey.times_inspected }.sort.reverse[0..1] | |
monkey_business = max_times_counted[0] * max_times_counted[1] | |
#################### Day 11 - part 2 ########################### | |
Monkey = Struct.new(:monkey_name, :times_inspected, :items, :worry_op, :test_div_num, :test_true_monkey, :test_false_monkey) do | |
def catch_item(item) | |
self.items.append(item) | |
end | |
def take_turn | |
tosses = [] | |
items.each do |item| | |
self.times_inspected = times_inspected + 1 | |
new_worry = worry_op.call(item) | |
new_worry_crted = new_worry % CRT_PRODUCT | |
if new_worry_crted % test_div_num == 0 | |
tosses << [new_worry_crted, test_true_monkey] | |
else | |
tosses << [new_worry_crted, test_false_monkey] | |
end | |
end | |
self.items = [] | |
tosses | |
end | |
end | |
# @param [String] | |
# Ex: "old * 11" | |
# Ex: "old + 4" | |
def lambdaize_worry(operation) | |
eval("-> (old) { #{operation} }") | |
end | |
@monkeys = [] | |
# Product of all divisibility numbers, from Chinese Remainder Theorem | |
CRT_PRODUCT = 5 * 17 * 2 * 7 * 3 * 11 * 13 * 19 | |
open('input.txt') { |f| f.read }.split("\n\n").each do |monkey_serialized| | |
monkey_number, starting_items, worry_operation, test_div, test_true, test_false = monkey_serialized.split("\n").map(&:strip) | |
starting_items_parsed = starting_items[15..-1].split(', ').map(&:to_i) | |
worry_operation_lambda = lambdaize_worry(worry_operation[17..-1]) | |
test_div_num = test_div[19..-1].to_i | |
test_true_monkey = test_true[25..-1].to_i | |
test_false_monkey = test_false[26..-1].to_i | |
@monkeys << Monkey.new(monkey_number, 0, starting_items_parsed, worry_operation_lambda, test_div_num, test_true_monkey, test_false_monkey) | |
end | |
def run_round | |
@monkeys.each do |monkey| | |
tosses = monkey.take_turn | |
tosses.each do |toss| | |
item, monkey_index = toss | |
target_monkey = @monkeys[monkey_index] | |
target_monkey.catch_item(item) | |
end | |
end | |
end | |
10000.times do |indx| | |
puts "On Round #{indx + 1}" | |
run_round | |
end | |
max_times_counted = @monkeys.map { |monkey| monkey.times_inspected }.sort.reverse[0..1] | |
puts monkey_business = max_times_counted[0] * max_times_counted[1] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment