Advent of Code 2015, Day 6 – In Ruby
Let’s see the puzzle for day 6 of 2015!
The first half of the puzzle
Because your neighbors keep defeating you in the holiday house decorating contest year after year, you’ve decided to deploy one million lights in a 1000x1000 grid.
Furthermore, because you’ve been especially nice this year, Santa has mailed you instructions on how to display the ideal lighting configuration.
Lights in your grid are numbered from 0 to 999 in each direction; the lights at each corner are at 0,0, 0,999, 999,999, and 999,0. The instructions include whether to turn on, turn off, or toggle various inclusive ranges given as coordinate pairs. Each coordinate pair represents opposite corners of a rectangle, inclusive; a coordinate pair like 0,0 through 2,2 therefore refers to 9 lights in a 3x3 square. The lights all start turned off.
To defeat your neighbors this year, all you have to do is set up your lights by doing the instructions Santa sent you in order.
For example:
- turn on 0,0 through 999,999 would turn on (or leave on) every light.
- toggle 0,0 through 999,0 would toggle the first line of 1000 lights, turning off the ones that were on, and turning on the ones that were off.
- turn off 499,499 through 500,500 would turn off (or leave off) the middle four lights.
After following the instructions, how many lights are lit?
The solution:
#!/usr/bin/env ruby
f = File.read(ARGV.first)
lights = Array.new(1000) { Array.new(1000, false) }
f.each_line do |line|
if line =~ /^turn on (\d+),(\d+) through (\d+),(\d+)$/
$1.to_i.upto($3.to_i) do |i|
$2.to_i.upto($4.to_i) do |j|
lights[i][j] = true
end
end
elsif line =~ /^turn off (\d+),(\d+) through (\d+),(\d+)$/
$1.to_i.upto($3.to_i) do |i|
$2.to_i.upto($4.to_i) do |j|
lights[i][j] = false
end
end
elsif line =~ /^toggle (\d+),(\d+) through (\d+),(\d+)$/
$1.to_i.upto($3.to_i) do |i|
$2.to_i.upto($4.to_i) do |j|
lights[i][j] = !lights[i][j]
end
end
end
end
num_lights = lights.flatten.count(true)
puts "The number of lights that are lit: #{num_lights}"
We’ll use regular expressions this time as well. We create a two-dimensional array to hold the light states, initializing them to false
, because they are off at the beginning. Our first thought might’ve been to initialize the array with this expression: Array.new(1000, Array.new(1000, false))
, but that would’ve been wrong: it would create the inner array once, and then would add it a thousand times to the outer array, meaning that every row would be the same: change one element of one row, and that element would change on every row. That’s not what we want.
Next, we process the input file line-by-line: for each line, we match it against the three possible instructions in turn. Once a match is found, we also have the four coordinates we need: on a successful match, they will be stored in $1
through $4
. Then we update the range of lights within the given coordinate pairs: we either turn them on, turn them off, or toggle them.
At the end, we count the number of true
values in the array: for this, we first flatten it (turn it into a one-dimensional array) because count
doesn’t work on two-dimensional arrays the way we need it to.
The second half of the puzzle
You just finish implementing your winning light pattern when you realize you mistranslated Santa’s message from Ancient Nordic Elvish.
The light grid you bought actually has individual brightness controls; each light can have a brightness of zero or more. The lights all start at zero.
The phrase turn on actually means that you should increase the brightness of those lights by 1.
The phrase turn off actually means that you should decrease the brightness of those lights by 1, to a minimum of zero.
The phrase toggle actually means that you should increase the brightness of those lights by 2.
What is the total brightness of all lights combined after following Santa’s instructions?
For example:
- turn on 0,0 through 0,0 would increase the total brightness by 1.
- toggle 0,0 through 999,999 would increase the total brightness by 2000000.
The solution:
#!/usr/bin/env ruby
f = File.read(ARGV.first)
lights = Array.new(1000) { Array.new(1000, 0) }
f.each_line do |line|
if line =~ /^turn on (\d+),(\d+) through (\d+),(\d+)$/
$1.to_i.upto($3.to_i) do |i|
$2.to_i.upto($4.to_i) do |j|
lights[i][j] += 1
end
end
elsif line =~ /^turn off (\d+),(\d+) through (\d+),(\d+)$/
$1.to_i.upto($3.to_i) do |i|
$2.to_i.upto($4.to_i) do |j|
lights[i][j] = [lights[i][j] - 1, 0].max
end
end
elsif line =~ /^toggle (\d+),(\d+) through (\d+),(\d+)$/
$1.to_i.upto($3.to_i) do |i|
$2.to_i.upto($4.to_i) do |j|
lights[i][j] += 2
end
end
end
end
brightness = lights.flatten.sum
puts "The number of lights that are lit: #{brightness}"
We’ll only need to adjust minor things of the first solution for this puzzle: instead of true/false values, our array will hold integers, initialized to 0. We’ll then either increase the current values by 1 or 2, or decrease them by 1, checking not to go negative. Then we summarize the elements to get the total brightness.
The code with my input text is available in the GitHub repo.
Thanks for reading! If you have any comments, additions, or corrections, feel free to reach me via e-mail.