Advent of Code 2020: Day 14 (Ruby solution)

Today's tasks were bit manipulation. The initialization program (your puzzle input) can either update the bitmask or write a value to memory. Values and memory addresses are both 36-bit unsigned integers.

In the first task, a bitmask is used to transform a value before writing it. A 0 or 1 overwrites the corresponding bit in the value, while an X leaves the bit in the value unchanged.

In another task, a bitmask is used to transform an address. Immediately before a value is written to memory, each bit in the bitmask modifies the corresponding bit of the destination memory address in the following way:

  • If the bitmask bit is 0, the corresponding memory address bit is unchanged.
  • If the bitmask bit is 1, the corresponding memory address bit is overwritten with 1.
  • If the bitmask bit is X, the corresponding memory address bit is floating. A floating bit is not connected to anything and instead fluctuates unpredictably. In practice, this means the floating bits will take on all possible values, potentially causing many memory addresses to be written all at once.

This is my Ruby solution:

def int_to_bin value
  value.to_i.to_s(2).rjust(36, "0")
end

def sum_mem_values file, decode_address = false
  mem = {}
  mask = 'X' * 36
  file.each_line do |line|
    command = line.split " = "
    variable = command.first
    address = variable.scan(/\d+/).first.to_i
    value = command.last.strip

    if variable == 'mask'
      mask = value
    elsif decode_address
      address_bin = int_to_bin(address)
      ['0', '1'].repeated_permutation(mask.count('X')) do |x_values|
        address_bin_copy = address_bin
        mask.each_char.with_index do |char, index|
          address_bin_copy[index] = '1' if char == '1'
          address_bin_copy[index] = x_values[mask[0...index].count('X')] if char == 'X'
        end
        mem[address_bin_copy.to_i(2)] = value.to_i
      end
    else # decode value
      value_bin = int_to_bin(value)
      mask.each_char.with_index do |char, index|
        value_bin[index] = '1' if char == '1'
        value_bin[index] = '0' if char == '0'
      end
      mem[address] = value_bin.to_i(2)
    end
  end
  mem.values.sum
end

file = File.read('inputs/day14.txt')
puts "First task answer: #{sum_mem_values(file)}"
puts "Second task answer: #{sum_mem_values(file, true)}"