[Elm]Life Gameで生命を生み出す

## はじめに
この記事は作ってみた系の記事です

こういう風に作った、こういう風に拡張したい、なんか重い、＋1つTipsみたいな構成です

## 作ったもの
こちらに置いてあります
[アプリ](https://miyamoen.github.io/elm-life-game/)、[github](https://github.com/miyamoen/elm-life-game)

## これは何？
[ライフゲーム](https://ja.wikipedia.org/wiki/ライフゲーム)です
ゲームといってもマス目がどう変化していくかぼけっと眺めて楽しむだけです
昔流行ったとか何とか
2次元セルオートマトンとか呼ばれたりします

簡単に説明すると（元から簡単なのですが）

- 時間は離散化されている（tickという単位で進む）
- 各セルは生（明るい緑）と死（暗い緑）の状態を持つ
- セルは8近傍（周りの8個体）の状態を見て次のtickでの状態が決定する
  - 周囲に3マス生きているセルがいると誕生（生状態）
  - 生きているセルは周囲に2か3マス生きていると生き残る
  - それ以外は死ぬ

ほどよい密度があればよいが、過密や過疎状態では死んでしまう感じです

### 何が面白いか
眺めてると、固定的なものや周期的に繰り返すパターン（2回で繰り返すものから長周期のものまで）、同じものを生み出し続けるパターン（グライダーガン）ができたりします。自己複製的なふるまいは生物っぽいです
チューリング完全らしいのでなんでもできます

## module構成
Cells.elmとLifeGame.elmです。CellsのほうにState更新ロジックとセルのviewをいれていて、LifeGameのほうにはゲームのコントロールロジックとかを入れています

## データ構造

### [elm-community/graph](http://package.elm-lang.org/packages/elm-community/graph/latest)
グラフあるじゃんって思って使ってみました。ただ欲しいAPIなかったのと、できあがって動かしたら結構重いのでどうしようかなってところです

```.elm
type alias Size =
  Int

type State
  = Living
  | Dead

type alias Cells =
  { cells : Graph State ()
  , size : Size
  }
```

データ構造はこんな感じです。
主なデータはcellsですね。Graphそのままです。`Graph State ()`はグラフのノードに乗っている値としてState、グラフのエッジには何も値が乗ってないことを表しています

ちなみにインデントはスペース2派です。Elmのcoreのコードでも、4と2が同じファイルでも混ざったりしていてよくわからんですね

今回作ったライフゲームでは8近傍のセルの状態を参照しますが、場合によっては4近傍や6近傍、自分も含めて9近傍、1次元にしたら2近傍や3近傍も考えられます。
ということでcellsの作成時に影響を受ける範囲にエッジを張って、エッジが張られたセルの生存数を調べて自身の状態を更新するように実装しています

```
livingCount =
  alongIncomingEdges cell
    |> List.filterMap (\id -> Graph.get id cells)
    |> List.map (.node >> .label)
    |> List.filter ((==) Living)
    |> List.length
```

コードとしてはここら辺です。`alongIncomingEdges`で自分に入ってくるエッジの先のnodeIdを取ってきます
`filterMap`は型合わせをしていて、`List (Maybe a)`の`Maybe`をつぶしています

elm-community/graphは[elm-community/intdict](http://package.elm-lang.org/packages/elm-community/intdict/latest)を使って実装されているので`Graph.get`のオーダーは*O(32)*です。(たぶん)
遅いとしたらここか全体の状態の更新だとは思うんですが。

### Size

現状Sizeは正方形の一辺の長さですが、長方形とか1次元に対応してもいいかなって思ってモデルに含ませてます

### State
`State`はライフゲームの説明の通りにそのまま書いています
もし状態を増やす拡張をするなら、下に1行足しましょう

```
type State
  = Living
  | Dead
  | Dying
```

死にかけ（Dying）を足してみました。これでコンパイル通してエラーした部分を直していけば拡張完了だと思います。やってませんが
わたしはvscodeをつかっているので保存したら、errorを赤線でwarnを緑線で教えてくれるので便利です

```
rule : Int -> State -> State
rule count state =
  case (count, state) of
    (2, Living) ->
      Living

    (3, _) ->
      Living

    (_, _) ->
      Dead
```

周囲の生きているセルの数と現在のStateを受け取って新しいStateを返す関数です。
上で説明したほぼそのままです。
case文をunion typeにそのまま使うのはよくあるんですが、一緒にパターンマッチしたいものとタプルにするとネストが1段で済むので楽ですし、見やすいです

```
rule : Int -> State -> State
rule count state =
  case state of
    Living ->
      if count == 2 || count == 3 then
        Living
      else
        Dead

    Dead ->
      if count == 3 then
        Living
      else
        Dead
```

nestするとこんな感じでしょうか.
よりフローチャート的ですね。上のはクロステーブル？みたいなイメージです

`Maybe Int`とかも`Just 1`でマッチできるので場合によってはネストが減ります。


ていうかよくみたら`rule`関数、`_`(なんにでもマッチする)でマッチしているので`Dying`足してもエラー出しませんね。全遷移を明記したほうが安心安全ですね

### LifeGame

```
type alias Tick =
  Int


type alias LifeGame =
  { cells : Cells
  , tick : Tick
  , stopped : Bool
  }
```

ゲームの制御周りです。
進行を止めたり進めたりしたかったので変数を持たしています。あとは更新スピードを変える値が足りないです

```
type Msg
  = NoOp
  | NewGame Cells
  | Start
  | Stop
  | Reset
  | ToggleState Int
  | NextTick
```

`Msg`みれば大体、制御できることがわかる気がします

```
subscriptions : LifeGame -> Sub Msg
subscriptions game =
  if game.stopped then
    Sub.none
  else
    Time.every (Time.millisecond * 40) (always NextTick)
```

subscriptionsです。`Time.every (Time.millisecond * 40) (always NextTick)`が大事なところですね。一定の間隔でNextTickメッセージを送っています。

この実装だと、一定間隔で毎に、**描画**ではなく**次のStateの計算**が始まるんですよね。先に計算するように実装しておけばいいんでしょうね。やっていませんが

## おわりに
現在の状態から新しい状態を計算する。The Elm Architectureそのままなので素直に実装できます。

elm-community/graphはちょっと思ってたのと違いました。グラフDBのNeo4Jみたいに特定のグラフの条件にマッチする部分だけ更新する、みたいなことできたらよかったんですけどね。
グラフは好きなのでまた使ってみようと思います

今日はクリスマスイブでこの記事はElm Advent Calendarの24日分でした。
Life Gameで生命を生み出して記事を生み出しました。