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}"