Working and understanding GenServer

Hi,

I am new to GenServer and I am having a hard time understanding it. I am trying to create a war card game. I am using GenServer to set up a server, client’s API’s and server callbacks. Functions I want to create are:

  • starting a game
  • adding a deck
  • shuffling a deck
  • placing a bet
  • drawing players card
  • drawing computers card

Up until now I have:

defmodule WarGame.Boundary.GameManager do
  use GenServer
  alias WarGame.Core.Game
  alias WarGame.Core.Deck


  # Client API
  def start_link(options \\ []) do
    GenServer.start_link(__MODULE__, %{}, options)
  end

  def start_game(manager \\ __MODULE__, game_fields) do
    GenServer.call(manager, {:start_game, game_fields})
  end

  def add_deck(manager \\__MODULE__) do
    GenServer.call(manager, {:add_deck})
  end


  # Server Callbackszz

  def init(war_game) do
    {:ok, war_game}
  end

  def handle_call({:add_deck}, _from. war_game) do
    deck = Deck.new_deck()
    game = %Game{}
    new_war_game = Map.put(war_game, game.deck, deck)

    {:reply, :ok, new_war_game}
  end

  def handle_call({:start_game, game_fields}, _from, war_game) do
    game = Game.new(game_fields)
    new_war_game = Map.put(war_game, game.user_cards, game)

    {:reply, :ok, new_war_game}

  end

end

I managed to start a server and start a game. However, I don’t know how to add fields etc, so my add_deck call doesn’t work.
This is my WarGame.Core.Deck

defmodule WarGame.Core.Deck do

  defmodule Card do
    defstruct value: [],
              suit: []
  end

  def new_deck do
    for  suit <- suits(), value <- values() do
      %Card{value: value, suit: suit}
    end
  end

  def shuffle(new_deck) do
    new_deck
    |> Enum.shuffle()
  end

  def deal_a_card(cards) do
    [card | new_deck] = shuffle(cards)
    [{to_tuple(card), new_deck}]
  end

  defp to_tuple(
    %WarGame.Core.Deck.Card{value: value, suit: suit}
    ), do: {value, suit}

  defp values(), do: Enum.to_list(2..14)
  defp suits(), do: [:hearts, :diamonds, :spades, :clubs]



end

My WarGame.Core.Game

defmodule WarGame.Core.Game do


  defstruct user_cards: [],
            croupier_cards: [],
            user_bets: [],
            deck: %{},
            used: []


  def new(fields) do
    struct!(__MODULE__, fields)
  end


end

I will be thankful for explanation how to update, change and show fields using GenServer in simple words. I did read documentations but as you can see it didn’t help. I did try Map.update but I am not getting it :frowning:

Best wishes to all!

Your Map.put doesn’t look right. Map.put takes a map, then the key to insert, then the value to insert.

In your handle_call functions, you’re creating an new Game struct, whose default deck is an empty map and then using that empty map as the key to insert into the state. Such that it looks like new_war_game = Map.put(war_game, %{}, deck) I presume you want to add the new deck to the game, which is Map.put(war_game, :deck, Deck.new_deck()) or %Game{ war_game | deck: Deck.new_deck()}.

Something like this?

def handle_call({:add_deck}, _from. war_game) do
    {:reply, :ok, %Game{ war_game | deck: Deck.new_deck()}
  end

  def handle_call({:start_game, game_fields}, _from, war_game) do
    game = Game.new(game_fields)
    {:reply, :ok, game}
  end
1 Like

Thanks for reply!

Yes, that’s what I was looking for! I will try to implement your suggestions to further functions.

1 Like

@cmo

I tried to follow your explanation with :shuffle_deck but I guess I am not getting it.

What am I doing wrong?

  def shuffle_deck(manager \\ __MODULE__, deck_field) do
    GenServer.call(manager, {:shuffle_deck, deck_field})
  end

  def handle_call({:shuffle_deck, deck_field}, _from, war_game) do
    {:reply, :ok, %Game{ war_game | deck: Deck.shuffle(deck_field)}}
  end

What is deck_field supposed to be? Deck.shuffle takes a deck. You already have a deck in your state, so shuffle it. You could put Deck.shuffle(war_game.deck) in what you have or do a Map.update(war_game, :deck, &Deck.shuffle/1) to get the new state.

1 Like

So once I initialized my deck I don’t need to bother doing it again? I didn’t get that.
deck_field was supposed to be a deck.