作ってみた:Pietコード埋め込みプログラム(1)

難解プログラミング言語Piet」の ソースコードを画像に埋め込んでみました。
長いので2部構成(予定)です。
今回は前半部分で、Pietソースコードの自動生成をしてみました。

ざっくりとPietの説明はしますが、基本的にはPietを知っている方に向けた記事です。
このブログ最初の記事にして、マニアック度は最高レベルですが、
興味があれば読んでいただき、雰囲気だけでも楽しんでいただけると嬉しいです。

Pietとは

Pietは、 David Morgan-Mar氏によって提案された、難解プログラミング言語です。

難解プログラミング言語では、とにかく読みにくくなるよう設計されたプログラミング言語です。
ふつうは考えないような方法でプログラミングをするので、
ある意味ではプログラミング言語の限界に挑戦して遊んでいるとも言えます。
当然ですが、実用性は求められていません。

その中でもPietは超異色です。 なんとソースコード画像です。 テキストですらありません。

画像といってもなんでもいいわけではありません。
色タイル("Codel"と呼ばれます)の配置と色の選び方で プログラムを記述します。
完成したPietソースコード画像は、たいてい抽象画のようになります。
(David Morgan-Mar氏のサイトにサンプルプログラムが掲載されています。)

何がしたいか

でも、せっかく画像をソースコードにできるなら、
任意の画像にPietコードを埋め込みたい
と思っていました。

これができると、
「この富士山の画像、実はPietコードになっていて、hello,worldができるんだ」
とか、
「このおいしそうなプリンの画像、実は素因数分解できる優れものなんだ」
とかいって、自慢できます(誰に)。1

この
任意の画像にPietコードを埋め込む
というのが最終目標です。

Pietサンプルプログラム

まずPietプログラムがどのようなものかを見てみます。
例えば下の画像は、10の階乗、つまり、10×9×…×2×1を計算するプログラムです。

f:id:y-mos:20210612231816p:plain
10の階乗を計算するPietソースコード

この画像が本当に10の階乗を計算するか確かめてみます。

Pietのインタプリタとして、Erik Schoenfelder氏が製作したnpietがあります。

npietを使うと、 Pietソースコードを実行できるだけではなく、
移動経路や、どこでどんな命令が実行されたかを可視化してくれます。

早速実行してみます。

f:id:y-mos:20210613184614p:plain
npietの実行結果

電卓をポチると、計算結果は3628800で正解でした。

下の画像が、移動経路と命令の実行結果を可視化した画像です。

f:id:y-mos:20210612231541p:plain
npietによる可視化結果

左上から始まる黒実線がPietの通った経路です。
また、経路のわきには、そこで実行された命令が、黒文字で書かれています。
(noopは命令が実行されていないことを表します。)
npietが左上から処理を開始し、いろいろ動き回ったのちに、
右のピンク色ブロックで処理が終了しています。

Pietの難しいところ

Pietの処理は以下を繰り返すことで実行されます。

  1. 画像上を、ある「ルール」で移動し、次の行先を決める
  2. 移動中に色が変化したら、色変化に応じて命令を決定し実行する

特に、行先を決める「ルール」は複雑で難解です。
噛み砕いていうと、

  • 進行方向(上下左右)
  • 右側通行か、左側通行か

という2つのパラメータで行先が決まります。
そして、特定の色に遭遇した時や、 画像からはみ出しそうになった時、
特定の命令が実行された時に、 これらのパラメータは切り替わっていきます。

さらに、Pietは本質的には「スタック志向プログラミング言語」です。
ざっくりいうとメモリの使い方に制約があるため、
プログラミングには多少慣れが必要で、しかも、若干面倒です。

したがって、

  • 慣れないスタック志向プログラミングでフローを考える
  • 移動パラメータの状態を常にトレース
  • 以上をまとめて、正しく色を配置

これらを同時に行わなければいけないところが、Pietプログラミングの難しいところです。

作ったもの ~Pietコードの自動生成プログラム~

そこで、Pietの複雑な移動ルールを気にせずプログラミングできるよう、
テキストファイルに記述した処理フローから、Pietソースコードを自動生成するプログラムを作りました。
それが今回作った"GridPietGenerator"です。

ざっくり使い方

まず、10の階乗を計算する処理を「処理フローファイル」にテキストで記述します。
こんな感じです。(コードの詳細な解説はこちら。 )

push1 push2 push5 mul
:loop
dup
push3 push1 roll
mul
push2 push1 roll
push1 sub
dup
if:loop:end
:end
pop outn end

基本的には、Pietの命令を順に記述していき、条件分岐は独自の命令とラベルで記述しています。
これを"fact.txt"などといったファイル名を付けて保存します。

次に、GridPietGeneratorを使って、このファイルを画像に変換します。

./GridPietGenerator fact.txt fact.ppm

その結果、先ほどの画像(fact.ppm)が生成されます。
(ブログ上では見やすさのために引き延ばしたpng画像を掲載しています。)

なお、この「.ppm」という画像フォーマットはあまり一般的ではなく、
例えばWindowsの標準ビューアでは見られません。

これは、GridPietGeneratorに特殊なライブラリを使っておらず、
自前で実装したためです。
申し訳ないですが、 特殊ライブラリの導入コストと天秤にかけた結果、
このような構成にしました。

なお、上述のnpietは「.ppm」ファイルに対応しています。

特徴

実は「テキストベースのPietコード→Pietソースコード画像」の変換の試みは、
先人により既に行われています。(例えばこちら

ですが、冒頭にあげたように、 任意の画像にPietコードを埋め込むという目標が最終目標であるため、 GridPietGeneratorには、以下のような特徴を持たせました。

出力画像が細長くなりにくい

これは、もっと規模の大きいプログラムだとよくわかります。
例えば次のコード。いわずと知れた"hello, world"です。

f:id:y-mos:20210613010627p:plain
hello,world

npietの出力結果がこちら(大きすぎて縮小されてもうた…)

f:id:y-mos:20210613011048p:plain
hello,worldのトレース画像

GridPietGeneratorでは、処理を条件分岐等で分割し、
画像の4辺に分散させて配置するため、 Pietソースコード画像は正方形に近くなります。
特に条件分岐を積極的に使うほど、 画像は正方形に近くなります。

画像中心にブロックがほとんどない

GridPietGeneratorが出力するPietソースコードは、
画像のフチに配置されたブロックで、メインの処理が実行されてます。
一方で、画像中央部は単に通路として使っており、命令は実行されていません。
したがって、画像中央にほとんどブロックがありません。

このような特徴を持つPietソースコード
任意の画像に埋め込みやすくなります

実は、Pietソースコードの白色領域のうち、一定の条件を満たす部分には、
元の処理内容を変えることなく自由にお絵描きができます。

したがって、Pietコードにピッタリ合うように、任意の画像を縮小して、
Pietコードのうち、白色領域で条件を満たす部分だけを書き換えれば、 任意の画像にPietコードを埋め込んだような画像を作ることができます。

次回予告

以上の方針で、Pietを任意の画像に埋め込むプログラムを作ります。 …が、長くなったので別記事で書きます。

では。


  1. 当然ですが、これらの画像のコードを実行するにはPietのインタプリタが必要です。