Skip to content

[投稿記事]brainf*ckをビジュアル化!

brainf*ckという言語を知っていますか?
命令がわずか8つ(><+-.,[])しかない難解なプログラミング言語です.
もともとはアラン・チューリングの定義した「チューリングマシン」にヒントを得ています。

言語仕様を簡単に説明すると,

	>< 現在さしているポインタをインクリメント/デクリメントする.
	+- ポインタがさす値をインクリメント/デクリメントする.
	. ポインタがさす値をアスキーで出力
	, 入力から1文字読み込み,ポインタがさす先に代入する.
	[ ポインタがさす値が0なら,対応する(先で一番近い)]にジャンプする.
	] ポインタがさす値が0でないなら,対応する(前で一番近い)[にジャンプする.

となります(ほとんどwikipediaの受け売りですw).ポインタとは,brainf*ck(の処理系)は30000個の要素を持つバイト型の配列があり、その要素を指すものです.

これをGUIに拡張させました.GUIに拡張するにあたって,「.」と「,」は外し(コンソールがないので),命令をいくつか追加しました.なお,

	ptr : 現在指しているポインタ
	> < : ポインタをシフト
	(x, y) : 座標x, yを表す
	src : ./image.bmpのこと

です.

	$
		(x, y)にマウス座標を格納する.
		ウィンドウから出ている場合は(x, y) = (0, 0)となる
		x : ptr
		y : ptr >
	#
		(x, y)にcolor(グレースケール)で点を描く
		x : ptr
		y : ptr >
		color : ptr >>
	@
		fl(マウス左), fr(マウス右)にマウスが押されているかを格納する
		(押されていたら1、そうでなければ0)
		fl : ptr
		fr : ptr >
	!
		messageをメッセージボックスで出す
		message : ptr>(ptr == 0 or ptr >= ProgramSizeまで)
	&
		画像(./image.bmp)を(x, y)に出す
		src(0, 0)が透過色として扱われる
		x : ptr
		y : ptr >
	%
		キャンバス(インタプリタのクライアント領域)を
		ビットマップ(./out.bmp)で保存する

以下のコード

+[>$>>>@[-<<<&>>>]>[-%]<<<<<] 

を実行したところ.

マウスの左を押しながらドラッグで画像を描画.右でファイルに保存.

簡単なお絵かきプログラム 左で鉛筆,右で消しゴム

+[>$>>>@[-<<<#>>>]>[-<<-<<#>>+>>]<<<<<]

線を書くだけなら +[>$#<] (わずか7文字!)でできます.

ここから技術的な話になります.
ちなみに開発言語はc++です.

まずbrainf*ckインタプリタについて.
brainf*ckの命令は文字なので,1文字ずつ読み込めば命令の解釈はできます.

><に関しては,ポインタ変数を足し引きすればよく,+-は配列の添え字をポインタ変数にして,足し引きすればできます.

//ソース(イメージ)
unsigned char data[30000]; //データ配列
vector<char> program; //プログラム本体
int pc = 0; //現在のプログラムの位置

	switch(program[pc]){
	case '>':
		ptr++;
		break;
	case '<':
		ptr--;
		break;
	case '+':
		data[ptr]++;
		break;
	case '-':
		data[ptr]--;
		break;

.,はGUI版にはないのですが,putchar(data[ptr])やgetchar()を使えばいいと思います.

問題は[]です.ここは悩みましたが,スタックを使うことにしました.
[のとき,ポインタがさす値が0なら一気に]に飛び,偽なら,スタックに現在のプログラムの位置を積みます.

]のとき,ポインタがさす値が0以外なら,現在のプログラムの位置にスタックから取り出したもの-1([の直前)を代入します.

stack<int> loopStack;

	case '[':
		if(!data[ptr])
		{
			while(program[pc] != ']') pc++;
		}
		else
		{
			loopStack.push(pc);
		}
		break;
	case ']':
		if(data[ptr])
		{
			pc = loopStack.top() - 1;
		}
		loopStack.pop();
		break;

これを組み終わった後,「GUIに拡張したら面白いんじゃね?」と思いつきました.ここからはWin32APIが絡みます.

$はGetCursorPos()という関数を使えば取得できます.

ウィンドウからはみ出したときはどうするかと悩みましたが,判定にも使えるので,(x, y) = (0, 0)としました.

@はGetAsyncKeyState()を使えば取得できます.
!はMessageBox()のlpTextに,条件を満たす間,代入された文字列を指定します.

	case '!':
	{
		string str(""); //ユニコード版は捨てたw
		int tempPtr = ptr;
		while(tempPtr < program.size() || data[tempPtr] != 0)
		{
			str += (char)data[tempPtr];
			tempPtr++;
		}
		MessageBox(hWnd, str.c_str(), TITLE, MB_OK);
		
		break;
	}

#と&と%については,いろいろあって(GetPixel(),SetPixel()の速度,ビットマップファイルのサポート)新たにBitmapというクラスを作って,それを編集しています(ここが一番詰まっていたりする).

//イメージ
class Bitmap
{
public:
	//ファイルに保存
	void WriteToBitmapFile(const char* filename);
	
	//srcから合成
	void Composition(Bitmap &src, int x, int y, unsigned long transColor);
	
	//データにアクセス
	unsigned long& operator()(int x, int y);
	
	//描画
	void Draw(HDC hdc, int x, int y, int w, int h);
};

#はアクセスするだけです
&はベースのBitmap(キャンバス)に画像(./image.bmp)を合成すればできます
%はWriteToBitmapFile("out.bmp")とすればできます.

#define GRAY(color) RGB(color, color, color)

Bitmap bmpData;
Bitmap image("image.bmp");

const int DATA_MAX = 30000;

//ラムダさん
auto acs = [=](int idx) -> int { return ((idx >= 0)? idx % DATA_MAX : DATA_MAX - (-idx % DATA_MAX)); };

	case '#':
		bmpData(data[ptr], data[acs(ptr + 1)]) = GRAY(data[acs(ptr + 2)]);
		break;
	case '&':
		bmpData.Composition(image, data[ptr] - image.width() / 2, data[acs(ptr + 1)] - image.height() / 2, image(0, 0));
		break;
	case '%':
		bmpData.WriteToBitmapFile("out.bmp");
		break;

さらっと言いましたが,ビットマップファイルの読み込み/書き込みで詰まりました.


ゆがんだ画像

これの原因はパディング(詰め物)というビットマップファイルの仕様です.横1行(幅)は必ず4バイト単位で記録されています.もし幅が4で割り切れない場合はパディングが入ります.なんででしょうね.きっと歴史的経緯だ.うん.

これで終わりです.brainfuckインタプリタはエラー処理などを無視すれば思ったよりあっさりできるので,ぜひ作ってみてください.

文・long_long_float
この記事はwise9に投稿されたものです
このエントリーをはてなブックマークに追加
はてなブックマーク - [投稿記事]brainf*ckをビジュアル化!
Post to Google Buzz
Share on GREE

Related posts:

  1. ダンジョンマップを生成するアルゴリズムの解説[投稿記事]

Facebook comments:

Post a Comment

Your email is never published nor shared. Required fields are marked *
*
*