👾
Elixirでライフゲームを作ってみた
Elixirでライフゲームを作ったことがそういえばなかった気がしたので作ってみました。
作ったもの
lifame.exs
defmodule LifeGame do
@x_size 10
@y_size 10
def start() do
board = for y <- 1..@y_size, x <- 1..@x_size, into: %{}, do: {{x, y}, :rand.uniform(2) - 1}
tick(board)
end
def tick(board) do
board = update(board)
print(board)
:timer.sleep(1000)
tick(board)
end
defp update(board) do
(for y <- 1..@y_size, x <- 1..@x_size, do: {x, y})
|> Enum.map(fn pos ->
case {Map.get(board, pos), count_lives(board, pos)} do
{0, 3} ->
{pos, 1}
{1, lives} when lives <= 1 or lives >= 4 ->
{pos, 0}
{cell, _} = _other ->
{pos, cell}
end
end)
|> Map.new()
end
defp count_lives(board, {x, y}) do
for dx <- -1..1, dy <- -1..1, {dx, dy} != {0, 0} do
Map.get(board, {x + dx, y + dy}, 0)
end
|> Enum.sum()
end
defp print(board) when is_map(board) do
IO.puts(IO.ANSI.clear())
for y <- 1..@y_size do
(for x <- 1..@x_size, do: Map.get(board, {x, y}))
|> Enum.map(fn
0 -> "□"
1 -> "■"
end)
|> Enum.join("")
end
|> Enum.join("\n")
|> IO.puts()
end
end
LifeGame.start()
ライフゲームを実装する際はボードを二次元配列にする場合が多いと思いますが、Elixirの場合はMapのキーにタプルを指定できるので比較的シンプルにすることができます✨
(パフォーマンス的にはそこまでよくないはず?)
隣接8方向を見るところは以下のように書いています
for dx <- -1..1, dy <- -1..1, {dx, dy} != {0, 0} do
Map.get(board, {x + dx, y + dy}, 0)
end
Elixirのfor
は条件文を書くことができるのが地味に便利です。
またタプルを使うことで条件もわかりやすく書くことができました。
ボードを表示する関数 print/1
の最初に以下のようなコードを入れています
IO.puts(IO.ANSI.clear())
Elixirは意外にも?標準出力をおしゃれにするためのモジュールIO.ANSI
があり、標準出力に色をつけたりすることも可能です。
IO.ANSI.clear()
はCSIのErase in Display \e[2J
を返してくれます。
これをIO.puts()
で出力することによってコンソールをクリアすることができます。
おわり
今回はLiveBookを使ってコーディングしてみました。
さくっとElixirコードを書けるのでとても便利でした✨
また気が向いたら今度はLiveViewでGUIありで作成してみたいと思います 💪
Discussion