Advent of Code 2016, Day 2: Bathroom Security

#ruby #advent of code 2016

Part A

On Day 2 we need to get access to the bathroom protected by a locked door with a keypad.

Our keypad is just 9-digit simple thing:

1 2 3
4 5 6
7 8 9

We also have an input with instructions on which buttons to press:

ULL
RRDDD
LURDL
UUUUD

Each line represents one digit to press. We start on digit 5 and then we follow the instructions. So for first line we have ULL, which means up, left and left. So we go from 5 to 2 then 1 and again left, but we have no way to go more, so we stay at 1.

For next lines we start at the last position and continue the same procedure.

Here is my solution:

data = File.readlines("2.txt").map(&:strip).map { |line| line.split("") }

buttons = {
  [0, 0] => 1,
  [1, 0] => 2,
  [2, 0] => 3,
  [0, 1] => 4,
  [1, 1] => 5,
  [2, 1] => 6,
  [0, 2] => 7,
  [1, 2] => 8,
  [2, 2] => 9
}
position = [1, 1]

def valid?(position)
  (0..2).include?(position[0]) && (0..2).include?(position[1])
end

buttons = data.map do |row|
  row.each do |move|
    case move
    when "U"
      new_position = [position[0], position[1] - 1]
    when "D"
      new_position = [position[0], position[1] + 1]
    when "L"
      new_position = [position[0] - 1, position[1]]
    when "R"
      new_position = [position[0] + 1, position[1]]
    end

    if valid?(new_position)
      position = new_position
    end
  end

  buttons[position]
end

puts buttons.join

We have buttons hash mapping positions on the keypad to button values, we have valid? method checking if our position is within the keypad and we have starting position set to [1, 1] which is the button 5.

Then we just iterate through our input, we follow the instructions updating our position and checking if it is valid. After processing each line we store button value in the array. And finally we print it out.

Part B

In second part we have more advanced keypad that looks like this:

    1
  2 3 4
5 6 7 8 9
  A B C
    D

We still start at button 5 and apply the same logic. We can use the previous code with just updated keypad mapping like this:

@buttons = {
  [0, -2] => 1,
  [-1, -1] => 2,
  [0, -1] => 3,
  [1, -1] => 4,
  [-2, 0] => 5,
  [-1, 0] => 6,
  [0, 0] => 7,
  [1, 0] => 8,
  [2, 0] => 9,
  [-1, 1] => "A",
  [0, 1] => "B",
  [1, 1] => "C",
  [0, 2] => "D"
}

And valid? method can just check if position is in the hash. If not, it is invalid position:

def valid?(position)
  @buttons.key?(position)
end

Here is the full solution:

data = File.readlines("2.txt").map(&:strip).map { |line| line.split("") }

@buttons = {
  [0, -2] => 1,
  [-1, -1] => 2,
  [0, -1] => 3,
  [1, -1] => 4,
  [-2, 0] => 5,
  [-1, 0] => 6,
  [0, 0] => 7,
  [1, 0] => 8,
  [2, 0] => 9,
  [-1, 1] => "A",
  [0, 1] => "B",
  [1, 1] => "C",
  [0, 2] => "D"
}
position = [-2, 0]

def valid?(position)
  @buttons.key?(position)
end

results = data.map do |row|
  row.each do |move|
    case move
    when "U"
      new_position = [position[0], position[1] - 1]
    when "D"
      new_position = [position[0], position[1] + 1]
    when "L"
      new_position = [position[0] - 1, position[1]]
    when "R"
      new_position = [position[0] + 1, position[1]]
    end

    if valid?(new_position)
      position = new_position
    end
  end

  @buttons[position]
end

puts results.join