日本語プログラミング言語「なでしこ」公式サイト |
筆者は『ライフゲーム』が大好きです。ライフゲームというのは、生物集団の栄枯盛衰をシミュレーションする環境ゲームです。つい先日も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貢献者章受賞。技術書も多く執筆している。