require_relative 'util' # --- Day 8: Two-Factor Authentication --- # You come across a door implementing what you can only assume is an # implementation of two-factor authentication after a long game of # requirements telephone. # To get past the door, you first swipe a keycard (no problem; there # was one on a nearby desk). Then, it displays a code on a little # screen, and you type that code on a keypad. Then, presumably, the # door unlocks. # Unfortunately, the screen has been smashed. After a few minutes, # you've taken everything apart and figured out how it works. Now you # just have to work out what the screen would have displayed. # The magnetic strip on the card you swiped encodes a series of # instructions for the screen; these instructions are your puzzle # input. The screen is 50 pixels wide and 6 pixels tall, all of which # start off, and is capable of three somewhat peculiar operations: # - `rect AxB` turns on all of the pixels in a rectangle at the top-left # of the screen which is A wide and B tall. # - `rotate row y=A by B` shifts all of the pixels in row A (0 is the # top row) right by B pixels. Pixels that would fall off the right # end appear at the left end of the row. # - `rotate column x=A by B` shifts all of the pixels in column A (0 # is the left column) down by B pixels. Pixels that would fall off # the bottom appear at the top of the column. # For example, here is a simple sequence on a smaller screen: # - `rect 3x2` creates a small rectangle in the top-left corner: # # ###.... # ###.... # ....... # - `rotate column x=1 by 1` rotates the second column down by one # pixel: # # #.#.... # ###.... # .#..... # - `rotate row y=0 by 4` rotates the top row right by four pixels: # # ....#.# # ###.... # .#..... # - `rotate column x=1 by 1` again rotates the second column down by # one pixel, causing the bottom pixel to wrap back to the top: # # .#..#.# # #.#.... # .#..... # As you can see, this display technology is extremely powerful, and # will soon dominate the tiny-code-displaying-screen market. That's # what the advertisement on the back of the display tries to convince # you, anyway. # There seems to be an intermediate check of the voltage used by the # display: after you swipe your card, if the screen did work, how many # pixels should be lit? input = File.open('08.txt') { |f| f.readlines.map(&:chomp) } test_input = ['rect 3x2', 'rotate column x=1 by 1', 'rotate row y=0 by 4', 'rotate column x=1 by 1'].freeze class Display def initialize(width, height) @pixels = Array.new(height) { Array.new(width, false) } @width = width @height = height end def rect(width, height) (0...height).each do |y| (0...width).each { |x| @pixels[y][x] = true } end end def rotate_row(y, offset) @pixels[y] = @pixels[y].rotate(-offset) end def rotate_column(x, offset) column = @pixels.transpose[x].rotate(-offset) (0...@height).each { |y| @pixels[y][x] = column[y] } end def lit_pixels count = 0 @pixels.each { |row| count += row.count(true) } count end def show @pixels.each do |row| row.each { |p| print(p ? '#' : '.') } puts end puts end end def easy(input, width, height) display = Display.new(width, height) input.each do |line| case line when /^rect (\d+)x(\d+)/ display.rect($1.to_i, $2.to_i) when /^rotate row y=(\d+) by (\d+)/ display.rotate_row($1.to_i, $2.to_i) when /^rotate column x=(\d+) by (\d+)/ display.rotate_column($1.to_i, $2.to_i) end end display.lit_pixels end assert(easy(test_input, 7, 3) == 6) puts "easy(input, 50, 6): #{easy(input, 50, 6)}" # --- Part Two --- # You notice that the screen is only capable of displaying capital letters; in the font it uses, each letter is 5 pixels wide and 6 tall. # After you swipe your card, what code is the screen trying to display? def hard(input, width, height) display = Display.new(width, height) input.each do |line| case line when /^rect (\d+)x(\d+)/ display.rect($1.to_i, $2.to_i) when /^rotate row y=(\d+) by (\d+)/ display.rotate_row($1.to_i, $2.to_i) when /^rotate column x=(\d+) by (\d+)/ display.rotate_column($1.to_i, $2.to_i) end end display.show end hard(input, 50, 6)