目次へ 1ページ 2ページ 3ページ 4ページ


2.1 生命体クラス   1999/03/11(初版)  アイデアは前述のC言語の場合と同じです。生命体が棲息するフィールドを、たとえ ば40×40の正方形の碁盤の升目とすると、その2次元配列配を用意します。ただし、 各配列要素は生命体を表現するクラスのオブジェクトへのポインタです。生命体クラス は、フィールドの位置と状態(生か死か)の属性を持ちます。これらを privateなデー タメンバーとし、データの値にはpublicメンバー関数を介してアクセスします。
#include <iostream.h>
#include <time.h> 
#include "glibw32.h"// グラフィックス・クラスライブラリ GLIBW32 


const int N = 40; // 正方形の升目の数 

class life; // 前方宣言 
typedef life* field[N][N]; 

enum state{ DYING, LIVING, STATES }; // 状態 

// 生命体クラス 
class life{ 
public: 
    life( int r, int c, state s = DYING ) // コンストラクタ 
     { row = r; col = c; state_now = s; state_next = DYING; } 
    state getstate() const { return state_now; }  //状態識別 
    void next(field w); 
    void active() { state_now = LIVING; } 
    void update() { state_now = state_next; } 
private: 
    int row, col;          // 位置 
    state state_now;    // 現在の状態 
    state state_next;   // 次の世代 
    void count(field w, int sum[]) const; // 隣の状態を数える 
};
 40×40の2次元配列 field のそれぞれの要素は生命体クラス life のオブジェクトへ のポインタ変数となっています。 [注]2次元配列とポインタについての解説はこちら  また、生命体の状態をキーワード enum を使い、列挙子(名前の付いた整数定数の集 合) で宣言しています。この場合、各タグ名(識別子)はDYING(死)は0(ゼロ)、 LIVING(生) は1、STATE(状態の種類の数)は2となります。タグ名に STATE を含ませる ことで次の配列宣言 int sum[STATE]; // int sum[2]; と同じ に対して2つの要素 sum[DYIMG] (つまり sum[0])および sum[LIVING](つまり sum[1]) をそれぞれ死んでいる状態の数、生きている状態の数として扱えます。この様に文字定 数を使うことでプログラムの可読性が良くなります。  隣の状態を数える関数は
// 隣接した生命体の状態をカウント
void life::count(field w, int sum[]) const 
{ 
    sum[DYING] = sum[LIVING] = 0; 
    for( int i = -1; i <= 1; ++i ) 
        for( int j = -1; j <= 1; ++j ) 
            sum[ w[row + i][col +j]->getstate() ]++; 

    sum[ w[row][col]->getstate() ]--; // 自分の状態は数えない 
}
となり、この結果から次の世代での状態が決まります。
// 次の世代の状態を決定する
void life::next(field w) 
{ 
    int sum[STATES]; 
    count(w, sum);  // 隣の状態を数える 

    if(state_now == LIVING){ // 現在生きている 
       if(sum[LIVING] == 2 || sum[LIVING] == 3) 
           state_next = LIVING; 
       else 
           state_next = DYING; 
    } 
    else{                    // 現在死んでいる(あるいは空) 
        if(sum[LIVING] == 3) 
            state_next = LIVING; 
        else 
            state_next = DYING; 
    }
}
 life クラスができましたので、このオブジェクトを生成し、世代変化を追い、最後 にオブジェクトを削除するコードを書きます。オブジェクトの初期化は、
// オブジェクトの生成
void init(field w) 
{ 
    for(int i = 0; i < N; ++i) 
        for(int j = 0; j < N; ++j) 
            w[i][j] = new life(i,j);                       
}
コンストラクタ life::life() が呼ばれます。配列要素 w[i][j] は life オブジェクト へのポインタであることに注意しましょう。この初期化ではすべての状態は DYING とな っています。まずは確実に初期化を行います。  生命体の第1世代の状態は改めて設定します、ここでは前ページの図のような初期状 態の種(タネ)を位置 (i,j) を中心にして与えます。メンバー関数 life::active()で 状態を変えます。
// 生命体の初期設定
void create(field w, int i, int j) 
{ 
 // 生命体の生息パターン 
    w[i][j+1]->active(); 
    w[i-1][j]->active(); 
    w[i][j]->active(); 
    w[i+1][j]->active(); 
    w[i+1][j-1]->active(); 
}
 また世代の交代とその描画関数は、次のようになります。
// 世代交代
void update(field w) 
{ 
    int i,j; // 境界を避ける 
    for(i = 1; i < N-1; ++i) 
        for(j = 1; j < N-1; ++j) 
            w[i][j]->next(w);    // 次の世代を求める           

    for(i = 1; i < N-1; ++i) 
        for(j = 1; j < N-1; ++j) 
            w[i][j]->update();   // 世代交代 
} 
// 描画関数 
void draw(GRAPH *g, field w) 
{ 
    g->cls(); // 画面消去
    for(int i = 0; i < N; ++i) 
        for(int j = 0; j < N; ++j){ 
         if(w[i][j]->getstate() == LIVING) // 生きているなら描画する  
            g->frectangle(i*10+3,j*10+3,i*10+7,j*10+7, WHITE); 
    } 
    Sleep(100); // 遅延関数(100ミリ秒間停止する) 
}
 最後に、演算子new で動的に確保したオブジェクトのメモリ領域を 演算子delete で削除します。
// オブジェクトの削除
void dele(field w) 
 { 
     for(int i = 0; i < N; ++i) 
         for(int j = 0; j < N; ++j) 
          delete (w[i][j]); 
 }
 main() 関数は、次のようになります。
int main() 
{ 
    int no; 
    cout << "このライフゲームは、あらかじめ決まったパターンの配置(種)\n"

          << "から開始します。それぞれの種の中心位置は乱数で決定します。\n"

          << "スペースキーで描画を終了します。\n\n" 
          << "種の個数を入力してください:"; 
    cin >> no; 
    ginit( N*10, N*10 ); // グラフィックスの初期化 

    GRAPH g; // GRAPH オブジェクトの生成 
    field w; 
    init(w); // life オブジェクトの生成 

    time_t t; 
    srand((unsigned) time(&t)); //乱数の初期化 

    if(no < 1) no = 1; // 値チェック 
    while(no--){ 
        int i = rand() % (N-5) + 3; // 種の中心を乱数で決める 
        int j = rand() % (N-5) + 3; 
        create(w, i, j); // 生命体の設定 
    } 

    while( vkey()  != VK_SPACE ){ // スペースキーが押されるまで 
        draw(&g, w); // 描画関数 
        update(w);   // 世代交代 
    } 
    dele(w); // life オブジェクトの削除 

    gend(); // グラフィックスの終了 

    return 0; 
}
このプログラムでは、生命体の種の数を標準入力で与え、種の中心を乱数で決めていま す。種の設定をもっとうまく選ぶことで、豊富な世代変化が期待できます。

実行ファイルとソースファイルのダウンロードはこちらです、
ライフゲーム (1)
実行ファイルとソース・ファイル
Windows 95/ 98上で動作します。
  ダウンロード
  life01.lzh (47KB)

 * このソフトウェアは、SYMANTEC. NORTON AntiVirus 5.0 でウィルス検査を 
  行っています。 
 * ファイルは吉崎栄泰氏による LHA (Copyright(c)1988-92 H.Yoshizaki)を 
  使って圧縮しています。 


目次 前のページへ 次のページへ

Copyright(c) 1999 Yamada,K