Ruby でつくるアドベントカレンダー

こんにちは、@makicamel です。体力ない民ですが最近ランニングを始めました。ランニングアプリの履歴を眺めてにこにこしています。成長が見えると嬉しいものですね。

この記事は ANDPAD Advent Calendar 2024 1 日目の記事です。
@hsbt さんに「Ruby ネタで登録待ってます」とお声がけ頂いて、「Rubyアドベントカレンダーの記事... Rubyアドベントカレンダーをつくる記事にしよう」と思いたち Rubyアドベントカレンダーをつくりました。

適当なファイルに保存して実行したり irb で実行したりすると、日付が入った箱が流れてきて今日の箱が開封されます。ぜひ毎日実行してみてください。
と言いたいところですが怠惰で短気なわたしたちには毎日の実行は困難なので、実行時引数を渡して任意の日としても実行できます。最終日クリスマスには違うアニメーションが表示されます。

1 日に実行した様子

本記事ではこのアドベントカレンダーをステップバイステップに実装します。
動作確認はすべて macOSRuby 3.4.0-preview2 で行っています。

1. ひとつの箱を動かす

今回はアスキーアートをパラパラ漫画の要領で動かすアニメーションを実装します。
最初から全部考えると難しいので、まずはひとつの箱を所定の位置まで動かします。

ruby-advent-calendar-2024/1.rb at main · makicamel/ruby-advent-calendar-2024 · GitHub

13 行目の \x1b[2JANSI エスケープシーケンスで、画面クリアを意味します。 ANSI エスケープシーケンスはターミナルでカーソル(キャレット)位置や文字色を変えることができ、例えば身近なところで Rails の SQL のログの色付けに使われています。

19 〜 21 行目で用意したアスキーアートから動かしたい箱(24)だけを取り出して box 変数に詰めます。
23 〜 27 行目でカーソル位置を左上に毎回セットし、行頭にスペースを 1 文字ずつ追加しながら puts することで左から右に流れる動きを表現します。
これで「24」の箱が現れ、箱みっつ分のスペースを空けたところで停止するアニメーションができました。

2. 複数個の箱を動かす

ひとつの箱を動かせたので、次は複数の箱を動かします。
1 では箱を \n を使ったひとつの文字列で表しましたが、扱いやすいよう配列にします。また日付とパディング(スペーサー)セットで 1 要素とする配列にしました。

ruby-advent-calendar-2024/2.rb at main · makicamel/ruby-advent-calendar-2024 · GitHub

さらに日付ボックスまたはパディングボックスに日付や空白を詰めたり削ったりして、複数の箱が流れてくるアニメーションができました。

3. 複数列の箱を動かす

1 列の箱を動かせたので、次は複数列の箱を動かします。
全ての箱が動くのを待つのは退屈なので、1 列の箱を配列として持ち、スレッドを使って各列を並行に描画します。

ruby-advent-calendar-2024/3.rb at main · makicamel/ruby-advent-calendar-2024 · GitHub

かわいいですね。嬉しくなって 100 回くらい実行してにこにこ眺めました。
これで 25 個の箱が流れてくるアニメーションができました。

4. 効果を加える

アドベントカレンダーなので毎日箱を開けたいです。ということで日付変数を持って今日の箱を開いたり、今日以前の箱の中身を変えたり、日付を受け取れるようにしたりしました。
またカーソルがチラつくのが気になったので非表示にして、最後に元に戻すようにしました。

ruby-advent-calendar-2024/4.rb at main · makicamel/ruby-advent-calendar-2024 · GitHub

5. クリスマスプレゼントを用意する

最終日にわれわれ Rubyist が楽しみにしているプレゼントを表示するようにしました。

ruby-advent-calendar-2024/5.rb at main · makicamel/ruby-advent-calendar-2024 · GitHub

この Ruby は予め準備したアスキーアートではなく、100 x 36 の配列を準備して各座標が図形の内側か外側かを確認し、内側の時だけ文字を出すようにして描いています。
また始点と終点の Ruby の頂点を持ち、徐々に大きくしてアニメーションを動かしています。
判定式はほぼ ChatGPT 作です。数学の勉強をしたくなりました(N 年ぶり 2 度目)。

6. データを圧縮する

最後に、巨大なアスキーアートがそのままソースコードにあると見栄えがよくないので 36 進数表記にしてデータを圧縮しました。

ruby-advent-calendar-2024/6.rb at main · makicamel/ruby-advent-calendar-2024 · GitHub

これで Ruby でつくるアドベントカレンダーができました。


このように Ruby で遊ぶ作品やテクニックを紹介する、遠藤侑介さんによるあなたの知らない超絶技巧プログラミングの世界という本があります。
今回は「8-1-1 アニメーションの基本」「8-1-2 アスキーアートの生成(1):サブキャラクターレンダリング」「8-4-1 テーブルとしての巨大整数」などを参考にしました。

実際にコードを書いてみるとこの本や TRICK(超絶技巧 Ruby 意味不明コンテスト for RubyKaigi) のコードたちがまさしく超絶技巧であることをより実感します。腕を磨いてこんなコードが書けるようになりたいと思いました。

明日は @tomtwinkle さんによる Trivy DB に 優しい Github Actions を作成する話です。