Lots of Ruby for 2016

This commit is contained in:
Bill Rossi 2025-08-07 06:20:08 -04:00
parent 7bc2c01242
commit 886def1a71
8 changed files with 532 additions and 0 deletions

42
ruby/2016/1/problem.rb Normal file
View File

@ -0,0 +1,42 @@
require "set"
input = STDIN.read.chomp
commands = input.split ", "
position = [0, 0] # Cartesian coordinates, +x is right, +y is up
direction = 0 # North I guess
visited_positions = Set.new([position.clone])
first_revisited_position = nil
commands.each do |command|
turn = command[0] == "L" ? 1 : -1
distance = command[1..].to_i
direction += turn
direction = direction % 4
distance.times do
case direction
when 0
position[1] += 1
when 1
position[0] += 1
when 2
position[1] -= 1
when 3
position[0] -= 1
end
if !visited_positions.add?(position.clone)
if first_revisited_position.nil?
first_revisited_position = position.clone
end
end
end
end
part_1 = position.map(&:abs).sum
part_2 = first_revisited_position.map(&:abs).sum
puts "Part 1: #{part_1}"
puts "Part 2: #{part_2}"

56
ruby/2016/2/problem.rb Normal file
View File

@ -0,0 +1,56 @@
class DirectionString
attr_reader :string, :start_position
def initialize(string, start_position=[1, 1])
@string = string
@start_position = start_position
end
def final_position
return start_position if string.empty?
self.class.new(string[1..], next_position).final_position
end
def next_position
case string[0]
when "D"
[start_position[0], start_position[1] + 1]
when "U"
[start_position[0], start_position[1] - 1]
when "L"
[start_position[0] - 1, start_position[1]]
when "R"
[start_position[0] + 1, start_position[1]]
end.map{ |pos| pos.clamp(0, 2) }
end
end
def reduce(array, initial_value)
acc = initial_value
array.each do |value|
acc = yield(acc, value)
end
acc
end
input = STDIN.read.chomp
direction_strings = input.split
KEYPAD = [[ 1, 2, 3 ],
[ 4, 5, 6 ],
[ 7, 8, 9 ]]
def keypad_digit(position)
x, y = position
KEYPAD[y][x]
end
digits = []
direction_strings.inject([1, 1]) do |acc, x|
y = DirectionString.new(x, acc).final_position
digits << keypad_digit(y)
y
end
puts "Part 1: #{digits.join}"

48
ruby/2016/3/problem.rb Normal file
View File

@ -0,0 +1,48 @@
class PotentialTriangle
attr_reader :sides
def initialize(line)
@sides = line.split.map(&:to_i)
end
%w[a b c].each_with_index do |side, index|
define_method("side_#{side}") do
sides[index]
end
end
def valid?
side_a_valid? && side_b_valid? && side_c_valid?
end
def side_a_valid?
side_a < side_b + side_c
end
def side_b_valid?
side_b < side_a + side_c
end
def side_c_valid?
side_c < side_b + side_a
end
end
input = STDIN.read.chomp
potential_triangle_lines = input.split("\n")
potential_triangles = potential_triangle_lines.map { |line| PotentialTriangle.new line }
valid_triangles = potential_triangles.count(&:valid?)
puts "Part 1: #{valid_triangles}"
grouped_triangle_lines = potential_triangle_lines.join(" ").split.each_with_index.each_with_object([[], [], []]) do |item_index, groups|
item, index = item_index
groups[index % 3] << item
groups
end
sideways_triangle_lines = grouped_triangle_lines.flatten.each_slice(3).to_a.map { |tri| tri.join " " }
sideways_triangles = sideways_triangle_lines.map { |line| PotentialTriangle.new line }
valid_sideways_triangles = sideways_triangles.count(&:valid?)
puts "Part 2: #{valid_sideways_triangles}"

36
ruby/2016/4/problem.rb Normal file
View File

