画像提供:マイナビニュース

写真拡大

筆者は『ライフゲーム』が大好きです。ライフゲームというのは、生物集団の栄枯盛衰をシミュレーションする環境ゲームです。つい先日もPython版を作ったばかりですが、なでしこ3でも作ってみましょう。ライフゲームを作ると、グラフィックスやマウスの扱い方を学ぶことができます。

○ライフゲームのルール

ライフゲームというのは、以下のような画面の環境ゲームです。生物が生まれては死んでいく様子をシミュレーションします。ゲームという名前が付いていますが、基本的には、生物の生死を眺めるだけの環境プログラムです。

最初に簡単なライフゲームのルールを紹介しましょう。ライフゲームでは「生物集団は過疎でも過密でも生きていけない」という基本的なルールに従って刻々と世代が進んでいきます。

一つの世代ごとに、ステージ上の各セルを調べて、次の世代に生物が生存するかどうかを決定します。生物が生存するか否かは次のルールに応じて進みます。

- 1. 生物の周囲を調べて、生きている生物が3つあれば、次の世代に生物が誕生する

- 2. 生きている生物の周囲に、生きている生物が2つか3つであれば、生物は引き続き生存する

- 3. 生きている生物の周囲に、生きている生物が4つ以上あるなら、過密状態なので生物は死滅する

- 4. 生きているセルの周囲に、生きている生物が1つ以下しかなければ、過疎状態なので生物は死滅する

具体的には、ステージ上にあるセルの一つずつについて、周囲八方向を調べます。そして、いくつのセルが存在しているかに応じて、次の世代の生物の生死を決定します。

○プログラムを作ってみよう

普段、本連載では、なでしこ3簡易エディタを使いますが、今回は、グラフィックス用に、描画領域を広めにとっている「グラフィックス用のなでしこ3エディタ」を使ってみましょう。

少しプログラムが長いので、プログラムを少しずつ紹介します。プログラムをコピーして、グラフィックス用なでしこ3エディタに貼り付けたら、実行ボタンを押してみてください。ライフゲームが始まります。

lifegame(プログラム全体)

# 変数の初期化

列数=30。行数=25。

タイル幅=15

ステージ=[]

●ステージ初期化

ステージ=[]

(行数)回

A=[]

(列数)回、Aに(2の乱数)を配列追加。

ステージにAを配列追加。

ここまで。

ここまで。

●ステージ描画

W=タイル幅×(列数+1) # 画面サイズの計算 --- (*1)

H=タイル幅×(行数+1)

T2=INT(タイル幅÷2)

[0, 0, W, H]の描画クリア。# --- (*2)

1に線太さ設定。 # --- (*3)

黄色に線色設定。

赤色に塗色設定。

# 生物を描画 --- (*4)

Yを0から(行数-1)まで繰り返す

Xを0から(列数-1)まで繰り返す

V=ステージ[Y][X]

もし、V=0ならば、続ける。

XX=X×タイル幅+T2

YY=Y×タイル幅+T2

[XX, YY]へT2-1の円描画。#---(*5)

ここまで。

ここまで。

ここまで。

●セルカウント(XとYで)

CNT=0

Iを-1から1まで繰り返す

Jを-1から1まで繰り返す

XX = X + J

YY = Y + I

もし、(XX=X)かつ(YY=Y)ならば、続ける。

もし、(XX<0)または(YY<0)ならば、続ける。

もし、(XX≧列数)または(YY≧行数)ならば、続ける。

CNT = CNT + ステージ[YY][XX]

ここまで。

ここまで。

CNTで戻る。

ここまで

●世代交代

新ステージ=[]

Yを0から(行数-1)まで繰り返す

A=[]

Xを0から(列数-1)まで繰り返す

C=XとYで生死判定

AにCを配列追加。

ここまで。

新ステージにAを配列追加。

ここまで。

ステージ=新ステージ。

ステージ描画。

ここまで

●生死判定(XとYで)

