Skip to content

Instantly share code, notes, and snippets.

@ocelotsloth
Last active December 7, 2021 14:41
Show Gist options
  • Save ocelotsloth/59cdf3ab32212450d0a9dccc402db8eb to your computer and use it in GitHub Desktop.
Save ocelotsloth/59cdf3ab32212450d0a9dccc402db8eb to your computer and use it in GitHub Desktop.
@ocleotsloth's Advent of Code 2021
# Helper functions for all days
def nested_iter a, b
a.each { |ai| b.each { |bi| yield(ai, bi) } }
end
def day1_1
input = File.readlines('2021.1.txt').map { |line| line.strip.to_i }
total = 0
input.each_with_index do |measurement, index|
total += 1 if index != 0 and measurement > input[index - 1]
end
total
end
day1_1
def day1_2
input = File.readlines('2021.1.txt').map { |line| line.strip.to_i }
sums = []
input.each_with_index do |_, index|
window = input[index,3]
sums << window.sum if window.length == 3
end
total = 0
sums.each_with_index do |s, i|
pair = sums[i,2]
total += 1 if pair.length == 2 and pair[1] > pair[0]
end
total
end
day1_2
def day2_1
position = [0,0] # x, z
input = File.readlines('2021.2.txt').each do |cmd|
cmd = cmd.strip.split
cmd[1] = cmd[1].to_i
case cmd[0]
when "forward"
position[0] += cmd[1]
when "down"
position[1] += cmd[1]
when "up"
position[1] -= cmd[1]
end
end
position[0] * position[1]
end
day2_1
def day2_2
aim = 0
position = [0,0] # x, z
input = File.readlines('2021.2.txt').each do |cmd|
cmd = cmd.strip.split
cmd[1] = cmd[1].to_i
case cmd[0]
when "forward"
position[0] += cmd[1]
position[1] += cmd[1] * aim
when "down"
aim += cmd[1]
when "up"
aim -= cmd[1]
end
end
position[0] * position[1]
end
day2_2
def day3_most_common input
common = [0,0,0,0,0,0,0,0,0,0,0,0]
input.each do |line|
(0..11).each do |n|
common[n] += line[n] == '0' ? -1 : 1
end
end
common.map { |n| n < 0 ? '0' : '1' }
end
def day3_1
input = File.readlines('2021.3.txt')
gammaarr = day3_most_common input
epsilonarr = gammaarr.map { |n| n == '1' ? '0' : '1' }
gamma = gammaarr.join.to_i 2
epsilon = epsilonarr.join.to_i 2
gamma * epsilon
end
day3_1
def day3_2
input = File.readlines('2021.3.txt')
oxy_com = day3_most_common input
co2_com = oxy_com.map { |n| n == '0' ? '1' : '0' }
oxy_in = input.dup
i = 0
while oxy_in.length > 1
oxy_in = oxy_in.filter { |n| n[i] == oxy_com[i] }
oxy_com = day3_most_common oxy_in
i += 1
end
co2_in = input.dup
i = 0
while co2_in.length > 1
co2_in = co2_in.filter { |n| n[i] == co2_com[i] }
co2_com = day3_most_common(co2_in).map { |n| n == '0' ? '1' : '0' }
i += 1
end
oxy = oxy_in.join.to_i 2
co2 = co2_in.join.to_i 2
oxy * co2
end
day3_2
class Bingo
# Create a new Bingo board from a 2d array of integers. It
# will convert to int if you pass strings or similar.
#
# @param board [Array<Array<Integer>>] 5x5 2d array of ints
def initialize board
raise "invalid board: must have exactly 5 rows" unless board.length == 5
@board = Array.new(5)
board.each_with_index do |row, i|
raise "invalid board: rows must have exactly 5 cols" unless row.length == 5
@board[i] = row.map { |n| Integer(n) }
end
@marks = []
end
# Parse a Bingo board string and return new Bingo object
#
# @param boardStr [String] Bingo board string
# @return [Bingo] new bingo board
def self.parse boardStr
Bingo.new(boardStr.strip.split("\n").map { |r| r.strip.split })
end
# Evaluate whether a board has won or not.
#
# @return [TrueClass,FalseClass]
def evaluate
(@board + @board.transpose)
.each { |group| return true if evaluateGroup(group) }
false
end
# Mark a given number. Returns whether or not the board is won
#
# @param i [Integer] Next called number
# @return [TrueClass,FalseClass] Whether new board state wins
def mark i
i = Integer(i)
raise "invalid number: already called" if @marks.index(i)
@marks << i
evaluate
end
# Sum all unmarked numbers
#
# @return [Integer] sum of all unmarked positions
def sumUnmarked
@board.flatten.difference(@marks).sum
end
private
# Evaluate whether a row of 5 has won or not
#
# @return [TrueClass,FalseClass]
def evaluateGroup group
return true if group.&(@marks).length == 5
false
end
end
def day4_1
input = File.read('2021.4.txt').split("\n\n")
marks = input.shift.split(',')
boards = input.map { |boardStr| Bingo.parse(boardStr) }
nested_iter(marks, boards) do |mark, board|
return board.sumUnmarked * Integer(mark) if board.mark(mark)
end
end
day4_1
def day4_2
input = File.read('2021.4.txt').split("\n\n")
marks = input.shift.split(',')
boards = input.map { |boardStr| Bingo.parse(boardStr) }
lastBoard = nil
marks.each do |mark|
boards.delete_if { |board| board.mark(mark) }
lastBoard = boards[0] if boards.length == 1
return lastBoard.sumUnmarked * Integer(mark) if boards.length == 0
end
end
day4_2
# Yeah this is a terribly messy solution. :(
def day5_1
grid = {}
sum = 0
File.readlines('2021.5.txt').map do |line|
line.strip.split(' -> ').map { |p| p.split(',') }
end.each do |line|
line[0][0] = Integer(line[0][0])
line[0][1] = Integer(line[0][1])
line[1][0] = Integer(line[1][0])
line[1][1] = Integer(line[1][1])
# skip diagonal lines
next unless line[0][0] == line[1][0] or line[0][1] == line[1][1]
x1 = line[0][0] < line[1][0] ? line[0][0] : line[1][0]
x2 = line[0][0] < line[1][0] ? line[1][0] : line[0][0]
y1 = line[0][1] < line[1][1] ? line[0][1] : line[1][1]
y2 = line[0][1] < line[1][1] ? line[1][1] : line[0][1]
nested_iter((x1..x2), (y1..y2)) do |x,y|
grid[x] = {} unless grid[x]
grid[x][y] = grid[x][y] ? grid[x][y] + 1 : 1
end
end
grid.each_value { |col| col.each_value { |point| sum += 1 if point > 1 } }
sum
end
day5_1
def day5_2
grid = {}
sum = 0
File.readlines('2021.5.txt').map do |line|
line.strip.split(' -> ').map { |p| p.split(',') }
end.each do |line|
line[0][0] = Integer(line[0][0])
line[0][1] = Integer(line[0][1])
line[1][0] = Integer(line[1][0])
line[1][1] = Integer(line[1][1])
if line[0][0] == line[1][0] or line[0][1] == line[1][1]
x1 = line[0][0] < line[1][0] ? line[0][0] : line[1][0]
x2 = line[0][0] < line[1][0] ? line[1][0] : line[0][0]
y1 = line[0][1] < line[1][1] ? line[0][1] : line[1][1]
y2 = line[0][1] < line[1][1] ? line[1][1] : line[0][1]
nested_iter((x1..x2), (y1..y2)) do |x,y|
grid[x] = {} unless grid[x]
grid[x][y] = grid[x][y] ? grid[x][y] + 1 : 1
end
else
sx = line[0][0] < line[1][0] ? 1 : -1
sy = line[0][1] < line[1][1] ? 1 : -1
x = line[0][0] - sx
y = line[0][1] - sy
while x != line[1][0]
x += sx
y += sy
grid[x] = {} unless grid[x]
grid[x][y] = grid[x][y] ? grid[x][y] + 1 : 1
end
end
end
grid.each_value { |col| col.each_value { |point| sum += 1 if point != 1 } }
sum
end
day5_2
def day6_1 days=80
# n fish: 0 1 2 3 4 5 6 7 8
school = [ 0, 0, 0, 0, 0, 0, 0 ]
little = [ 0, 0 ]
File.read('2021.6.txt').strip.split(',').each do |fish|
fish = Integer(fish)
if fish <= 6
school[fish] += 1
else
little[fish - 6] += 1
end
end
(1..days).each do |day|
school = school.rotate
shift = little[0]
little[0] = little[1]
little[1] = school[6]
school[6] += shift
end
(school + little).sum
end
day6_1
def day6_2
day6_1 256
end
day6_2
def day7_1
sorted = File.read('2021.7.txt').strip.split(',').map { |i| Integer(i) }.sort
midpoint = sorted.length / 2 # integer division
median = if sorted.length.even?
sorted[midpoint-1, 2].sum / 2.0
else
sorted[midpoint]
end
Integer(sorted.map { |i| (i - median).abs }.sum)
end
day7_1
def day7_2
input = File.read('2021.7.txt').strip.split(',').map { |i| Float(i) }
mean = (input.sum / input.length).round
mean_tr = Integer(input.sum / input.length)
# Returns both results for rounded and truncated mean. Seems like there's
# an issue in how the results were calculated on the site such that the example
# and actual results require different rounding techniques.
input.map do |i|
n = (Integer(i) - mean).abs
n_tr = (Integer(i) - mean_tr).abs
[(n ** 2 + n) / 2, (n_tr ** 2 + n_tr) / 2]
end.transpose.map { |l| l.sum }
end
day7_2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment