気になっていた以下な記述、Cell なプロセス単位で位置情報も保持、ということなのか。
# Start a generic server process for the Cell. The process
# is named according to the position with a Registry as index
def start_link(position) do
GenServer.start_link(__MODULE__, position, name: {
:via, Registry, {Cell.Registry, position}
})
end
これを使ってこんなことしています。
# Translate the Cell position {x, y} into the process
# ID if the Cell is alive (avoid ghost neighbours)
def lookup(position) do
Cell.Registry
|> Registry.lookup(position)
|> Enum.map(fn
{pid, _valid} -> pid
nil -> nil
end)
|> Enum.filter(&Process.alive?/1)
|> List.first
end
ある位置の Cell は生存状態かどうか、な判定ができるのかどうか。参考にしたのは以下です。
とりあえず
最上位なナニから掘削を試みてみます。Universe.Supervisor から。キモは以下の部分かと。
# Supervise the Universe object, the Cell supervisor and
# the Cell registry with one_for_one strategy: if a
# child process terminates, only that process is restarted
def init(_) do
children = [
worker(Universe, []),
supervisor(Cell.Supervisor, []),
supervisor(Registry, [:unique, Cell.Registry])
]
supervise(children, strategy: :one_for_one)
end
これってこーゆー意味になるのかな。
Universe.Supervisor --+-- Universe
|
+-- Cell.Supervisor --+-- Cell
|
+-- Registry --+-- Cell.Registry
なるほど。one_for_one な strategy というのは失敗したやつのみを再生成、なのかどうか。あるいはスルーしている以下の部分ですが
defmodule Universe.Supervisor do
use Supervisor
# Entry point for the application
def start(_type, _args) do
start_link()
end
# Start a supervisor for the Universe
def start_link do
Supervisor.start_link(__MODULE__, [], name: __MODULE__)
end
# Supervise the Universe object, the Cell supervisor and
# the Cell registry with one_for_one strategy: if a
# child process terminates, only that process is restarted
def init(_) do
- start がエントリポイント
- start_link は start から呼び出されている
- init は初期化終了時 (?) の callback
という理解で良いのかしら
次
Universe 確認してみます。基本的にゲームの進行を tick という i/f にて管理していることがわかります。以下、記述について確認してみる方向にて。
def handle_call({:tick}, _from, []) do
get_cells()
|> tick_each_process
|> wait_for_ticks
|> reduce_ticks
|> reap_and_sow
{:reply, :ok, []}
end
# Return all the Cell processes
defp get_cells do
Cell.Supervisor.children
end
# Asynchronously advance each Cell to next state
defp tick_each_process(processes) do
map(processes, &(Task.async(fn -> Cell.tick(&1) end)))
end
# Wait for the asynchronous ticks of the Cells
defp wait_for_ticks(processes) do
map(processes, &Task.await/1)
end
# Build lists for Cells to be reaped and sowed
defp reduce_ticks(ticks) do
reduce(ticks, {[], []}, &accumulate_ticks/2)
end
# Accumulator helper fn
defp accumulate_ticks({reap, sow}, {acc_reap, acc_sow}) do
{acc_reap ++ reap, acc_sow ++ sow}
end
# Reap and sow Cells according to the provided lists
defp reap_and_sow({to_reap, to_sow}) do
map(to_reap, &Cell.reap/1)
map(to_sow, &Cell.sow/1)
end
ざっくり、以下なカンジ?
- Cell.Supervisor が持ってる Cell を全て取得
- 非同期に Cell.tick 実行
- 非同期処理の終了待ち
- 生まれる、消える、な Cell の列挙
- 列挙されたものについて実際に生まれる、消えるを適用
reduce て何だろ、と思ったのですがおそらくは
- 第一引数なリストを順に
- 第三引数な手続きを適用
- 第二引数な初期値のソレを accumulator として第一引数なリストの car を渡すのか
書いてて自分でも分かりにくいw