CNT=XとYでセルカウント

V=ステージ[Y][X]

もし(CNT=3)ならば、1で戻る。

もし(V=1)かつ(2≦CNT)かつ(CNT≦3)ならば、1で戻る。

もし(V=1)かつ(CNT≦1)ならば、0で戻る。

もし(V=1)かつ(CNT≧4)ならば、0で戻る。

Vで戻る。

ここまで。

●連続再生

世代交代。

0.3秒後には

連続再生。

ここまで。

ここまで。

ステージ初期化。連続再生。

描画中キャンバスをマウス押した時には

XX=INT(マウスX÷タイル幅)

YY=INT(マウスY÷タイル幅)

もし、ステージ[YY][XX]=0ならば

ステージ[YY][XX]=1

ここまで。

ステージ描画。

ここまで。

○プログラムを少しずつ見ていこう

ライフゲームの冒頭では、プログラムの全体で利用する変数を初期化します。ここでは、以下の変数を利用します。

# 変数の初期化

列数=30。

行数=25。

タイル幅=15。

ステージ=[]。

このように、変数の初期化のために変数の一覧を、プログラムの冒頭に記述しておくなら、後から、容易に値を変更することができるようになります。変数『列数』と『行数』は、ステージのサイズを指定するものです。そして、『タイル幅』は、生物を表す円のピクセルサイズです。

そして、続く以下のプログラムは、ステージデータをランダムに初期化するプログラムです。

●ステージ初期化

ステージ=[]

(行数)回

A=[]

(列数)回、Aに(2の乱数)を配列追加。

ステージにAを配列追加。

ここまで。

ここまで。

「●」から始まる行は、関数の宣言です。関数というのは、プログラムを機能ごとに、一塊にまとめるものです。このように、ステージデータの初期化処理を関数にまとめておけば、後から必要に応じて呼びだすことができます。

そして、この初期化処理では、行数と列数のそれぞれについて、繰り返し配列に値を追加します。例えば、この初期化処理を実行すると、次のようなデータを生成します。

[

[1,1,0,1,0,0,0,0,1,1,0,1,1,0,0,0,1,0,0,0,1,0,0,1,0,1,0,1,0,1],

[1,1,1,0,1,0,0,0,0,1,1,0,1,0,0,0,1,0,0,1,0,0,0,1,1,1,1,0,1,0],

(省略)

[1,0,0,0,1,1,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,1,0,1,0,1]

]

このように、今回は、ステージデータを二次元配列で表現し、その各値を整数で管理します。そして、0が生物がいない状態、1が生物がいる状態を表します。

こうしたデータを画面に表示するのが、以下のプログラムです。

●ステージ描画

W=タイル幅×(列数+1) # 画面サイズの計算 --- (*1)

H=タイル幅×(行数+1)

T2=INT(タイル幅÷2)

[0, 0, W, H]の描画クリア。# --- (*2)

1に線太さ設定。 # --- (*3)

赤色に線色設定。

赤色に塗色設定。

# 生物を描画 --- (*4)

Yを0から(行数-1)まで繰り返す

Xを0から(列数-1)まで繰り返す

V=ステージ[Y][X]

もし、V=0ならば、続ける。

XX=X×タイル幅+T2

YY=Y×タイル幅+T2

[XX, YY]へT2-1の円描画。#---(*5)

ここまで。

ここまで。

ここまで。

プログラムの(*1)では、画面サイズを計算します。そして、(*2)の部分で画面の描画をクリアします。(*3)の部分では、生物を描画するために、線の太さ、線の色、塗色を設定します。(*4)以降の部分では、Y軸、X軸それぞれに、繰り返し文を利用して、ステージ上の生物の様子を描画します。(*5)では『円描画』命令を使って赤い円を描画します。

以下のプログラムは、グリッドの(X, Y)の周囲八方向に、生物(セル)がいくつあるのかを数え上げるプログラムです。

●セルカウント(XとYで)

CNT=0

