2023年9月23日(土) 12:26 JST

ライフゲーム

  • 2009年5月 7日(木) 00:00 JST
  • 投稿者:

前回からずいぶん時間があいてしまいました。今回のテーマは実は2ヶ月以上前に終わらせていたのですが、記事にする時間がありませんでした。Prologシリーズはしばらくお休みです。次はまとまった時間が取れる夏休み期間後になるでしょうが、このままフェードアウトするかもしれません。

ペントミノの画面を見ていてなんとなくライフゲームを思い出したのでPrologで書いてみました。


(全部読むには 全文表示 をクリックしてください。)

結論からいうと、エレガントなプログラムは書けませんでした。ライフゲームのアルゴリズムをPrologで表現するわけですが、C言語なら普通に配列を用いて各セルの状態を記録するのが効率的でしょう。PrologにはSQLのUPDATEに相当する機能がないので、assert/retractを用いて cell(I,J,State) の上書きを実現します。ところが、この実装でライフゲームを実行すると retract の効率が徐々に下がっていき世代の更新にかかる時間が長くなっていきます。理由はわかりませんがある程度assert/retractを実行したらGCが機能するからかもしれません。理由はどうあれ、このままでは気持ちが悪いので、assert/retractの代わりにsetval/getvalを用いてセル状態の更新をできるだけ高速に処理されるようにしました。こうすることで、世代更新のインターバルが一定しました。更新速度は安定しましたが、プログラムは美的に?なものになってしまいました。

さて、ライフゲームの見た目と前回紹介したペントミノのそれが似ているというお話しに戻ります。同じように感じた人がいたようで、ペントミノをライフゲームに配置してどうなるかはもう調べられています。以下のコードでいろいろと確かめてみてください。

と、投げかけてみても、当ゼミの学生達はC言語習得に必死でPrologどころではないようですが(笑)。

有名パターンの動画/*ただし世界が狭すぎます*/
(コーデックはH264です。再生出来ない場合はffdshow-tryoutsをインストールしましょう。)
%
% LifeGame.pl
% HLAB(C) 2009
%
:- encoding(utf8).
:- new(@w, picture('ライフゲーム')), send(@w,open).

theWorld(50,30). % 世界の大きさを決める

%% お隣さんの位置
neighbor( 1, 0).
neighbor( 1, 1).
neighbor( 0, 1).
neighbor(-1, 1).
neighbor(-1, 0).
neighbor(-1,-1).
neighbor( 0,-1).
neighbor( 1,-1).

go :-
	initCells,
	initConf, 
	repeat,
	display,
	update,
	fail. % いつまでも終わりませんCtrl-Cで

initConf :-
%	placeActiveCell(10,10), % ■■■
%	placeActiveCell(10,11),
%	placeActiveCell(10,12),
%	placeActiveCell(2,2), % グライダー
%	placeActiveCell(3,3),
%	placeActiveCell(3,4),
%	placeActiveCell(2,4),
%	placeActiveCell(1,4),
%	placeActiveCell(20,21), % グライダー
%	placeActiveCell(21,21),
%	placeActiveCell(22,21),
%	placeActiveCell(20,22),
%	placeActiveCell(21,23),
%	placeActiveCell(16,10), % Die Hard
%	placeActiveCell(10,11),
%	placeActiveCell(11,11),
%	placeActiveCell(11,12),
%	placeActiveCell(15,12),
%	placeActiveCell(16,12),
%	placeActiveCell(17,12),
	placeActiveCell(13,12), % acom、ですが世界が狭すぎるようです
	placeActiveCell(15,13), % グリッドを十分大きくするか、上辺と下辺
	placeActiveCell(12,14), % 左辺と右辺を連続にするとか、工夫すると
	placeActiveCell(13,14), % いいかもしれません。
	placeActiveCell(16,14),
	placeActiveCell(17,14),
	placeActiveCell(18,14),
	%writeln(initialized),
	true.

initCells :-
	theWorld(WX,WY),
	MX is WX + 1,
	MY is WY + 1,
	between(0,MX,I),
	between(0,MY,J),
	X is 10*I,
	Y is 10*J,
	send(@w, display, new(Handle, box(10,10)), point(X,Y)),
	term_to_atom(p(I,J), Key),
	nb_setval(Key, cell(off,off,Handle)),
	fail.
initCells.

update :-
	theWorld(WX,WY),
	between(1,WX,I),
	between(1,WY,J),
	neighborsCount(I,J,Count), 
	changeState(I,J,Count),
	fail.
update.

changeState(I,J,2) :- 
	term_to_atom(p(I,J), Key),
	nb_getval(Key, cell(on,_,_)),
	!,
	updateCellStatus(I,J,on).
changeState(I,J,3) :- !,
	% cell(I,J,off), !,
	updateCellStatus(I,J,on).
changeState(I,J,_) :-
	updateCellStatus(I,J,off).

neighborsCount(I,J,Count) :-
	nb_setval(c,0),
	neighbor(X,Y),
	NX is I+X,
	NY is J+Y,
	term_to_atom(p(NX,NY),Key),
	nb_getval(Key, cell(on,_,_)),
	nb_getval(c,N),
	N1 is N + 1,
	nb_setval(c,N1),
	fail.
neighborsCount(I,J,Count) :-
	nb_getval(c,Count).

updateCellStatus(I,J,NewState) :-
	term_to_atom(p(I,J),Key),
	nb_getval(Key, cell(Curr,_,H)),
	nb_setval(Key, cell(Curr,NewState,H)).

placeActiveCell(I,J) :-
	term_to_atom(p(I,J),Key),
	nb_getval(Key, cell(_,_,H)),
	nb_setval(Key, cell(on,on,H)).

xGeneration :-
	theWorld(WX, WY),
	between(1,WX,I),
	between(1,WY,J),
	term_to_atom(p(I,J),Key),
	nb_getval(Key, cell(C,N,H)),
	nb_setval(Key, cell(N,off,H)),
	fail.
xGeneration.

display :-
	theWorld(WX,WY),
	between(1,WX,I),
	between(1,WY,J),
	term_to_atom(p(I,J), Key),
	nb_getval(Key, cell(Status,T,H)),
	( Status == on ->
		send(H, fill_pattern, colour(red))
	; send(H, fill_pattern, colour(white))
	),
	fail.
display :- 
	send(@w, flush),
	xGeneration.

以下のコメントは、その投稿者が所有するものでサイト管理者はコメントに関する責任を負いません。