@ -0,0 +1,36 @@
class Room
attr_reader :name, :sector_id, :checksum
def initialize(string)
@name = string.split("-")[..-2].join("-")
@sector_id = string.split("-")[-1].split("[")[0].to_i
@checksum = string.split("[")[1][..-2]
end
def calculated_checksum
letter_counts.sort_by { |k, v| [-v, -k] }.map(&:first)[0...5].join
end
def real?
calculated_checksum == checksum
end
def letter_counts
(name.chars - ["-"]).group_by(&:itself).to_h { |k, v| [k, v.count] }
end
def rotated_name
1.upto(sector_id % 26).reduce(name) { |n, _| n.codepoints.map{ |c| ((((c + 1) - 97) % 26) + 97).chr }.join("") }
end
end
input = STDIN.read.chomp
room_lines = input.split("\n")
rooms = room_lines.map { |line| Room.new line }
part_1 = rooms.select(&:real?).sum(&:sector_id)
puts "Part 1: #{part_1}"
part_2 = rooms.find { |room| room.rotated_name.include? "northpole" }.sector_id
puts "Part 2: #{part_2}"

39
ruby/2016/5/problem.rb Normal file
View File

@ -0,0 +1,39 @@
require "digest"
input = STDIN.read.chomp
salt = 0
password = []
loop do
digest = Digest::MD5.hexdigest("#{input}#{salt}")
if digest[0...5] == "00000"
password << digest[5]
puts password.join("")
break if password.length >= 8
end
salt += 1
end
part_1 = password.join("")
puts "Part 1: #{part_1}"
salt = 0
to_solve = 8
password = "--------"
loop do
digest = Digest::MD5.hexdigest("#{input}#{salt}")
if digest[0...5] == "00000"
if digest[5].to_i.to_s == digest[5] && digest[5].to_i < 8 && password[digest[5].to_i] == "-"
password[digest[5].to_i] = digest[6]
to_solve -= 1
puts password
end
break if to_solve <= 0
end
salt += 1
end
part_2 = password
puts "Part 2: #{part_2}"

23
ruby/2016/6/problem.rb Normal file
View File

@ -0,0 +1,23 @@
input = STDIN.read.chomp
columns = input
.split("\n")
.map{ |line| line.split("") }
.transpose
part_1 = columns.map do |column|
column
.group_by(&:itself)
.sort_by { |k, v| v.count }
.last[0]
end.join
puts "Part 1: #{part_1}"
part_2 = columns.map do |column|
column
.group_by(&:itself)
.sort_by { |k, v| v.count }
.first[0]
end.join
puts "Part 2: #{part_2}"

71
ruby/2016/7/problem.rb Normal file
View File

@ -0,0 +1,71 @@
class String
def to_ip
IP.new self
end
end
class IP < String
def initialize(s)
super s
end
def supports_tls?
!has_abba_in_hypernet_sequences? && has_abba?
end
def abba?
self[0] == self[3] && self[2] == self[1] && self[0] != self[1]
end
def has_abba?
chars.each_cons(4).map(&:join).map(&:to_ip).any?(&:abba?)
end
def has_abba_in_hypernet_sequences?
hypernet_sequences.any? &:has_abba?
end
def hypernet_sequences
supernet_and_hypernet_sequences.each_with_index.select do |seq, i|
i % 2 == 1
end.map(&:first)
end
def supernet_sequences
supernet_and_hypernet_sequences.each_with_index.select do |seq, i|
i % 2 == 0
end.map(&:first)
end
def supernet_and_hypernet_sequences
split("[").map{ |chunk| chunk.split("]") }.flatten.map(&:to_ip)
end
def abas
@abas ||= chars.each_cons(3).select { |triple| triple[0] == triple[2] && triple[0] != triple[1] }.map(&:join).map(&:to_ip)
end
def bab?(abas_to_match)
abas_to_match.any? { |aba| aba[0] == self[1] && self[0] == aba[1] }
end
def babs(abas_to_match)
abas.select { |aba| aba.bab?(abas_to_match) }
end
def supports_ssl?
supernet_abas = supernet_sequences.map(&:abas).flatten
hypernet_sequences.any? { |seq| seq.babs(supernet_abas).length > 0 }
end
end
input = STDIN.read.chomp
lines = input.split("\n").map(&:to_ip)
part_1 = lines.count(&:supports_tls?)
puts "Part 1: #{part_1}"
part_2 = lines.count(&:supports_ssl?)
puts "Part 2: #{part_2}"

