On Day 25 we need to work with assembunny code again. We have our input program:
cpy a d
cpy 15 c
cpy 170 b
inc d
dec b
jnz b -2
dec c
jnz c -5
cpy d a
jnz 0 0
cpy a b
cpy 0 a
cpy 2 c
jnz b 2
jnz 1 6
dec b
dec c
jnz c -4
inc a
jnz 1 -7
cpy 2 b
jnz c 2
jnz 1 4
dec b
dec c
jnz 1 -4
jnz 0 0
out b
jnz a -19
jnz 1 -21
And it is supposed to transmit clock signal 0, 1, 0, 1, 0, 1 and so on forever. Emitting signal is done by out
instruction. What we need to figure out is what is the lowest positive integer that can be used to initialize register A so such infinite signal is registered.
For that you need to reverse engineer the code. In my case there was multiplication again in the beginning. My program is multiplying 15 by 170 and then adds the result to A register. Multiplication is implement as addition, so it is better to change input program to use mul
instruction (implemented on some other day):
add a 10
jnz 0 0
cpy a b
cpy 0 a
cpy 2 c
jnz b 2
jnz 1 6
dec b
dec c
jnz c -4
inc a
jnz 1 -7
cpy 2 b
jnz c 2
jnz 1 4
dec b
dec c
jnz 1 -4
jnz 0 0
out b
jnz a -19
jnz 1 -21
We can reverse engineer later on. What the rest of the code is doing is diving the number in A register by 2 and then calculating the modulo. And this is send as our clock signal. So to make it alternate between 0 and 1, we need to feed it the number that will look like this 101010101010101
. In my case it was 180. 180 + 15 * 170 = 2730 = 101010101010.
Here is also the naive solution to check consecutive values for A register:
require "io/console"
data = File.readlines("25.txt", chomp: true)
class InvalidSignal < StandardError; end
def run(data, registers)
pc = 0
previous = nil
ticks = 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
elsif line =~ /out ([a-d])/
register = Regexp.last_match[1]
if previous.nil?
if registers[register] == 0
previous = registers[register]
ticks += 1
else
raise InvalidSignal, ticks
end
else
if previous && (1 - registers[register]) != previous
raise InvalidSignal, ticks
else
previous = registers[register]
ticks += 1
return if ticks > 100
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/
elsif line =~ /add ([a-d]) (-?\d+)/
register = Regexp.last_match[1]
value = Regexp.last_match[2].to_i
registers[register] += value
end
pc += 1 unless jump
end
end
a = 0
while true
begin
run(data, { "a" => a, "b" => 0, "c" => 0, "d" => 0 })
puts a
break
rescue InvalidSignal => error
a += 1
puts "a = #{a}, #{error.message}"
end
end
I used program with mul
instruction to make it run fast. When executing out
instruction I was checking if the signal is correct for 100 ticks.
The second part is to just admire your achievements.