放射線耐性 Quine (1 文字消しても動く Quine)

ref: https://github.com/mame/radiation-hardened-quine

放射線はメモリエラーを引き起こすらしいです。そんな放射線が飛び交う過酷な環境でも、できることなら Quine したい。

ということで、プログラム内の 1 文字をランダムに消しても元のプログラムを出力する、なんともロバストな Quine を書きました。*1

何を言っているかわからないと思いますが、こんなふうに動くものです。

# ランダムに 1 文字消すスクリプト
$ cat mutate.rb
src = $<.read
src[rand(src.size), 1] = ""
print src

# rrquine.rb からランダムに 1 文字消したプログラムを生成する
$ ruby mutate.rb rrquine.rb > broken.rb

# 壊れたプログラムを実行する (!)
$ ruby broken.rb > rrquine2.rb

# 元に戻る (!!)
$ diff rrquine.rb rrquine2.rb # 一致!

正直書けるわけないと思ってましたが、なんと書けてしまった。Ruby 恐ろしいなあ。他に書ける言語があるだろうか。*2

以下コード。もっと賢い方法があったら教えてください。(追記: GitHub のリポジトリも作っておきました。)

eval='eval$q=%q(puts %q(10210/#{1 1 if 1==21}}/.i rescue##/

1 1"[13,213].max_by{|s|s.size}#"##").gsub(/\d/){["=\47eval$q=%q(#$q)#\47##\47

",:eval,:instance_,"||=9"][eval$&]}
exit)#'##'

instance_eval='eval$q=%q(puts %q(10210/#{1 1 if 1==21}}/.i rescue##/

1 1"[13,213].max_by{|s|s.size}#"##").gsub(/\d/){["=\47eval$q=%q(#$q)#\47##\47

",:eval,:instance_,"||=9"][eval$&]}
exit)#'##'

/#{eval eval if eval==instance_eval}}/.i rescue##/

eval eval"[eval||=9,instance_eval||=9].max_by{|s|s.size}#"##"

以下は最初に公開したバージョン。とりあえず動いただけのものなのでいろいろ汚い。

"%\  #{at_exit{eval'(eval q=%(s=%(B%A  C{at_exit{ZGQG##G

}}}ABB.rescue x rescue 42##B

Z=GQG##G

instance_Z=GQG##G

Z Z if Z==instance_Z
).gsub ?Z,%[eval]
7.times{|n|s.gsub! (n+65).chr,(n<1?92:n+33).chr}
puts s.gsub ?Q,%[(eval q=%(]+q+%[))#]
$stdout.flush
exit!0))#'##'

}}}\"".rescue x rescue 42##"

eval='(eval q=%(s=%(B%A  C{at_exit{ZGQG##G

}}}ABB.rescue x rescue 42##B

Z=GQG##G

instance_Z=GQG##G

Z Z if Z==instance_Z
).gsub ?Z,%[eval]
7.times{|n|s.gsub! (n+65).chr,(n<1?92:n+33).chr}
puts s.gsub ?Q,%[(eval q=%(]+q+%[))#]
$stdout.flush
exit!0))#'##'

instance_eval='(eval q=%(s=%(B%A  C{at_exit{ZGQG##G

}}}ABB.rescue x rescue 42##B

Z=GQG##G

instance_Z=GQG##G

Z Z if Z==instance_Z
).gsub ?Z,%[eval]
7.times{|n|s.gsub! (n+65).chr,(n<1?92:n+33).chr}
puts s.gsub ?Q,%[(eval q=%(]+q+%[))#]
$stdout.flush
exit!0))#'##'

eval eval if eval==instance_eval

*1:まあ、実際の放射線によるメモリエラーは除去じゃなくて文字化けですが、さすがにそれは対処不可能だと思う……。

*2:0 バイトで Quine と主張するのはナシで。