217
ruby/2016/8/problem.rb Normal file
View File

@ -0,0 +1,217 @@
class Command
def self.for(line)
if line.split.first == "rect"
RectCommand
elsif line.split[1] == "row"
RotateRowCommand
elsif line.split[1] == "column"
RotateColumnCommand
end.new line
end
end
class RectCommand < Command
attr_reader :width, :height
def initialize(line)
@width, @height = line.split.last.split("x").map(&:to_i)
end
def apply(screen)
screen.rect!(width, height)
end
end
class RotateRowCommand < Command
attr_reader :row, :by
def initialize(line)
@row = line.split[2].split("=")[1].to_i
@by = line.split[4].to_i
end
def apply(screen)
screen.rotate_row!(row, by)
end
end
class RotateColumnCommand < Command
attr_reader :column, :by
def initialize(line)
@column = line.split[2].split("=")[1].to_i
@by = line.split[4].to_i
end
def apply(screen)
screen.rotate_column!(column, by)
end
end
class Screen
attr_reader :pixels
def initialize
@pixels = Array.new(height) { Array.new(width, " ") }
end
def width
50
end
def height
6
end
def rect!(width, height)
width.times { |x| height.times { |y| pixels[y][x] = "#" } }
end
def rotate_row!(row, by)
by.times { rotate_row_once! row }
end
def rotate_row_once!(row)
last = pixels[row].pop
pixels[row].unshift(last)
end
def rotate_column!(column, by)
by.times { rotate_column_once! column }
end
def rotate_column_once!(column)
last = pixels[height - 1][column]
(height - 1).downto(1) do |y|
pixels[y][column] = pixels[y - 1][column]
end
pixels[0][column] = last
end
def print
puts pixels.map(&:join)
end
def lit_pixel_count
pixels.flatten.count { |pixel| pixel == "#" }
end
end
class ScreenLetter
attr_reader :pixels
def initialize(screen, position)
@pixels = screen.pixels.map { |line| line.slice(position * 5, 5) }
end
def print
puts pixels.map(&:join)
puts " "
end
def to_s
pixels.map(&:join).join("\n").to_s
end
def letter_value
case to_s
when LETTER_E.chomp
"E"
when LETTER_F.chomp
"F"
when LETTER_Y.chomp
"Y"
when LETTER_K.chomp
"K"
when LETTER_R.chomp
"R"
when LETTER_I.chomp
"I"
when LETTER_J.chomp
"J"
else
"?"
end
end
end
LETTER_E = <<-E
####
#
###
#
#
####
E
LETTER_F = <<-F
####
#
###
#
#
#
F
LETTER_Y = <<-Y
# #
# #
# #
#
#
#
Y
LETTER_K = <<-K
# #
# #
##
# #
# #
# #
K
LETTER_R = <<-R
###
# #
# #
###
# #
# #
R
LETTER_I = <<-I
###
#
#
#
#
###
I
LETTER_J = <<-J
##
#
#
#
# #
##
J
input = STDIN.read.chomp
commands = input.split("\n").map { |line| Command.for line }
screen = Screen.new
commands.each { |command| command.apply screen }
part_1 = screen.lit_pixel_count
puts "Part 1: #{part_1}"
letters = 0.upto(9).map do |position|
ScreenLetter.new screen, position
end
part_2 = letters.map(&:letter_value).join
puts "Part 2: #{part_2}"