Elixir - Tic Tac Toe

8e961e9ffb92825bd46bc6aedcea2f96?s=47 Felipe Renan
January 01, 2019
16

Elixir - Tic Tac Toe

8e961e9ffb92825bd46bc6aedcea2f96?s=128

Felipe Renan

January 01, 2019
Tweet

Transcript

  1. Jogo da velha em Elixir

  2. Felipe Renan twitter.com/feeliperenan github.com/feliperenan

  3. https://careers.plataformatec.com.br

  4. A idéia de fazer esse jogo foi para aprender mais

    sobre a linguagem fazendo algo prático.
  5. Coisas que gostaria de explorar 4 Como criar criar processos.

    4 Como gerenciar processos.
  6. speakerdeck.com/ventsislaf/building-multiplayer-real-time-game-with-elixir-and-phoenix https://codepen.io/ziga-miklic/pen/Fagmh

  7. Processos em Elixir

  8. 4 É um processo da VM do Erlang e não

    do SO. 4 São extremamente leves. 4 Podemos criar milhares deles. 4 Não compartilham mémoria entre si.
  9. http://blog.plataformatec.com.br/2018/04/elixir-processes-and-this-thing-called-otp/

  10. Exemplo sem utilizar um processo sum = fn(x, y) ->

    # Simulando um processamento longo. Essa operação irá demorar 2s. :timer.sleep(2000) x + y end
  11. Exemplo sem utilizar um processo sum = fn(x, y) ->

    # Simulando um processamento longo. Essa operação irá demorar 2s. :timer.sleep(2000) x + y end sum.(2, 2) 4 # depois de 2 segundos.
  12. Exemplo sem utilizar um processo sum = fn(x, y) ->

    # Simulando um processamento longo. Essa operação irá demorar 2s. :timer.sleep(2000) x + y end Enum.map(1..3, &sum.(&1, 2)) [3, 4, 5] # depois de 6 segundos.
  13. Exemplo sem utilizar um processo Enum.map(1..3, &sum.(&1, 2)) # 6s

    até retornar todos os resultados. Enum.map(1..5, &sum.(&1, 2)) # 10s até retornar todos os resultados. Enum.map(1..10, &sum.(&1, 2)) # 20s até retornar todos os resultados.
  14. Kernel.spawn/1

  15. Exemplo utilizando um processo sum = fn(x, y) -> #

    Simulando um processamento longo. Essa operação irá demorar 2s. :timer.sleep(2000) x + y end # Função que irá executar a `sum/2` em um processo. sum_async = fn (x, y) -> spawn(fn -> IO.inspect sum.(x, y) end) end
  16. Exemplo utilizando um processo Enum.each(1..3, &sum_async.(&1, 2)) # 2s até

    retornar todos os resultados. Enum.each(1..5, &sum_async.(&1, 2)) # 2s até retornar todos os resultados. Enum.each(1..10, &sum_async.(&1, 2)) # 2s até retornar todos os resultados.
  17. GenServer

  18. GenServer 4 Um behaviour. 4 É um processo como qualquer

    outro em Elixir. 4 Pode ser usado para manter estado. https://hexdocs.pm/elixir/GenServer.html
  19. defmodule ListCache do use GenServer @impl true def init(list \\

    []) do {:ok, list} end @impl true def handle_call(:show_items, _from, state) do {:reply, state, state} end @impl true def handle_call({:add, item}, _from, state) do new_state = [item | state] {:reply, new_state, new_state} end @impl true def handle_call({:remove, item}, _from, state) do new_state = List.delete(state, item) {:reply, new_state, new_state} end end
  20. {:ok, pid} = GenServer.start_link(ListCache, [:hello, :world])

  21. defmodule ListCache do use GenServer @impl true def init(list \\

    []) do {:ok, list} end @impl true def handle_call(:show_items, _from, state) do {:reply, state, state} end @impl true def handle_call({:add, item}, _from, state) do new_state = [item | state] {:reply, new_state, new_state} end @impl true def handle_call({:remove, item}, _from, state) do new_state = List.delete(state, item) {:reply, new_state, new_state} end end
  22. {:ok, pid} = GenServer.start_link(ListCache, [:hello, :world]) GenServer.call(pid, :show_items)

  23. defmodule ListCache do use GenServer @impl true def init(list \\

    []) do {:ok, list} end @impl true def handle_call(:show_items, _from, state) do {:reply, state, state} end @impl true def handle_call({:add, item}, _from, state) do new_state = [item | state] {:reply, new_state, new_state} end @impl true def handle_call({:remove, item}, _from, state) do new_state = List.delete(state, item) {:reply, new_state, new_state} end end
  24. {:ok, pid} = GenServer.start_link(ListCache, [:hello, :world]) GenServer.call(pid, :show_items) [:hello, :world]

  25. {:ok, pid} = GenServer.start_link(ListCache, [:hello, :world]) GenServer.call(pid, :show) [:hello, :world]

    GenServer.call(pid, {:add, :felipe})
  26. defmodule ListCache do use GenServer @impl true def init(list \\

    []) do {:ok, list} end @impl true def handle_call(:show_items, _from, state) do {:reply, state, state} end @impl true def handle_call({:add, item}, _from, state) do new_state = [item | state] {:reply, new_state, new_state} end @impl true def handle_call({:remove, item}, _from, state) do new_state = List.delete(state, item) {:reply, new_state, new_state} end end
  27. {:ok, pid} = GenServer.start_link(ListCache, [:hello, :world]) GenServer.call(pid, :show) [:hello, :world]

    GenServer.call(pid, {:add, :felipe}) [:hello, :world, :felipe]
  28. {:ok, pid} = GenServer.start_link(ListCache, [:hello, :world]) GenServer.call(pid, :show) [:hello, :world]

    GenServer.call(pid, {:add, :felipe}) [:hello, :world, :felipe] GenServer.call(pid, {:remove, :felipe})
  29. defmodule ListCache do use GenServer @impl true def init(list \\

    []) do {:ok, list} end @impl true def handle_call(:show_items, _from, state) do {:reply, state, state} end @impl true def handle_call({:add, item}, _from, state) do new_state = [item | state] {:reply, new_state, new_state} end @impl true def handle_call({:remove, item}, _from, state) do new_state = List.delete(state, item) {:reply, new_state, new_state} end end
  30. {:ok, pid} = GenServer.start_link(ListCache, [:hello, :world]) GenServer.call(pid, :show) [:hello, :world]

    GenServer.call(pid, {:add, :felipe}) [:hello, :world, :felipe] GenServer.call(pid, {:remove, :felipe}) [:hello, :world]
  31. defmodule ListCache do use GenServer def start_link(default) do GenServer.start_link(__MODULE__, default)

    end def add(pid, item) do GenServer.call(pid, {:add, item}) end def remove(pid, position) do GenServer.call(pid, {:remove, position}) end def show_items(pid) do GenServer.call(pid, :show_items) end ... end
  32. {:ok, pid} = ListCache.start_link([:hello, :world]) ListCache.show_items(pid) [:hello, :world] ListCache.add(pid, :felipe)

    [:hello, :world, :felipe] ListCache.remove(pid, :felipe) [:hello, :world]
  33. GenServer summary 4 É um processo como qualquer outro em

    Elixir. 4 Usado para manter estados e processar mensagens.
  34. Fluxo do jogo

  35. None
  36. None
  37. None
  38. None
  39. None
  40. tictactoe 4 apps 4 game_engine 4 game_ui

  41. game_engine 4 application.ex 4 board.ex 4 game.ex 4 game_server.ex 4

    game_supervisor.ex
  42. game_engine 4 ... 4 board.ex 4 game.ex 4 ...

  43. defmodule GameEngine.Board do @moduledoc """ Struct and functions to control

    a game board. """ defstruct positions: [ nil, nil, nil, nil, nil, nil, nil, nil, nil ] ... end
  44. defmodule GameEngine.Game do @moduledoc """ Struct and functions to control

    a game state. """ alias GameEngine.Board defstruct board: %Board{}, x: nil, o: nil, next: :x, first: :x, finished: false, winner: nil ... end
  45. %GameEngine.Game{ board: %GameEngine.Board{ positions: [ nil, nil, nil, nil, nil,

    nil, nil, nil, nil ] }, finished: false, first: :x, next: :x, o: nil, x: nil, winner: nil }
  46. game_engine 4 ... 4 board.ex 4 game.ex 4 ... 4

    game_server.ex 4 ...
  47. defmodule GameEngine.GameServer do @moduledoc """ Create a process for a

    game with two players. """ use GenServer def start_link(name) do GenServer.start_link(__MODULE__, nil, name: via_tuple(name)) end def join_player(game, player) do GenServer.call(via_tuple(game), {:join_player, player}) end def put_player_symbol(game, symbol, pos) do GenServer.call(via_tuple(game), {:put_player_symbol, symbol, pos}) end def new_round(game) do GenServer.call(via_tuple(game), :new_round) end def leave(game, symbol) do GenServer.cast(via_tuple(game), {:leave, symbol}) end defp via_tuple(name) do {:via, GameRegistry, name} end ... end
  48. alias GameEngine.{Game, GameServer, Board} GameServer.start_link("my-game")

  49. defmodule GameEngine.GameServer do ... def start_link(name) do GenServer.start_link(__MODULE__, nil, name:

    via_tuple(name)) end ... @impl true def init(_) do {:ok, %Game{}} end ... end
  50. defmodule GameEngine.GameServer do ... def start_link(name) do GenServer.start_link(__MODULE__, nil, name:

    via_tuple(name)) end ... @impl true def init(_) do {:ok, %Game{}} end ... end
  51. alias GameEngine.{Game, GameServer, Board} GameServer.start_link("my-game") %GameEngine.Game{ board: %GameEngine.Board{ positions: [nil,

    nil, nil, nil, nil, nil, nil, nil, nil] }, finished: false, first: :x, next: :x, o: nil, x: nil, winner: nil }
  52. {:ok, player_x, game} = GameServer.join_player("my-game", "Felipe")

  53. defmodule GameEngine.GameServer do ... def join_player(game, player) do GenServer.call(via_tuple(game), {:join_player,

    player}) end ... @impl true def handle_call({:join_player, player}, _from, %{x: nil} = state) do new_state = %{state | x: player} {:reply, {:ok, :x, new_state}, new_state} end ... end
  54. defmodule GameEngine.GameServer do ... def join_player(game, player) do GenServer.call(via_tuple(game), {:join_player,

    player}) end ... @impl true def handle_call({:join_player, player}, _from, %{x: nil} = state) do new_state = %{state | x: player} {:reply, {:ok, :x, new_state}, new_state} end ... end
  55. {:ok, player_x, game} = GameServer.join_player("my-game", "Felipe") %GameEngine.Game{ board: %GameEngine.Board{ positions:

    [nil, nil, nil, nil, nil, nil, nil, nil, nil] }, finished: false, first: :x, next: :x, o: nil, x: "Felipe", winner: nil }
  56. {:ok, player_o, game} = GameServer.join_player("my-game", "Renan")

  57. defmodule GameEngine.GameServer do ... def join_player(game, player) do GenServer.call(via_tuple(game), {:join_player,

    player}) end ... @impl true def handle_call({:join_player, player}, _from, %{o: nil} = state) do new_state = %{state | o: player} {:reply, {:ok, :o, new_state}, new_state} end ... end
  58. {:ok, player_o, game} = GameServer.join_player("my-game", "Renan") %GameEngine.Game{ board: %GameEngine.Board{ positions:

    [nil, nil, nil, nil, nil, nil, nil, nil, nil] }, finished: false, first: :x, next: :x, o: "Renan", x: "Felipe", winner: nil }
  59. None
  60. GameServer.put_player_symbol("my-game", player_x, 1)

  61. defmodule GameEngine.GameServer do ... def put_player_symbol(game, symbol, pos) do GenServer.call(via_tuple(game),

    {:put_player_symbol, symbol, pos}) end ... @impl true def handle_call({:put_player_symbol, _symbol, _pos}, _from, %Game{finished: true} = state) do {:reply, :finished, state} end @impl true def handle_call({:put_player_symbol, symbol, position}, _from, %Game{next: symbol} = state) do ... end ... end
  62. defmodule GameEngine.GameServer do ... def put_player_symbol(game, symbol, pos) do GenServer.call(via_tuple(game),

    {:put_player_symbol, symbol, pos}) end ... @impl true def handle_call({:put_player_symbol, _symbol, _pos}, _from, %Game{finished: true} = state) do {:reply, :finished, state} end @impl true def handle_call({:put_player_symbol, symbol, position}, _from, %Game{next: symbol} = state) do ... end ... end
  63. defmodule GameEngine.GameServer do ... def put_player_symbol(game, symbol, pos) do GenServer.call(via_tuple(game),

    {:put_player_symbol, symbol, pos}) end ... @impl true def handle_call({:put_player_symbol, _symbol, _pos}, _from, %Game{finished: true} = state) do {:reply, :finished, state} end @impl true def handle_call({:put_player_symbol, symbol, position}, _from, %Game{next: symbol} = state) do ... end ... end
  64. defmodule GameEngine.GameServer do ... def handle_call({:put_player_symbol, symbol, position}, _from, %Game{next:

    symbol} = state) do case Board.put(state.board, position, symbol) do {:error, error_message} -> {:reply, {:retry, error_message}, state} board -> state = %{state | board: board} cond do winner = Board.winner(board) -> new_state = Game.finish(state, winner) {:reply, {:winner, new_state}, new_state} Board.full?(board) -> new_state = Game.finish(state, :draw) {:reply, {:draw, new_state}, new_state} true -> new_state = Game.next_turn(state) {:reply, {:ok, new_state}, new_state} end end end ... end
  65. defmodule GameEngine.Board do @symbols [:x, :o] ... def put(board, pos,

    symbol) when symbol in @symbols and pos >= 0 and pos <= 8 do case Enum.at(board.positions, pos) do nil -> new_positions = List.replace_at(board.positions, pos, symbol) %__MODULE__{board | positions: new_positions} existent_symbol -> {:error, "The position: #{pos} already has the symbol: #{existent_symbol}"} end end ... end
  66. defmodule GameEngine.Board do @symbols [:x, :o] ... def put(board, pos,

    symbol) when symbol in @symbols and pos >= 0 and pos <= 8 do case Enum.at(board.positions, pos) do nil -> new_positions = List.replace_at(board.positions, pos, symbol) %__MODULE__{board | positions: new_positions} existent_symbol -> {:error, "The position: #{pos} already has the symbol: #{existent_symbol}"} end end ... end
  67. defmodule GameEngine.Board do @symbols [:x, :o] ... def put(board, pos,

    symbol) when symbol in @symbols and pos >= 0 and pos <= 8 do case Enum.at(board.positions, pos) do nil -> new_positions = List.replace_at(board.positions, pos, symbol) %__MODULE__{board | positions: new_positions} existent_symbol -> {:error, "The position: #{pos} already has the symbol: #{existent_symbol}"} end end ... end
  68. defmodule GameEngine.GameServer do ... def handle_call({:put_player_symbol, symbol, position}, _from, %Game{next:

    symbol} = state) do case Board.put(state.board, position, symbol) do {:error, error_message} -> {:reply, {:retry, error_message}, state} board -> state = %{state | board: board} cond do winner = Board.winner(board) -> new_state = Game.finish(state, winner) {:reply, {:winner, new_state}, new_state} Board.full?(board) -> new_state = Game.finish(state, :draw) {:reply, {:draw, new_state}, new_state} true -> new_state = Game.next_turn(state) {:reply, {:ok, new_state}, new_state} end end end ... end
  69. defmodule GameEngine.GameServer do ... def handle_call({:put_player_symbol, symbol, position}, _from, %Game{next:

    symbol} = state) do case Board.put(state.board, position, symbol) do {:error, error_message} -> {:reply, {:retry, error_message}, state} board -> state = %{state | board: board} cond do winner = Board.winner(board) -> new_state = Game.finish(state, winner) {:reply, {:winner, new_state}, new_state} Board.full?(board) -> new_state = Game.finish(state, :draw) {:reply, {:draw, new_state}, new_state} true -> new_state = Game.next_turn(state) {:reply, {:ok, new_state}, new_state} end end end ... end
  70. defmodule GameEngine.Board do ... def winner(%__MODULE__{positions: positions}) do do_winner(positions) end

    ... end
  71. defmodule GameEngine.Board do ... defp do_winner([ s, s, s, _,

    _, _, _, _, _ ]) when s in @symbols, do: s ... defp do_winner([ s, _, _, s, _, _, s, _, _ ]) when s in @symbols, do: s ... defp do_winner([ _, _, s, _, _, s, _, _, s ]) when s in @symbols, do: s defp do_winner([ s, _, _, _, s, _, _, _, s ]) when s in @symbols, do: s ... defp do_winner(_), do: nil end
  72. defmodule GameEngine.Board do ... defp do_winner([ s, s, s, _,

    _, _, _, _, _ ]) when s in @symbols, do: s ... defp do_winner([ s, _, _, s, _, _, s, _, _ ]) when s in @symbols, do: s ... defp do_winner([ _, _, s, _, _, s, _, _, s ]) when s in @symbols, do: s defp do_winner([ s, _, _, _, s, _, _, _, s ]) when s in @symbols, do: s ... defp do_winner(_), do: nil end
  73. defmodule GameEngine.GameServer do ... def handle_call({:put_player_symbol, symbol, position}, _from, %Game{next:

    symbol} = state) do case Board.put(state.board, position, symbol) do {:error, error_message} -> {:reply, {:retry, error_message}, state} board -> state = %{state | board: board} cond do winner = Board.winner(board) -> new_state = Game.finish(state, winner) {:reply, {:winner, new_state}, new_state} Board.full?(board) -> new_state = Game.finish(state, :draw) {:reply, {:draw, new_state}, new_state} true -> new_state = Game.next_turn(state) {:reply, {:ok, new_state}, new_state} end end end ... end
  74. defmodule GameEngine.GameServer do ... def handle_call({:put_player_symbol, symbol, position}, _from, %Game{next:

    symbol} = state) do case Board.put(state.board, position, symbol) do {:error, error_message} -> {:reply, {:retry, error_message}, state} board -> state = %{state | board: board} cond do winner = Board.winner(board) -> new_state = Game.finish(state, winner) {:reply, {:winner, new_state}, new_state} Board.full?(board) -> new_state = Game.finish(state, :draw) {:reply, {:draw, new_state}, new_state} true -> new_state = Game.next_turn(state) {:reply, {:ok, new_state}, new_state} end end end ... end
  75. defmodule GameEngine.GameServer do ... def handle_call({:put_player_symbol, symbol, position}, _from, %Game{next:

    symbol} = state) do case Board.put(state.board, position, symbol) do {:error, error_message} -> {:reply, {:retry, error_message}, state} board -> state = %{state | board: board} cond do winner = Board.winner(board) -> new_state = Game.finish(state, winner) {:reply, {:winner, new_state}, new_state} Board.full?(board) -> new_state = Game.finish(state, :draw) {:reply, {:draw, new_state}, new_state} true -> new_state = Game.next_turn(state) {:reply, {:ok, new_state}, new_state} end end end ... end
  76. defmodule GameEngine.Game do ... def next_turn(%__MODULE__{next: :x} = game), do:

    %__MODULE__{game | next: :o} def next_turn(%__MODULE__{next: :o} = game), do: %__MODULE__{game | next: :x} ... end
  77. defmodule GameEngine.Game do ... def next_turn(%__MODULE__{next: :x} = game), do:

    %__MODULE__{game | next: :o} def next_turn(%__MODULE__{next: :o} = game), do: %__MODULE__{game | next: :x} ... end
  78. GameServer.put_player_symbol("my-game", player_x, 1) %GameEngine.Game{ board: %GameEngine.Board{ positions: [nil, :x, nil,

    nil, nil, nil, nil, nil, nil] }, finished: false, first: :x, next: :o, o: "Renan", x: "Felipe", winner: nil }
  79. None
  80. GameServer.put_player_symbol("my-game", player_o, 8)

  81. GameServer.put_player_symbol("my-game", player_o, 8) %GameEngine.Game{ board: %GameEngine.Board{ positions: [nil, :x, nil,

    nil, nil, nil, nil, nil, :o] }, finished: false, first: :x, next: :x, o: "Renan", x: "Felipe", winner: nil }
  82. None
  83. GameServer.put_player_symbol("my-game", player_x, 0)

  84. GameServer.put_player_symbol("my-game", player_x, 0) %GameEngine.Game{ board: %GameEngine.Board{ positions: [:x, :x, nil,

    nil, nil, nil, nil, nil, :o] }, finished: false, first: :x, next: :o, o: "Renan", x: "Felipe", winner: nil }
  85. None
  86. GameServer.put_player_symbol("my-game", player_o, 7)

  87. GameServer.put_player_symbol("my-game", player_o, 7) %GameEngine.Game{ board: %GameEngine.Board{ positions: [:x, :x, nil,

    nil, nil, nil, nil, :o, :o] }, finished: false, first: :x, next: :x, o: "Renan", x: "Felipe", winner: nil }
  88. None
  89. GameServer.put_player_symbol("my-game", player_x, 2)

  90. defmodule GameEngine.GameServer do ... def handle_call({:put_player_symbol, symbol, position}, _from, %Game{next:

    symbol} = state) do case Board.put(state.board, position, symbol) do {:error, error_message} -> {:reply, {:retry, error_message}, state} board -> state = %{state | board: board} cond do winner = Board.winner(board) -> new_state = Game.finish(state, winner) {:reply, {:winner, new_state}, new_state} Board.full?(board) -> new_state = Game.finish(state, :draw) {:reply, {:draw, new_state}, new_state} true -> new_state = Game.next_turn(state) {:reply, {:ok, new_state}, new_state} end end end ... end
  91. defmodule GameEngine.Board do ... defp do_winner([ s, s, s, _,

    _, _, _, _, _ ]) when s in @symbols, do: s ... end
  92. defmodule GameEngine.GameServer do ... def handle_call({:put_player_symbol, symbol, position}, _from, %Game{next:

    symbol} = state) do case Board.put(state.board, position, symbol) do {:error, error_message} -> {:reply, {:retry, error_message}, state} board -> state = %{state | board: board} cond do winner = Board.winner(board) -> new_state = Game.finish(state, winner) {:reply, {:winner, new_state}, new_state} Board.full?(board) -> new_state = Game.finish(state, :draw) {:reply, {:draw, new_state}, new_state} true -> new_state = Game.next_turn(state) {:reply, {:ok, new_state}, new_state} end end end ... end
  93. defmodule GameEngine.Game do ... def finish(game, winner), do: %__MODULE__{game |

    finished: true, winner: winner} ... end
  94. GameServer.put_player_symbol("my-game", player_x, 2) %GameEngine.Game{ board: %GameEngine.Board{ positions: [:x, :x, :x,

    nil, nil, nil, nil, :o, :o] }, finished: true, first: :x, next: :o, o: "Renan", x: "Felipe", winner: :x }
  95. None
  96. game_engine 4 ... 4 board.ex 4 game.ex 4 game_server.ex 4

    ...
  97. game_engine 4 ... 4 board.ex 4 game.ex 4 game_server.ex 4

    game_supervisor.ex
  98. game_supervisor.ex 4 É um DynamicSupervisor 4 Reponsável por criar os

    GameServer
  99. defmodule GameEngine.GameSupervisor do alias GameEngine.GameServer use DynamicSupervisor def start_link(_) do

    DynamicSupervisor.start_link(__MODULE__, nil, name: __MODULE__) end def init(_) do DynamicSupervisor.init(strategy: :one_for_one) end def create_game(name) do child_spec = %{ id: GameServer, start: {GameServer, :start_link, [name]}, restart: :temporary } DynamicSupervisor.start_child(__MODULE__, child_spec) end def game_exists?(game_name) do case Registry.lookup(:game_server_registry, game_name) do [] -> false _ -> true end end end
  100. defmodule GameEngine.GameSupervisor do alias GameEngine.GameServer use DynamicSupervisor def start_link(_) do

    DynamicSupervisor.start_link(__MODULE__, nil, name: __MODULE__) end def init(_) do DynamicSupervisor.init(strategy: :one_for_one) end def create_game(name) do child_spec = %{ id: GameServer, start: {GameServer, :start_link, [name]}, restart: :temporary } DynamicSupervisor.start_child(__MODULE__, child_spec) end def game_exists?(game_name) do case Registry.lookup(:game_server_registry, game_name) do [] -> false _ -> true end end end
  101. defmodule GameEngine.GameSupervisor do alias GameEngine.GameServer use DynamicSupervisor def start_link(_) do

    DynamicSupervisor.start_link(__MODULE__, nil, name: __MODULE__) end def init(_) do DynamicSupervisor.init(strategy: :one_for_one) end def create_game(name) do child_spec = %{ id: GameServer, start: {GameServer, :start_link, [name]}, restart: :temporary } DynamicSupervisor.start_child(__MODULE__, child_spec) end def game_exists?(game_name) do case Registry.lookup(:game_server_registry, game_name) do [] -> false _ -> true end end end
  102. defmodule GameEngine.GameServer do ... @impl true def handle_call({:leave, symbol}, _from,

    state) do new_state = state |> Game.remove_player(symbol) |> Game.reset_board() if Game.without_players?(new_state) do {:stop, :normal, new_state} else {:reply, {:ok, game_state}, new_state} end end ... end
  103. defmodule GameEngine.GameServer do ... @impl true def handle_cast({:leave, symbol}, state)

    do new_state = state |> Game.remove_player(symbol) |> Game.reset_board() if Game.without_players?(new_state) do {:stop, :normal, new_state} else {:noreply, new_state} end end ... end
  104. defmodule GameEngine.GameSupervisor do alias GameEngine.GameServer use DynamicSupervisor def start_link(_) do

    DynamicSupervisor.start_link(__MODULE__, nil, name: __MODULE__) end def init(_) do DynamicSupervisor.init(strategy: :one_for_one) end def create_game(name) do child_spec = %{ id: GameServer, start: {GameServer, :start_link, [name]}, restart: :temporary } DynamicSupervisor.start_child(__MODULE__, child_spec) end def game_exists?(game_name) do case Registry.lookup(:game_server_registry, game_name) do [] -> false _ -> true end end end
  105. game_engine 4 application.ex 4 board.ex 4 game.ex 4 game_server.ex 4

    game_supervisor.ex
  106. application.ex 4 Entry point da aplicação. 4 Inicializa o GameRegistry

    e o GameSupervisor.
  107. defmodule GameEngine.Application do alias GameEngine.{GameRegistry, GameSupervisor} use Application def start(_type,

    _args) do import Supervisor.Spec, warn: false children = [ GameRegistry, GameSupervisor ] opts = [strategy: :one_for_one, name: GameEngine.Supervisor] Supervisor.start_link(children, opts) end end
  108. defmodule GameEngine.Application do alias GameEngine.{GameRegistry, GameSupervisor} use Application def start(_type,

    _args) do import Supervisor.Spec, warn: false children = [ GameRegistry, GameSupervisor ] opts = [strategy: :one_for_one, name: GameEngine.Supervisor] Supervisor.start_link(children, opts) end end
  109. defmodule GameEngine.Application do alias GameEngine.{GameRegistry, GameSupervisor} use Application def start(_type,

    _args) do import Supervisor.Spec, warn: false children = [ GameRegistry, GameSupervisor ] opts = [strategy: :one_for_one, name: GameEngine.Supervisor] Supervisor.start_link(children, opts) end end
  110. None
  111. None
  112. None
  113. None
  114. game_engine 4 application.ex 4 board.ex 4 game.ex 4 game_server.ex 4

    game_supervisor.ex
  115. game_engine.ex

  116. defmodule GameEngine do @moduledoc """ Expose all public API's for

    projects that depend on `GameEngine`. """ alias GameEngine.{GameServer, GameSupervisor} defdelegate join_player(game, player), to: GameServer defdelegate put_player_symbol(game, symbol, pos), to: GameServer defdelegate new_round(game), to: GameServer defdelegate leave(game, symbol), to: GameServer def find_or_create_game(game_name) do if GameSupervisor.game_exists?(game_name) do game_name else {:ok, _pid} = GameSupervisor.create_game(game_name) game_name end end end
  117. defmodule GameEngine do @moduledoc """ Expose all public API's for

    projects that depend on `GameEngine`. """ alias GameEngine.{GameServer, GameSupervisor} defdelegate join_player(game, player), to: GameServer defdelegate put_player_symbol(game, symbol, pos), to: GameServer defdelegate new_round(game), to: GameServer defdelegate leave(game, symbol), to: GameServer def find_or_create_game(game_name) do if GameSupervisor.game_exists?(game_name) do game_name else {:ok, _pid} = GameSupervisor.create_game(game_name) game_name end end end
  118. defmodule GameEngine do @moduledoc """ Expose all public API's for

    projects that depend on `GameEngine`. """ alias GameEngine.{GameServer, GameSupervisor} defdelegate join_player(game, player), to: GameServer defdelegate put_player_symbol(game, symbol, pos), to: GameServer defdelegate new_round(game), to: GameServer defdelegate leave(game, symbol), to: GameServer def find_or_create_game(game_name) do if GameSupervisor.game_exists?(game_name) do game_name else {:ok, _pid} = GameSupervisor.create_game(game_name) game_name end end end
  119. That's all for game_engine

  120. tictactoe 4 apps 4 game_engine 4 game_ui

  121. game_ui 4 Phoenix application. 4 Reponsável pela interface web da

    applicação. 4 Depende do game_engine.
  122. defmodule GameUi.MixProject do ... defp deps do [ {:phoenix, "~>

    1.4.0"}, {:phoenix_pubsub, "~> 1.1"}, {:phoenix_html, "~> 2.11"}, {:phoenix_live_reload, "~> 1.2", only: :dev}, {:gettext, "~> 0.11"}, {:jason, "~> 1.0"}, {:plug_cowboy, "~> 2.0"}, {:game_engine, in_umbrella: true} ] end end
  123. controllers 4 game_controller.ex 4 player_controller.ex

  124. channels 4 user_socket.ex 4 game_channel.ex

  125. github.com/feliperenan/tictactoe

  126. Obrigado :)