日本語プログラミング言語「なでしこ」公式サイト

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