On Day 23 we are playing again with assembunny code, but now we have one more instruction tgl x
.
Here is the full solution:
require "io/console"
data = File.readlines("23.txt", chomp: true)
registers = { "a" => 7, "b" => 0, "c" => 0, "d" => 0 }
pc = 0
iterations = 0
while pc < data.size
line = data[pc]
jump = false
if line =~ /cpy (-?\d+) ([a-d])/
value = Regexp.last_match[1].to_i
register = Regexp.last_match[2]
registers[register] = value
elsif line =~ /cpy ([a-d]) ([a-d])/
source_register = Regexp.last_match[1]
target_register = Regexp.last_match[2]
registers[target_register] = registers[source_register]
elsif line =~ /inc ([a-d])/
register = Regexp.last_match[1]
registers[register] += 1
elsif line =~ /dec ([a-d])/
register = Regexp.last_match[1]
registers[register] -= 1
elsif line =~ /jnz (-?\d+) (-?\d+)/
value = Regexp.last_match[1].to_i
offset = Regexp.last_match[2].to_i
if value != 0
pc += offset
jump = true
end
elsif line =~ /jnz ([a-d]) (-?\d+)/
register = Regexp.last_match[1]
offset = Regexp.last_match[2].to_i
if registers[register] != 0
pc += offset
jump = true
end
elsif line =~ /jnz (-?\d+) ([a-d])/
register = Regexp.last_match[2]
offset = registers[register]
value = Regexp.last_match[1].to_i
if value != 0
pc += offset
jump = true
end
elsif line =~ /tgl ([a-d])/
register = Regexp.last_match[1]
offset = registers[register]
if data[pc + offset]
case data[pc + offset][0..2]
when "inc"
data[pc + offset][0..2] = "dec"
when "dec"
data[pc + offset][0..2] = "inc"
when "cpy"
data[pc + offset][0..2] = "jnz"
when "jnz"
data[pc + offset][0..2] = "cpy"
when "tgl"
data[pc + offset][0..2] = "inc"
end
end
end
pc += 1 unless jump
iterations += 1
end
puts registers.inspect
It is slightly modified solution for Day 12.
Now in second part we need to execute the same code, but now A register should be set to 12 instead of 7. There is an information in the description that now our computer is overheating. And yeah, if you run it is actually computing but not returning any value. There is a hint that we should use multiply instead of adding one.
So the task is to reverse engineer our program that looks like this:
cpy a b
dec b
cpy a d
cpy 0 a
cpy b c
inc a
dec c
jnz c -2
dec d
jnz d -5
dec b
cpy b c
cpy c d
dec d
inc c
jnz d -2
tgl c
cpy -16 c
jnz 1 c
cpy 84 c
jnz 75 d
inc a
inc d
jnz d -2
inc c
jnz c -5
We can implement a method to print out debugger screen from out computer with registers:
def render(data, pc, registers)
$stdout.clear_screen
data.each_with_index do |line, index|
puts [index == pc ? "=> " : " ", index.to_s.rjust(2), line].join(" ")
end
puts "A = #{registers["a"]}, B = #{registers["b"]}, C = #{registers["c"]}, D = #{registers["d"]}, PC = #{pc}"
end
I used this method to execute code line by line and see what it is doing. And on first 10 lines it is computing factorial, but using only addition. I didn’t execute that many lines, but I think it is a factorial of what is in the A register. For 7 it is fine, but for 12 it is a large number and a lot of add instructions.
What we can do is to implement multiplication instruction and rewrite our program. Here is updated solution with mul
instruction:
require "io/console"
data = File.readlines("23mul.txt", chomp: true)
registers = { "a" => 12, "b" => 0, "c" => 0, "d" => 0 }
pc = 0
def render(data, pc, registers)
$stdout.clear_screen
data.each_with_index do |line, index|
puts [index == pc ? "=> " : " ", index.to_s.rjust(2), line].join(" ")
end
puts "A = #{registers["a"]}, B = #{registers["b"]}, C = #{registers["c"]}, D = #{registers["d"]}, PC = #{pc}"
end
iterations = 0
while pc < data.size
line = data[pc]
jump = false
if line =~ /cpy (-?\d+) ([a-d])/
value = Regexp.last_match[1].to_i
register = Regexp.last_match[2]
registers[register] = value
elsif line =~ /cpy ([a-d]) ([a-d])/
source_register = Regexp.last_match[1]
target_register = Regexp.last_match[2]
registers[target_register] = registers[source_register]
elsif line =~ /inc ([a-d])/
register = Regexp.last_match[1]
registers[register] += 1
elsif line =~ /dec ([a-d])/
register = Regexp.last_match[1]
registers[register] -= 1
elsif line =~ /jnz (-?\d+) (-?\d+)/
value = Regexp.last_match[1].to_i
offset = Regexp.last_match[2].to_i
if value != 0
pc += offset
jump = true
end
elsif line =~ /jnz ([a-d]) (-?\d+)/
register = Regexp.last_match[1]
offset = Regexp.last_match[2].to_i
if registers[register] != 0
pc += offset
jump = true
end
elsif line =~ /jnz (-?\d+) ([a-d])/
register = Regexp.last_match[2]
offset = registers[register]
value = Regexp.last_match[1].to_i
if value != 0
pc += offset
jump = true
end
elsif line =~ /tgl ([a-d])/
register = Regexp.last_match[1]
offset = registers[register]
if data[pc + offset]
case data[pc + offset][0..2]
when "inc"
data[pc + offset][0..2] = "dec"
when "dec"
data[pc + offset][0..2] = "inc"
when "cpy"
data[pc + offset][0..2] = "jnz"
when "jnz"
data[pc + offset][0..2] = "cpy"
when "tgl"
data[pc + offset][0..2] = "inc"
end
end
# additional instructions to execute multiplication instantly, only for 23mul.txt updated instruction set
elsif line =~ /mul ([a-d]) ([a-d])/
register_1 = Regexp.last_match[1]
register_2 = Regexp.last_match[2]
registers[register_1] *= registers[register_2]
elsif line =~ /nop/
end
pc += 1 unless jump
iterations += 1
end
puts registers.inspect
And here is updated program:
cpy a b
dec b
cpy a d
cpy 0 a
cpy b c
nop
mul c d
cpy c a
cpy 0 c
cpy 0 d
dec b
cpy b c
cpy c d
dec d
inc c
jnz d -2
tgl c
cpy -16 c
jnz 1 c
cpy 84 c
jnz 75 d
inc a
inc d
jnz d -2
inc c
jnz c -5
I also added nop
instruction to just keep the same number of lines in this program, so all jump instructions work the same.