Iを-1から1まで繰り返す

Jを-1から1まで繰り返す

XX = X + J

YY = Y + I

もし、(XX=X)かつ(YY=Y)ならば、続ける。

もし、(XX<0)または(YY<0)ならば、続ける。

もし、(XX≧列数)または(YY≧行数)ならば、続ける。

CNT = CNT + ステージ[YY][XX] # --- (*6)

ここまで。

ここまで。

CNTで戻る。

ここまで

やはり、『繰り返す』文をY軸、X軸のそれぞれに実行して、指定された座標(X, Y)の周囲にある生物の数を数えます。(*6)の部分ですが、生物がいないところが0、いるところが1という性質を利用して、変数『CNT』に該当するステージデータの値を足します。

そして、以下のプログラムが、ライフゲームの心臓部分となるルールを定義している部分です。

●世代交代

新ステージ=[]

Yを0から(行数-1)まで繰り返す

A=[]

Xを0から(列数-1)まで繰り返す

C=XとYで生死判定

AにCを配列追加。

ここまで。

新ステージにAを配列追加。

ここまで。

ステージ=新ステージ。

ステージ描画。

ここまで

●生死判定(XとYで)

CNT=XとYでセルカウント

V=ステージ[Y][X]

もし(CNT=3)ならば、1で戻る。

もし(V=1)かつ(2≦CNT)かつ(CNT≦3)ならば、1で戻る。

もし(V=1)かつ(CNT≦1)ならば、0で戻る。

もし(V=1)かつ(CNT≧4)ならば、0で戻る。

Vで戻る。

ここまで。

関数『世代交代』では、ステージ上の各セルごとに、関数『生死判定』を実行するように指定しています。そして、関数『生死判定』では、周囲にある生物の数を数えて、その数に応じて、次の世代の生死を決定します。つまり、このルールを変更すると、生物の栄枯盛衰のパターンが変化します。簡単にルールを変えることができるので、いろいろ変更してみると、よりライフゲームを楽しめます。

そして、この関数『世代交代』を定期的に実行するように指定するのが、以下のプログラムです。

●連続再生

世代交代。

0.3秒後には

連続再生。

ここまで。

ここまで。

ステージ初期化。連続再生。

なでしこの『(N)秒後』命令を使うことで、定期的に処理を実行できます。ここでは、0.3秒後に、関数『連続再生』を実行するように指定します。関数『連続再生』の中で、再帰的に『連続再生』を実行するように指定することで、繰り返し同じ関数が実行されることになります。

ここまでのプログラムでも、ライフゲームを楽しむことができます。しかし、以下のコードを追加することにより、マウスで任意の場所に生物を配置できます。

描画中キャンバスをマウス押した時には

XX=INT(マウスX÷タイル幅)

YY=INT(マウスY÷タイル幅)

もし、ステージ[YY][XX]=0ならば

ステージ[YY][XX]=1

ここまで。

ステージ描画。

ここまで。

「マウス押した時には...ここまで」を記述すると、マウスボタンを押した時の動作を指定できます。それで、ここでは、変数『ステージ』の指定座標に値1を代入し、これにより生物を配置します。

○まとめ

以上、なでしこでライフゲームを作ってみました。ライフゲームを作るためには、二次元配列変数や、繰り返し構文、タイマー、グラフィックス、マウス操作など、いろいろな要素が必要になります。自分でライフゲームを完成させるなら、ライフゲームを楽しむことができる上に、ここに挙げた基本的なプログラミング・テクニックを覚えることができるのです。皆さんも、本稿を参考にしつつ、ライフゲームを作ってみてください。

自由型プログラマー。くじらはんどにて、プログラミングの楽しさを伝える活動をしている。代表作に、日本語プログラミング言語「なでしこ」 、テキスト音楽「サクラ」など。2001年オンラインソフト大賞入賞、2005年IPAスーパークリエイター認定、2010年 OSS貢献者章受賞。技術書も多く執筆している。