日記20230809 ライフゲーム

 ChatGPTに教えて貰いながら、ライフゲームをC++で書いてみた。ChatGPTに教えてもらったものを、Windowsフォームで操作できるようにしている途中である。中身はこんな感じ。初心者なのでくどいほどコメントが書いてあるが、大目に見てほしい。





ライフゲームの中身

 ChatGPTが教えてくれたコードは、ほとんど丸ごとコピーして、自分用にコメントを足して使っている。

 これは関数や型の宣言の一部抜粋。

// セルの状態を表す型
typedef vector<vector<bool>> Grid;
//typeをdefineする
//C++における型エイリアス(type alias)
//std::vector<std::vector<bool>>という型名(2次元のブール値(真偽値)の行列を表現するためのデータ構造)をGridという名前で代用することを意味しています。
Grid grid;//gridを宣言

void displayGrid(const Grid& grid);// グリッドを表示
void initializeGrid(Grid& grid);// グリッドの初期化(ランダムな状態で初期化)
bool computeNextState(const Grid& grid, int x, int y);// セルの次の状態を計算
void updateGrid(Grid& grid);// グリッドを更新

 これは今回の主役、セルの生死を決めるプログラム。

bool computeNextState(const Grid& grid, int x, int y) {//gridの中の座標x,yについて
    int liveNeighbors = 0;//生きてる隣のセルのカウンター

    for (int i = -1; i <= 1; ++i) {//-1から1まで、つまり下から上まで
        for (int j = -1; j <= 1; ++j) {//-1から1まで、つまり左から右まで
            if (i == 0 && j == 0) continue;//xもyも0だと自分自身の居場所になっちゃうのでスキップ

            int nx = x + i;
            int ny = y + j;
            //座標(x,y)にi,jをそれぞれ足すと、上下左右を示せる。多分-1,-1なら左下、1,0なら右

            if (nx >= 0 && nx < rows && ny >= 0 && ny < cols) {//nx,nyでグリッド内の存在するセルを示すことができるなら
                if (grid[nx][ny]) {//grid[nx][ny]で指定した上下左右の某セルが1なら
                    liveNeighbors++;//生きてるセルがあるとみなし、1つカウント
                }
            }
        }
    }//forここまで

    if (grid[x][y]) {//生きたセルなら
        return liveNeighbors == 2 || liveNeighbors == 3;
        //周囲が2か3の時だけ生存でtrue,他は死亡でfalse
    }
    else {//死んだセルなら
        return liveNeighbors == 3;
        //周囲が3の時に生存でtrue
    }
}

 これは初期のセル配置を決めるもの。

void initializeGrid(Grid& grid) {
    grid.resize(rows, vector<bool>(cols, false));//各要素をfalseで初期化したベクター

    srand(time(nullptr));//時刻情報を乱数のシード(タネ)に設定
    for (int i = 0; i < rows; ++i) {//縦軸いっぱい
        for (int j = 0; j < cols; ++j) {//横軸いっぱい
            grid[i][j] = rand() % 2 == 1;
            //ランダムに生成した整数を2で割った余りを求めると、0か1が現れる。それが1かどうかを判定すると結果はboolになるので、結果を代入
        }
    }
}

 これは結果をコマンドプロンプトに表示するもの。

void displayGrid(const Grid& grid) {
    system("cls"); // 画面をクリア

    for (int i = 0; i < rows; ++i) {//縦軸いっぱい
        for (int j = 0; j < cols; ++j) {//横軸いっぱい
            cout << (grid[i][j] ? " * " : "  ");//1なら*,0なら空白
        }
        cout << endl;
    }
}

 これは計算結果をもとに、新しい配置をセルに反映するもの。

void updateGrid(Grid& grid) {
    Grid newGrid = grid;//今のグリッドをコピーして新グリッドを作成

    for (int i = 0; i < rows; ++i) {
        for (int j = 0; j < cols; ++j) {//縦横いっぱい
            newGrid[i][j] = computeNextState(grid, i, j);//さっきの判定に基づいて新グリッドを作成
        }
    }

    grid = newGrid; //上書き保存
}


 どれも全部、ChatGPTが書いてくれたものである。有難い。自分ではとてもじゃないけど思いつけない。

 ここからはChatGPTに助言を受けながらも、なるべく自力で改造を加えた。まず、コマンドプロンプトに表示する方法から、OpenCVを用いて表示する方法に変更した。そして、先述の通り、Windowsフォームで各種パラメータやスタート/ストップを操作できるようにしている。今はまだバグやエラーが多いので、それを直しているところである。



現在はこんな感じ。速度の「10ミリ秒」は誤り。今度直しておきます。


今回はこの形で終わった。ここからは同じパターンを繰り返すだけで、全体は変化しない。


「ループ」

 私がライフゲームを知ったきっかけは、鈴木光司氏の著書の「ループ」を読んだことだ。「ループ」は同氏の著作の「リング」「らせん」に続く、3作目である。リングと聞いて、髪の長い化け物の貞子を連想する方は多いだろう。まさしくそうで、鈴木光司氏の「リングシリーズ」は映画「リング」の原作である。


 原理は実に簡単であるが、実際に動かしてみると、様々なパターンが生じて、示唆に富む結果を産むことになる。(略) パターンどうしは互いに干渉し合い、まるで生き物のように碁盤目上で姿を変えていく。その変化は、すべてのセルが「死に絶える」か、パターンが固定して動かなくなるまで続くことになる。
 こういったライフゲームの概念を推し進めるうちに、研究者たちは、コンピューターの中に生物の世界を嗅ぎ始めるようになってしまった。

「ループ」より

 「ループ」の世界観は、雑に述べるなら、「コンピュータと生命とガン」である。SFの中でも飛びぬけて深いこの世界観に当時中学一年生の私を誘い込んだ小説の中に、導入として描かれたライフゲームは、私の脳内でずっと存在を主張してきた。いつか自分もそれを再現したいと思っていたが、いくら工学部と言えども、プログラミングを専攻していない自分がそれをすぐに習得することは叶わなかった。
 しかしそれがChatGPTによって容易に叶ってしまった。生命の在り方を、コンピュータとの関連性を、「ループすること」の恐ろしさを教えてくれたライフゲームは、私のPC上で再現された。それを手助けしたのも、紛れもなく、コンピュータが産んだ知性だった。

この記事が気に入ったらサポートをしてみませんか?