よもやま話β版

よもやま話を書きます。内容はぺらぺら。自由に書く。

TRICK 2025 出しました記録

TRICK 2025 へ提出したソースコードが “Most Natural”『最も自然で賞』をいただきました。お祝いくださったみなさま、本当にありがとうございました!「あとでブログ書きます!」と言って回ったので、なんとか書きました。

また次の機会もぜひオモシロコードをぜひ書きたいなという気持ちはあるのですが、数年後ともなると自分の脳細胞は高確率で色んなことを失念している気がするので、未来の自分のためにも濃いスナップショットをとっておこう…と思ったらめちゃ長い記事になりました。ご笑覧いただけますと幸いです。

TRICK とは?

Transcendental Ruby Imbroglio Contest for rubyKaigi ( 超絶技巧 Ruby 意味不明コンテスト in RubyKaigi )*1のこと。2013年以降、数年に一度のペースで不定期に開催されており、2025年は第5回にあたる。

何を作った?

くるくる回転するお花を描画するRubyプログラムを作りました。

実行結果のスクリーンショット x 3枚
スクショですがこんな感じ

gifを投稿しようとしたら失敗した…ご興味ある方は、ぜひお手元で動かしてみてください!

https://github.com/betachelsea/trick2025/blob/main/04_entry.rb

(自分のは)むずかしくないぞ!

TRICKの作品群は ”超絶技巧”・”意味不明” の言葉通りに「わーっ、なんもわからん!w」となりがちですが、お花のコレはシンプルにそのままRubyなので、今回挙がったソースコードのなかで最もわかりやすいのではと思っております。花の形まで成形してしまったものを一見すると、一瞬「わーっ」となるかもしれませんが、成形前のコードは「読み慣れたやつ」という感想をいただけるのではと思っています。

変数名とかをわかりやすい名付けをしているコードはこちら(ただしそんなにリファクタリングはしていないです…お見逃しください!)

https://github.com/betachelsea/trick2025/blob/main/01_flower_description.rb

花っぽい線をつくる数式

花っぽい線は「2次ベジェ曲線」を使って作成しています。2次ベジェ曲線とは、3つの制御点を定めることで滑らかな曲線を描く計算手法です。この数式を使うと、次のような線を描くことができます。

2次ベジェ曲線の説明画像
絵を描くソフトのツールなどで覚えのある方もいるかなと

これを2本描くと花びら1枚っぽくなります。

2次ベジェ曲線で描かれた2本の線分
花びらっぽい気がしませんか?

この線を花びらN枚分だけ座標を回転させていくと、お花っぽくなります。

花びらをもした2本の2次ベジェ曲線を90度回転させた想定の図
例えば90度回転させるとこう。これをたくさん重ねる。

描画する

2次ベジェ曲線の数式で求めた計算結果を元に、xy平面に見立てた配列に文字を入れることで描画していきます。

