6
1

はじめに

あなたは 超絶技巧プログラミング を知っていますか?

書籍「あなたの知らない超絶技巧プログラミングの世界」では以下のように書かれています。

超絶技巧プログラミングとは、「実用的なプログラミング言語を使って全く実用的でないものを作る遊び」です。

超絶技巧プログラミングの例として、以下のようなものがあります。

上記のポストを投稿した tompng さんが RubyKaigi にて紹介していた超絶技巧プログラミングに魅了され、僕も入門してみました。

この記事では、書籍「あなたの知らない超絶技巧プログラミングの世界」の紹介と、書籍に記載の方法で作ってみたオリジナルのプログラムの紹介を行います。

超絶技巧プログラミング

様々な種類の超絶技巧プログラミングがあります。書籍では以下のプログラムの紹介がありました。

  • 自己生成プログラミング (Quine)
    • プログラムを実行すると、そのプログラムが出力されるプログラム
    • アスキーアートかつ Quine な、アスキーアート Quine
    • 動く、アニメーション Quine
    • 円柱に書かれたプログラムを実行すると、その円柱の3Dモデルが生成される
  • 〇〇禁止プログラミング
    • 記号だけでプログラムを書く
    • 数字だけでプログラムを書く
    • _ だけでプログラムを書く
    • 小文字アルファベットだけでプログラムを書く
  • 「堅牢」なプログラミング
    • 「宇宙線耐性」のあるプログラム
    • リポグラム耐性のあるプログラム
    • プログラムのどこかに XXX を挿入されても動くプログラム

この中から、比較的簡単そうなアスキーアート Quineを作ってみたので、実装の流れを紹介します。

アスキーアートプログラム

はじめに、プログラムをアスキーアートにする方法を紹介します。この記事では Ruby を使用します。

Rubyでは、eval の引数に Ruby プログラムの文字列を与えると、それを実行できます。そのため Ruby で超絶技巧プログラミングを作る手順として、プログラムで文字列を組み立て、それを eval で実行することが多いです。

> eval 'puts "Hello World"'
Hello World

また、 %w (文字列の配列の%記法) と join (または *"") を使うと、好きなように改行やスペースを入れた文字列を、元の文字列に修正することができます。

