cursesでのコンソールゲームの作り方

http://panyara.hp.infoseek.co.jp/crapgame.html
の「終わらない夏休みロワイヤル」(以下「夏休み」)のソースをもとに解説する。ただそのゲーム自体は面白くはないという問題がある。
curses」というルーチン群(ライブラリ)を使うのが最大のポイントだ。
実際のコードを元に、解説が必要なところを解説する。
 *
Rogueの画面出力について
内部を知らなくても体感でわかってるという人もいるかもしれないが、Rogue(および全てのcursesのプログラム)は、画面表示のうちで変更が必要になった部分だけを再描画している。それがcursesの最要点だ。
cursesのルーチンで文字を描画すると、まずcurses裏画面にそれが描かれる。
refresh()関数でそれが実画面に表示される。
元祖Rogueではないものを例とするが、次のようなコードの書き方ができる。

void paint_game_map()
{
	int i, j;
	for(i=0;i<HGT;i++) {
		for(j=0;j<WID;j++) {
			// y == i, x == j の位置を描画する
			block_wo_kaku(i,j);
		}
	}
}
...
	while(1)
	{
		int cmd = getch();
		clear(); // erase() のほうがいいのかも
		paint_game_map();
		refresh();
	}

画面を全部消去して全部のマスを描画して表画面にコピー、という風に書かれている。
画面全部を書き直すかのように書かれている。
しかしrefresh()という重要な部分がそれを自動的に最適化して行ってくれる。
refresh()は、前回のrefresh()とは違った箇所だけを描画してくれる。
たとえば画面上の@が一歩右に動くとする。
コード上では「画面を全部消してマップを描画してから移動後の位置に@を置く」とやっても、cursesが、実画面の描画時には「前の@のところを変えて、右のところに@を書く、それだけ」としてくれる。もちろん、"HP:3/10"→"HP:2/10"と変わったら「2」のとこだけが描画される。
Rogueにこのルーチンが欠かせないことが伝わったら幸い。
 *
初期化と文字列出力
「夏休み」からとにかくやり方の要点だけ抜き出す。
色はマジックナンバー的に決まっている。

/*
 * character colors
 */
#define MAX_COLORS			16
#define C_BLACK				0
#define C_BLUE				1
#define C_GREEN				2
#define C_CYAN				3
#define C_RED				4
#define	C_MAGENTA			5
...

main()の最初にこのようにおまじないをする。

	int f;

	initscr();
	start_color();
	for (f = 0; f < MAX_COLORS; f++) {
		init_pair(f, f, 0);
	}
	clear();

色の指定と文字列出力。mvaddstr() などで、画面の任意の位置に出力される。

void set_color(int i)
{
	attrset(COLOR_PAIR(i));
}

...
	set_color(C_RED);
	mvprintw(ypos, xpos, "Mojiretsu");
	mvprintw(ypos+1, xpos, "文字列");
	refresh();

これで画面の好きな位置に赤い文字列を出力する方法がわかったのでもはやローグライクゲームが完成したも同然である。
 *
実際のゲームではどうか
AngbandWindows版は、見てわかるとおりcursesを使っていない*1
NetHackは、よくわからない。
よくわかるのは「終わらない夏休みロワイヤル」あたりだろう。
 *
日本語に対応しているかどうか
経験からの話だが、pdcursesをBCCにインストールしたものの場合、日本語に対応していて、mingwの場合デフォルトでは対応していなかった。
対応していないとどうなるかというと、たとえば「な」の上に「に」を書こうとすると、文字列の1バイト目が同じなので、2バイト目だけが画面に出力されることになる。結果、文字化けなどが起きる。なぜBCCだと大丈夫なのかは私は知らない。*2
 *
参考リンク:
cursesの基本的な関数
http://www.fireproject.jp/feature/c-language/curses/basic.html
Wikipedia
http://ja.wikipedia.org/wiki/Curses
PDCurses
http://pdcurses.sourceforge.net/

*1:追記:この言い方はある意味間違っている。ここでWindows版と言ったもの以外の各機種向けのコードでcursesライブラリを利用しているものもある。また、Angband独自の画面出力ルーチン(各機種全てのバージョンで使われている共通のコードで、それを介して実際の画面描画の低級ルーチンが使われる)をcursesの一種と見れば、cursesを使っているといえる。

*2:追記:cursesのオプションで、refresh()のとき、変更のあった“行を”まるごと書き換えるようにすることができる(? 未確認)