描画は、遠藤さんの著作「あなたの知らない超絶技巧プログラミングの世界」(以下、”超絶技巧本”) の 8-1章(p.218)で説明されている「マーチングスクエア法」を読んで実装しました。この手法を読んで、自分はこれを「描画される予定の線をイイ感じの文字に置き換える」ものだと解釈し、次のように定義しました。この辺をどう定義・置換していくのかは、実装者の自由が効くところだなと感じました。

  • 図形のど真ん中 → 「*」
  • 線分の傾きが完全に垂直 → 「!」
  • y軸のマス内0〜30%の位置に点がある → 「`」
  • y軸のマス内80〜100%の位置に点がある → 「,」 ※
  • 線分の傾きの値が
    • 0.0〜1.0未満 → 「*」 (水平に近い)
    • 1.0以上〜2.0未満 → 「;」 (ナナメに近い)
    • 2.0以上〜 → 「l」 (垂直に近い)

※このあたり、判定処理をミスっており意図しない処理(バグ)になっていることに今気づきました…なんと…反省…。バグってなければ線がもっと滑らかに描画できてたかもです。他の箇所も、みれば見るほどまだ改善の余地があったのではないかと思ってしまう…。くーっ!

圧縮して難読化・花の形に成形

花の形へのコードの成形自体は、超絶技巧本 2-1章(p.108) を元にカスタムして作成した成形用コードを使いました。

https://github.com/betachelsea/trick2025/blob/main/03_asciiart.rb

ただし、成形すると全ての空白部分が失われるため、初期のコードを成形にかけるとあっさり壊れました。

例えば、こんな感じのコードがあるとして、

x = 0
if x.zero?
  y = 1
end
z = 2

これを単純に圧縮すると

x=0ifx.zero?y=1endz=2

となり、さっぱり動きません。なので、適宜 ; で区切ってあげる必要がありました。

x=0;if(x.zero?);y=1;end;z=2;

提出後は、末尾に普段つけない ; をつけてしまう後遺症が数日残りました。

また、そもそも文字数が多すぎてTRICKの応募要件(2048文字以内)を満たせていなかったため、提出直前はコードの短縮とバグが出ないようにする作業に追われました。結果、3145文字→1660文字への圧縮と、成形しても壊れないソースコードにすることに成功しました。ソースコードの劇的ビフォー・アフターは下記です。

イースターエッグ(隠し要素)

特に隠してはいないのですが、TRICK過去作を拝見してマネしたいこととして「意味ありげな文字列をソースコードに埋める」がありました。今回2箇所埋めました。 (埋めた結果上述のバグを仕込んでしまったので、提出ギリギリに仕様変更はやっぱり良くない。)

ソースコードに仕込んだ「TRICK2025」「RubyKaigi@Matsuyama!」の文字

よく聞かれたこと

Q: どうして作ろうと思ったのか

面白そうだと思ったからです! …の一言だけだとあんまりにもあっさりなので、挫折しなかった経緯を記録しておきます。サマリーとしては2回くらい心が折れてからの3度目の正直での提出でした。

  • 昨年春、RubyKaigi 2024 (那覇) でTRICK実施の告知を見て、一念発起して超絶技巧本を購入しました。しかし、内容の難しさに心が折れました(1回目)。

  • 冬、島根の RubyWorld Conference 2024 に参加しました。朝ご飯をご一緒させていただいた喫茶店で、 makicamel さんに「出しなよ!」と背中を押していただいたのを機に、改めて超絶技巧本を元にQuineの勉強を始めました。が、純粋なQuine(自身のソースコードと完全に同じ文字列を出力するもの)の難易度に圧倒されて心が折れました(2回目)。

  • 2025年明けの 東京Ruby会議12 、懇親会で makicamel さんに「Quineが難しくって…どうにもこうにもならず…」と無念敗退のご報告をしようとしたところ、「Quineじゃなくてもいいんだよ!」「出しなよ!」と再度激励をいただきました。「なんと!完全純粋なQuineじゃなくていいんだ!」という気付きがあり、トライする勇気に繋がりました。

  • 本格作成中にも心が折れそうになったので、週1ペースで参加させていただいているRails Girls More! 内の競プロ同好会オンラインもくもく会( thatblue_plus さん主催 )で進捗報告をすることで、完走までのモチベ維持のパワーとさせていただきました。ソースコード文字減量の作業の際には、競プロで得た知識が役立ちました。

この場を借りて改めて makicamel さん、thatblue_plus さん、勇気づけてくださった・チャレンジする機会をくださったみなさまに御礼を…。ありがとうございました…!

Q: どうやって思いついたのか

年明け一念発起時点ではまだノーアイデアだったこともあり、過去作品を全部リストアップして拝見するところから改めて始めました。8割くらいは逆立ちしても無理と思いましたが、見ていくなかで、アニメーションが入っている作品に強く惹かれるところがあるなぁという自分の興味を発見しました。

過去作一覧スプレッドシートのスクショ
過去作がどういうものかをチェック。ネタ被りが無いかも知りたかったという個人的な需要もあった。表記内容が完全に自分用です、すみません!🙇

アニメーションする作品のコードを見ても、難読化されている状態だとサッパリ分からなかったため、超絶技巧本に立ち返りました。超絶技巧本の 8-1章「端末でアニメーション」を手習いしているうちに、学生時代にProcessingで花の図形描画をして遊んでいたことを思い出し、提出作品に応用しようと決めました。

しかしながら、まず過去作を触ってみないと昔の思い出のところまで発案がいかなかったので、過去作ちゃんと拝見する、というのが大事だったんだなと改めて思います。

Q: 作成にどれくらいかかったのか

  • 2024年
    • 5月 超絶技巧本を購入 → 読んだけど凄すぎてそっ閉じ
    • 12月 島根でやる気再燃 → Quine難しすぎて再度そっ閉じ
  • 2025年
    • 1/18 東京Ruby会議12でいよいよ尻に火がついた状態の一念発起
    • 2/7 過去作リストアップしてチェックする資料が発生してた
    • 2/12 方針が決まって first commit してた
    • 2/24 実行結果がほぼ完成した祝日月曜深夜、文字量が大幅に規定オーバーしていることに気付き絶望。締切の2/28(金)まで4日間平日しかない&&仕事は普通にあるので「え〜あ〜もう無理〜終わった〜〜〜」と言いながらふて寝
    • 2/25 TRICKリポジトリのREADMEに「実質3/2まで待つ」という旨の更新が入り 、やる気再燃。〜28まで仕事以外の朝晩の時間を遅寝早起きして全投入した結果、大幅に文字数減量に成功。
    • 3/1 夕方まで最終調整、イースターエッグ(とバグ)を仕込んで完成、Submit完了

教科書にサインをもらった!

これまでの経緯を読んでくださった方には自明の通り、今回の取り組みには、遠藤さんの超絶技巧本が必須の教科書でした。RubyKaigi 2025 では本屋さんでサイン会が実施されており(持ち込みで恐縮でしたが)サインいただいてきました。

超絶技巧本に書いていただいたサイン
うおおおおやったああああああああ

なんとその場でQuineを書いていただきました。実行するとそのまま同じ出力が返ってくる、これぞ純粋なQuine。

最初、コメント行をスルーしてしまい、あれれ動かんなぁ…などと悩んでいましたが、よくよく読むと改行後お名前コメント表記の最後の「!」が重要でした。粋…!

eval s=%q!puts"eval s=%q"<<33<<s<<33
#BETAさんへ
#Yusuke Endoh!

# どうやって動いているのか考えてみよう…。

# eval → 文字列として与えられたRubyコードを実行
# %q!STRING! → シングルクォート文字列
#   ┗ 文字列として 「puts"eval s=%q...Endoh!」まで変数sに入る
# 33 → ASCIIコードの「!」を指す
# "文字列"<<"A" → 文字列に対する破壊的連結。この場合"文字列A" となる。
# ゆえに "eval s=%q"<<33<<s<<33 で次のようになる
#    ┗ eval s=%q! ここにsの展開 !
#    ┗ eval s=%q! puts"eval s=%q...Endoh! !
# 一番最後のお名前後の ! で、頭からお尻までのシングルクォート文字列が完成してるので動く

# 超絶技巧本 3-2章(p.121)「サイン入りQuine」参照

いやぁ…これを拝見すると、やっぱりまだまだ学びがあるな…としみじみ思います。正直、教科書たる超絶技巧本の1割ほどしか活用できていません。Quine心折れてたのですが、やっぱり真髄はそこにあるなと感じます。コレが完全無欠に動くという事実にシビれる!もっと読み倒していきたいです。

面白かった〜

会期前に書いていた締め文がありまして、素直な気持ちが現れてるなと思ったので、あえてそのまま掲載します。


無事に提出が間に合ってよかったです。普段仕事では絶対に書かないようなコードを書く楽しさがあるなと思いました。また、普段絶対に出会わないようなバグにぶつかる・それをリファクタするのも楽しかったです。苦労した分、他の作品の凄さもよりしみじみと体感するところでして、数年に1度のTRICKという特別なお祭りに提出できたことを今晴々とした気持ちで嬉しく思っています。(2025/03/08)


ありがとうございました! (2025/04/28)

*1:和訳は2015のREADME.ja.mdより。