ライフゲームを実装してみた！

# セル・オートマトンとは
格子状のセルと単純な規則による、離散的計算モデルである。離散的な時間で個々のセルの状態が変化する。その変化は、ある時刻 t においてのセルの状態、および近傍のセルの内部状態によって、次の時刻t+1 、すなわち新たな「ジェネレーション」（世代）での各セルの状態が決定される。

# ライフゲームとは
セル・オートマトンに従った数理モデルのこと。生物集団において過密でも過疎でも個体の生存に適さないという個体群生態学的な背景に持つ。セル・オートマトンの代表例としてライフゲームが挙げられます。

# ライフゲームのルール
 __誕生__ 
死んでいるセルに隣接する生きたセルがちょうど3つあれば、次の世代がちょうど3つあれば、次の世代が誕生する。
__灰色が生のセル__
__白色が死のセル__
__赤色は生成されたセル__

これが
![Screenshot from 2024-08-29 00-38-45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2153036/f5776b9b-e967-b032-bb8f-6be6e0ecd75f.png)
こうなる
![Screenshot from 2024-08-29 00-40-30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2153036/17f453b7-547b-37ab-3072-3d6e3a8cfd52.png)

__生存__
生きているセルに隣接する生きたセルが2つか3つならば、次の世代でも生存する。

これは
![Screenshot from 2024-08-29 00-52-13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2153036/48c88811-cd2a-1cfd-ff64-470ec92f271c.png)
変化なし

__過疎__
生きているセルに隣接する生きたセルが1つ以下ならば、過疎により死滅する。

これが
![Screenshot from 2024-08-29 01-04-24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2153036/27de4f4a-b568-254a-5b3c-21df30890e9e.png)
こうなる
![Screenshot from 2024-08-29 01-04-53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2153036/49cc9306-5f89-1ff5-81c1-f317d8eb5658.png)

__過密__
生きているセルに隣接する生きたセルが4つ以上ならば、過密により死滅する。

これが
![Screenshot from 2024-08-29 01-00-07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2153036/fb3ef3d5-5000-1cdd-4566-cbe19718d3e3.png)
こうなる
![Screenshot from 2024-08-29 01-02-55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2153036/14ef678d-f1f5-d999-2d52-2cb9dd7387ab.png)

# ライフゲームの実装
このセル配置で銀河が生成される
[p5.js](https://editor.p5js.org/)のサイトで初心者でも簡単にJavaScriptを試せますよ
```javascript
class Gen{
    constructor(){
      this.cells = [
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,1,1,0,1,1,1,1,1,1,0,0,0,
        0,0,0,0,1,1,0,1,1,1,1,1,1,0,0,0,
        0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,1,1,0,0,0,0,0,1,1,0,0,0,
        0,0,0,0,1,1,0,0,0,0,0,1,1,0,0,0,
        0,0,0,0,1,1,0,0,0,0,0,1,1,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,
        0,0,0,0,1,1,1,1,1,1,0,1,1,0,0,0,
        0,0,0,0,1,1,1,1,1,1,0,1,1,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
      ]
    }
    next(){
      let t = this;
      let n = new Gen();
      for(let i=0; i<t.cells.length; i++){
        let L = t.livesAround(...t.indexToXy(i));
        if (t.cells[i] === 0){
          //現世代で死んでいるセルは......
          //3個の生きてるセルに囲まれたら復活
          n.cells[i] = (L === 3) ? 1 : 0;
        }else{
          //現世代で生きているセルは......
          //2個か3個の生きてるセルに囲まれていれば生存、それ以外なら過疎か過密で死
          n.cells[i] = (L === 2 || L === 3) ? 1 : 0;
        }
      }
      return n;
    }
    // 周りの生きているセルの数
    livesAround(x,y){
      let L = 0;
      // 中心のセルまで数えている
      for(let a=x-1; a<=x+1; a++){
        for(let b=y-1; b<=y+1; b++){
          let i = this.xyToIndex(a,b);
          L += (i === -1) ? 0 : this.cells[i];
        }
      }
      // 中心のセルをひく
      L -= this.cells[this.xyToIndex(x,y)];
      return L;
    }
    xyToIndex(x,y){
      let w = sqrt(this.cells.length);
      if(x<0 || x>=w || y<0 || y>=w) return -1;
      return y*w + x;
    }
    indexToXy(i){
      let w = sqrt(this.cells.length);
      let x = i%w;
      let y = floor(i/w);
      return [x,y]
    }
  
    draw(){
      fill(0, 206, 209);
      stroke(255,255,255);
      for (let [i,c] of this.cells.entries()){
        let [x,y] = this.indexToXy(i);
        let w = height/sqrt(this.cells.length);
        if (c === 1){
          rect(w*x, w*y, w, w);  
        }
      }
    }
  }
  // Generationのインスタンスを生成
  let g = new Gen();
  
  
  function setup(){
    createCanvas(480, 480);
    background(0, 128, 255);
    g.draw();
  }
  function redrawAll(){
    background(0, 128, 255);
    g.draw();
  }
  function mousePressed(){
    g = g.next();
    redrawAll();
  }
  function draw(){
    if(frameCount%10 === 9){
      mousePressed();
    }
  }
```
![galaxy-lifegame.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2153036/59577a9d-1c31-2606-b534-4048ffb65d01.gif)