Advent of Code 2020: Day 8 (Ruby solution)
Tasks of the 8th day of Advent of Code 2020 are about executing instructions from the input file. The instruction list is corrupted. In the first task, you have to detect the infinite loop, stop the algorithm in this place, and check its result so far. The second task is to repair this list by finding one wrong command and swapping them to the correct one.
I chose an objective approach and implemented InstructionProcessor and ProcessInstruction classes.
class InstructionProcessor
class InfinityLoop < StandardError
attr_reader :acc, :steps_sequence
def initialize acc, steps_sequence
@acc = acc
@steps_sequence = steps_sequence
end
end
def initialize
@acc = 0
@index = 0
end
def process_instructions instruction
steps_sequence = []
while @index < instruction.size
raise InfinityLoop.new(@acc, steps_sequence) if steps_sequence.include? @index
steps_sequence << @index
step = instruction.get_step @index
send step[:command], step[:value] if step
end
@acc
end
private
def acc value
@acc += value
@index += 1
end
def jmp value
@index += value
end
def nop value
@index += 1
end
end
class ProcessInstruction
def initialize path
@file = File.read('inputs/day8.txt')
reload
end
def reload
@steps = @file.lines.map do |line|
args = line.split
{command: args.first, value: args.last.to_i}
end
end
def get_step index
@steps[index]
end
def size
@steps.size
end
def swap_command index
step = @steps[index]
if step[:command] == 'jmp'
@steps[index][:command] = 'nop'
elsif step[:command] == 'nop'
@steps[index][:command] = 'jmp'
end
end
end
Having those classes implemented it is easy to solve both tasks:
instructions = ProcessInstruction.new('inputs/day8.txt')
retry_count = 0
begin
res = InstructionProcessor.new.process_instructions(instructions)
puts "Second task answer: #{res}"
rescue InstructionProcessor::InfinityLoop => e
if retry_count == 0
puts "First task answer: #{e.acc}"
original_step_sequence = e.steps_sequence
end
instructions.reload
retry_count += 1
instructions.swap_command(original_step_sequence[original_step_sequence.size - retry_count])
retry
end