diff --git a/ruby/2016/1/problem.rb b/ruby/2016/1/problem.rb new file mode 100644 index 0000000..ce450fd --- /dev/null +++ b/ruby/2016/1/problem.rb @@ -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}" diff --git a/ruby/2016/2/problem.rb b/ruby/2016/2/problem.rb new file mode 100644 index 0000000..024c1ac --- /dev/null +++ b/ruby/2016/2/problem.rb @@ -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}" diff --git a/ruby/2016/3/problem.rb b/ruby/2016/3/problem.rb new file mode 100644 index 0000000..64dfa81 --- /dev/null +++ b/ruby/2016/3/problem.rb @@ -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}" diff --git a/ruby/2016/4/problem.rb b/ruby/2016/4/problem.rb new file mode 100644 index 0000000..87dd16f --- /dev/null +++ b/ruby/2016/4/problem.rb @@ -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}" diff --git a/ruby/2016/5/problem.rb b/ruby/2016/5/problem.rb new file mode 100644 index 0000000..618f50c --- /dev/null +++ b/ruby/2016/5/problem.rb @@ -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}" diff --git a/ruby/2016/6/problem.rb b/ruby/2016/6/problem.rb new file mode 100644 index 0000000..dc70e98 --- /dev/null +++ b/ruby/2016/6/problem.rb @@ -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}" diff --git a/ruby/2016/7/problem.rb b/ruby/2016/7/problem.rb new file mode 100644 index 0000000..1a9c4a3 --- /dev/null +++ b/ruby/2016/7/problem.rb @@ -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}" diff --git a/ruby/2016/8/problem.rb b/ruby/2016/8/problem.rb new file mode 100644 index 0000000..2e0fdf5 --- /dev/null +++ b/ruby/2016/8/problem.rb @@ -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}"