%w(pu
ts
"Hel lo, W
orl
d").join
=> "puts\"Hello,World\""

eval とこれを組み合わせ、eval(%w(...)*"")... の部分にプログラムを好きなように書くことで、アスキーアートプログラムが作成できます。(... の部分のプログラムにスペースやバックスラッシュが必要な場合は工夫が必要です。)

アスキーアートプログラムの例
eval(%w(pu
ts      "H
el      lo
,w      or
ld!")*"")#
=> Hello,world!

https://github.com/mame/trance-book/blob/master/2-1/square-hello.rb より

アスキーアートプログラムをより簡単に作る方法として、アスキーアートプログラムを生成するプログラムを作る方法も紹介されていました。

手順としては、初めに以下のようなアスキーアートを用意して、そのアスキーアートの文字を1つずつプログラムに置換していくだけです。

アスキーアートプログラムを生成するプログラム
asciiart = <<END
   ##########
 ##          ##
##   #    #   ##
##            ##
##  ##    ##  ##
##    ####    ##
 ##          ##
   ##########
END

code = <<'END'
  3.times {
    puts "Hello%c:-%c" % [32, 41]
  }
  #######
END

code = code.split.join
code = 'eval(%w(' + code + ')*"")'
code = asciiart.gsub("#") { code.slice!(0, 1) }
puts code

実行結果がこちらです。

smile.rb
   eval(%w(3.
 ti          me
s{   p    u   ts
"H            el
lo  %c    :-  %c
"%    [32,    41
 ]}          ##
   #####)*"")

ちゃんと実行もできます。

$ ruby smile.rb
Hello :-)
Hello :-)
Hello :-)

Quine

Quine(クワイン)とは、自分自身のソースコードを出力するプログラムです。作り方はいろいろありますが、書籍で紹介されていた方法を簡単に説明すると、「文字列を置換して、ソースコードと同じ文字列を作る」という方法が一般的です。先に Quine の例を見てみましょう。

s = "s = ...; print s.sub(\"...\", s.dump)"; print s.sub("...", s.dump)

雑に解説すると、こんな感じになっています。

  • 前半の s = XXXX の部分
    • ...s.dump に置換する文字列 s を定義
  • 後半の print YYYY の部分
    • s の ... の部分を s.dump に置換したものを print する
      • 置換により s = ...s = "s = ...; print s.sub(\"...\", s.dump)" になる
      • 元のプログラムと同じ文字列が print される

上記で紹介したプログラムは ...dump などが何度も出現しますが、eval などを活用すると次のように書くこともできます。
https://github.com/mame/trance-book/blob/master/3-2/quine3.rb

eval s=%q(puts %(eval s=%q(#{ s })))

さいごに、アスキーアートQuineを作るためのテクニックを紹介します。

アスキーアートプログラムで使用したテクニックを追加したQuine がこちらです。これをアスキーアートプログラム生成プログラムと組み合わせていい感じに整形すると、アスキーアートQuineができます。

eval$s=%w(
  s = %(eval$s=%w(#{$s})*"");
  puts(s)
)*""

オリジナルアスキーアート Quine を作ってみる

ここまでに紹介したテクニックを組み合わせると、自分で好きな形のオリジナルアスキーアート Quine が作れます。

Qiita のロゴ のアスキーアート Quine を作ってみました。

qiia_quine.rb
          eval$s=%q(eval(                                               %w(s=
      %(eval$s=%q(#{$s}));pu              ts(32         .chr*           10+s)
    #######           #########           #####         #####           #####
  ######                  #######                                   #################              #####       ####
 ######                     ######                                  #################         ################ ####
######                       ######       #####         #####           #####               #########   ###########
######                       ######       #####         #####           #####              ######            ######
######                       #####        #####         #####           #####             ######              #####
 ######                     ######        #####         #####           #####             ######              #####
  ######                   ######         #####         #####           #####             ######              #####
   #######              ########          #####         #####           #####              ######            ######
     ###########################          #####         #####            ############       #########   ###########
         ################  #######        #####         #####             ###########          #############   ####
                            )*""))

手順は先ほど紹介した通り、アスキーアートを用意して、それをQuineのプログラムに置換するだけです。いい感じに表示するための工夫はしていますが(32.chr*10 など)、ほとんど上記で紹介したテクニックのみで実装しています。

qiita.rb
asqiitart = <<-QIITA
          ###############                                               #####
      ######################              #####         #####           #####
    #######           #########           #####         #####           #####
  ######                  #######                                   #################              #####       ####
 ######                     ######                                  #################         ################ ####
######                       ######       #####         #####           #####               #########   ###########
######                       ######       #####         #####           #####              ######            ######
######                       #####        #####         #####           #####             ######              #####
 ######                     ######        #####         #####           #####             ######              #####
  ######                   ######         #####         #####           #####             ######              #####
   #######              ########          #####         #####           #####              ######            ######
     ###########################          #####         #####            ############       #########   ###########
         ################  #######        #####         #####             ###########          #############   ####
                            ######
QIITA

start_code = "eval$s=%q(eval(%w("
main_code = "s=%(eval$s=%q(\#{$s}));puts(32.chr*10+s)"
end_code = ')*""))'

padding_length = asqiitart.split.join.length - (start_code + main_code + end_code).length
padding = "#" * padding_length

code = start_code + main_code + padding + end_code
code = asqiitart.gsub("#") { code.slice!(0, 1) }

puts code

これを実行すると、Quine が出力されます。

$ ruby qiita.rb
          eval$s=%q(eval(                                               %w(s=
      %(eval$s=%q(#{$s}));pu              ts(32         .chr*           10+s)
    #######           #########           #####         #####           #####
  ######                  #######                                   #################              #####       ####
 ######                     ######                                  #################         ################ ####
######                       ######       #####         #####           #####               #########   ###########
######                       ######       #####         #####           #####              ######            ######
######                       #####        #####         #####           #####             ######              #####
 ######                     ######        #####         #####           #####             ######              #####
  ######                   ######         #####         #####           #####             ######              #####
   #######              ########          #####         #####           #####              ######            ######
     ###########################          #####         #####            ############       #########   ###########
         ################  #######        #####         #####             ###########          #############   ####
                            )*""))

出力されたものを別ファイルに保存して、それを実行すると同じものが出力されます。

$ ruby qiita.rb > qiita_quine.rb
$ ruby qiita_quine.rb
          eval$s=%q(eval(                                               %w(s=
      %(eval$s=%q(#{$s}));pu              ts(32         .chr*           10+s)
    #######           #########           #####         #####           #####
  ######                  #######                                   #################              #####       ####
 ######                     ######                                  #################         ################ ####
######                       ######       #####         #####           #####               #########   ###########
######                       ######       #####         #####           #####              ######            ######
######                       #####        #####         #####           #####             ######              #####
 ######                     ######        #####         #####           #####             ######              #####
  ######                   ######         #####         #####           #####             ######              #####
   #######              ########          #####         #####           #####              ######            ######
     ###########################          #####         #####            ############       #########   ###########
         ################  #######        #####         #####             ###########          #############   ####
                            )*""))

とりあえずQuine にはなっていますが、ほとんど # になっています。 # が残っているということは他のプログラムに置き換えることができるということなので、色を付けたり動かしたり、まだまだ工夫の余地がありそうです。

おわりに

超絶技巧プログラミングは役に立たないことが多い技術ですが、楽しいです。

僕はまだ入門したばかりなので簡単なQuineくらいしか作れないですが、今後もっとテクニックを身に着け、様々なおもしろいプログラムを作ってみたいと思いました。

また、超絶技巧プログラミングを競う、IOCCC や TRICK といったコンテストもあるので、いつか参加してみたいです!

この記事で紹介した書籍「あなたの知らない超絶技巧プログラミングの世界」には、この記事よりも丁寧にQuineの作り方などが説明されているので、ぜひ書籍も読んでみて欲しいです!

6
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
1