Skip to content

Commit

Permalink
Extract state calculations into GameState
Browse files Browse the repository at this point in the history
  • Loading branch information
m1foley committed Jul 3, 2024
1 parent 8f43e14 commit 75a0623
Show file tree
Hide file tree
Showing 7 changed files with 231 additions and 117 deletions.
90 changes: 1 addition & 89 deletions lib/mjw/core/game.ex
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ defmodule Mjw.Game do
end)
end

defp remaining_winds_to_pick(%__MODULE__{seats: seats}) do
def remaining_winds_to_pick(%__MODULE__{seats: seats}) do
@wind_tiles -- Enum.map(seats, & &1.picked_wind)
end

Expand Down Expand Up @@ -996,92 +996,4 @@ defmodule Mjw.Game do
def resume_bots(%__MODULE__{} = game) do
%{game | pause_bots: false}
end

@doc """
Calculate the state of a game
"""
def state(%__MODULE__{} = game) do
{game, :tbd}
|> state_waiting_for_players
|> state_picking_winds
|> state_rolling_for_first_dealer
|> state_rolling_for_deal
|> state_win_declared
|> state_discarding
|> state_drawing
|> state_invalid
end

defp state_waiting_for_players({game, :tbd}) do
if empty_seats_count(game) > 0 do
{game, :waiting_for_players}
else
{game, :tbd}
end
end

defp state_waiting_for_players({game, state}), do: {game, state}

defp state_picking_winds({game, :tbd}) do
if !Enum.empty?(remaining_winds_to_pick(game)) do
{game, :picking_winds}
else
{game, :tbd}
end
end

defp state_picking_winds({game, state}), do: {game, state}

defp state_rolling_for_first_dealer({game, :tbd}) do
if Enum.empty?(game.dice) do
{game, :rolling_for_first_dealer}
else
{game, :tbd}
end
end

defp state_rolling_for_first_dealer({game, state}), do: {game, state}

defp state_rolling_for_deal({game, :tbd}) do
if game.turn_state == :rolling do
{game, :rolling_for_deal}
else
{game, :tbd}
end
end

defp state_rolling_for_deal({game, state}), do: {game, state}

defp state_win_declared({game, :tbd}) do
if win_declared_seatno(game) do
{game, :win_declared}
else
{game, :tbd}
end
end

defp state_win_declared({game, state}), do: {game, state}

defp state_discarding({game, :tbd}) do
if game.turn_state == :discarding do
{game, :discarding}
else
{game, :tbd}
end
end

defp state_discarding({game, state}), do: {game, state}

defp state_drawing({game, :tbd}) do
if game.turn_state == :drawing do
{game, :drawing}
else
{game, :tbd}
end
end

defp state_drawing({game, state}), do: {game, state}

defp state_invalid({_game, :tbd}), do: :invalid
defp state_invalid({_game, state}), do: state
end
89 changes: 89 additions & 0 deletions lib/mjw/core/game_state.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
defmodule Mjw.GameState do
@doc """
Calculate the state of a game
"""
def state(%Mjw.Game{} = game) do
{game, :tbd}
|> waiting_for_players
|> picking_winds
|> rolling_for_first_dealer
|> rolling_for_deal
|> win_declared
|> discarding
|> drawing
|> invalid
end

defp waiting_for_players({game, :tbd}) do
if Mjw.Game.empty_seats_count(game) > 0 do
{game, :waiting_for_players}
else
{game, :tbd}
end
end

defp waiting_for_players({game, state}), do: {game, state}

defp picking_winds({game, :tbd}) do
if !Enum.empty?(Mjw.Game.remaining_winds_to_pick(game)) do
{game, :picking_winds}
else
{game, :tbd}
end
end

defp picking_winds({game, state}), do: {game, state}

defp rolling_for_first_dealer({game, :tbd}) do
if Enum.empty?(game.dice) do
{game, :rolling_for_first_dealer}
else
{game, :tbd}
end
end

defp rolling_for_first_dealer({game, state}), do: {game, state}

defp rolling_for_deal({game, :tbd}) do
if game.turn_state == :rolling do
{game, :rolling_for_deal}
else
{game, :tbd}
end
end

defp rolling_for_deal({game, state}), do: {game, state}

defp win_declared({game, :tbd}) do
if Mjw.Game.win_declared_seatno(game) do
{game, :win_declared}
else
{game, :tbd}
end
end

defp win_declared({game, state}), do: {game, state}

defp discarding({game, :tbd}) do
if game.turn_state == :discarding do
{game, :discarding}
else
{game, :tbd}
end
end

defp discarding({game, state}), do: {game, state}

defp drawing({game, :tbd}) do
if game.turn_state == :drawing do
{game, :drawing}
else
{game, :tbd}
end
end

defp drawing({game, state}), do: {game, state}

defp invalid({_game, :tbd}), do: :invalid
defp invalid({_game, state}), do: state
end
2 changes: 1 addition & 1 deletion lib/mjw_web/live/game_live/show.ex
Original file line number Diff line number Diff line change
Expand Up @@ -803,7 +803,7 @@ defmodule MjwWeb.GameLive.Show do
show_wind_picking_was = socket.assigns[:show_wind_picking]

current_user_seatno = Mjw.Game.sitting_at(game, current_user_id)
game_state = Mjw.Game.state(game)
game_state = Mjw.GameState.state(game)
last_discarded_seatno = Mjw.Game.last_discarded_seatno(game)

win_declared_seatno =
Expand Down
19 changes: 10 additions & 9 deletions lib/mjw_web/stores/bot_service.ex
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ defmodule MjwWeb.BotService do
def optionally_enqueue_roll(%Mjw.Game{pause_bots: true} = game), do: game

def optionally_enqueue_roll(%Mjw.Game{} = game) do
game_state = Mjw.Game.state(game)
game_state = Mjw.GameState.state(game)
roller_seatno = Mjw.Game.current_roller_seatno(game, game_state)

if roller_seatno && bot_sitting_at?(game, roller_seatno) do
Expand All @@ -27,7 +27,7 @@ defmodule MjwWeb.BotService do
def optionally_enqueue_draw(%Mjw.Game{pause_bots: true} = game), do: game

def optionally_enqueue_draw(%Mjw.Game{} = game) do
if bot_sitting_at?(game, game.turn_seatno) && Mjw.Game.state(game) == :drawing do
if bot_sitting_at?(game, game.turn_seatno) && Mjw.GameState.state(game) == :drawing do
enqueue_delayed_action(:draw, game.id, game.turn_seatno)
end

Expand All @@ -37,7 +37,7 @@ defmodule MjwWeb.BotService do
def optionally_enqueue_try_win_out_of_turn(%Mjw.Game{pause_bots: true} = game), do: game

def optionally_enqueue_try_win_out_of_turn(%Mjw.Game{} = game) do
if Mjw.Game.state(game) == :drawing && bots_out_of_turn?(game) do
if Mjw.GameState.state(game) == :drawing && bots_out_of_turn?(game) do
enqueue_delayed_action(
:try_win_out_of_turn,
game.id,
Expand All @@ -52,7 +52,7 @@ defmodule MjwWeb.BotService do
def optionally_enqueue_discard(%Mjw.Game{pause_bots: true} = game), do: game

def optionally_enqueue_discard(%Mjw.Game{} = game) do
if bot_sitting_at?(game, game.turn_seatno) && Mjw.Game.state(game) == :discarding do
if bot_sitting_at?(game, game.turn_seatno) && Mjw.GameState.state(game) == :discarding do
enqueue_discard(game)
end

Expand Down Expand Up @@ -116,7 +116,8 @@ defmodule MjwWeb.BotService do
defp perform_action(_action_type, %Mjw.Game{pause_bots: true}, _bot_seatno), do: nil

defp perform_action(:rolling_for_first_dealer, %Mjw.Game{} = game, bot_seatno) do
if Mjw.Game.state(game) == :rolling_for_first_dealer && bot_sitting_at?(game, bot_seatno) do
if Mjw.GameState.state(game) == :rolling_for_first_dealer &&
bot_sitting_at?(game, bot_seatno) do
game
|> Mjw.Game.roll_dice_and_reseat_players()
|> update_game(:rolled_for_first_dealer, bot_seatno)
Expand All @@ -125,7 +126,7 @@ defmodule MjwWeb.BotService do
end

defp perform_action(:rolling_for_deal, %Mjw.Game{} = game, bot_seatno) do
if Mjw.Game.state(game) == :rolling_for_deal && bot_sitting_at?(game, bot_seatno) do
if Mjw.GameState.state(game) == :rolling_for_deal && bot_sitting_at?(game, bot_seatno) do
game
|> Mjw.Game.roll_dice_and_deal()
|> update_game(:rolled_for_deal, bot_seatno)
Expand All @@ -139,7 +140,7 @@ defmodule MjwWeb.BotService do
%Mjw.Game{turn_state: :drawing, turn_seatno: bot_seatno} = game,
bot_seatno
) do
if Mjw.Game.state(game) == :drawing && bot_sitting_at?(game, bot_seatno) do
if Mjw.GameState.state(game) == :drawing && bot_sitting_at?(game, bot_seatno) do
case Mjw.Game.bot_draw(game) do
{:draw_deck_tile, game} ->
game
Expand Down Expand Up @@ -167,7 +168,7 @@ defmodule MjwWeb.BotService do
game,
turn_seatno
) do
if Mjw.Game.state(game) == :drawing && bots_out_of_turn?(game) do
if Mjw.GameState.state(game) == :drawing && bots_out_of_turn?(game) do
case Mjw.Game.bots_try_win_out_of_turn(game) do
{:ok, won_game, win_declared_seatno} ->
update_game(won_game, :declared_win, win_declared_seatno, %{tile: discard_tile})
Expand All @@ -183,7 +184,7 @@ defmodule MjwWeb.BotService do
%Mjw.Game{turn_state: :discarding, turn_seatno: bot_seatno} = game,
bot_seatno
) do
if Mjw.Game.state(game) == :discarding && bot_sitting_at?(game, bot_seatno) do
if Mjw.GameState.state(game) == :discarding && bot_sitting_at?(game, bot_seatno) do
case Mjw.Game.bot_discard(game) do
{:ok, game} ->
game
Expand Down
112 changes: 112 additions & 0 deletions test/mjw/core/game_state_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
defmodule Mjw.GameStateTest do
use ExUnit.Case, async: true

test "waiting_for_players" do
game = Mjw.Game.new()
assert Mjw.GameState.state(game) == :waiting_for_players
end

test "waiting for players when partially filled" do
game =
%Mjw.Game{}
|> Mjw.Game.seat_player("id0", "name0")
|> Mjw.Game.seat_player("id1", "name1")

assert Mjw.GameState.state(game) == :waiting_for_players
end

test "picking_winds" do
game =
%Mjw.Game{}
|> Mjw.Game.seat_player("id0", "name0")
|> Mjw.Game.seat_player("id1", "name1")
|> Mjw.Game.seat_player("id2", "name2")
|> Mjw.Game.seat_player("id3", "name3")

assert Mjw.GameState.state(game) == :picking_winds
end

test "rolling_for_first_dealer" do
game =
%Mjw.Game{}
|> Mjw.Game.seat_player("id0", "name0")
|> Mjw.Game.seat_player("id1", "name1")
|> Mjw.Game.seat_player("id2", "name2")
|> Mjw.Game.seat_player("id3", "name3")
|> Mjw.Game.pick_random_available_wind(0)
|> Mjw.Game.pick_random_available_wind(1)
|> Mjw.Game.pick_random_available_wind(2)
|> Mjw.Game.pick_random_available_wind(3)

assert Mjw.GameState.state(game) == :rolling_for_first_dealer
end

test "rolling_for_deal" do
game =
%Mjw.Game{}
|> Mjw.Game.seat_player("id0", "name0")
|> Mjw.Game.seat_player("id1", "name1")
|> Mjw.Game.seat_player("id2", "name2")
|> Mjw.Game.seat_player("id3", "name3")
|> Mjw.Game.pick_random_available_wind(0)
|> Mjw.Game.pick_random_available_wind(1)
|> Mjw.Game.pick_random_available_wind(2)
|> Mjw.Game.pick_random_available_wind(3)
|> Mjw.Game.roll_dice_and_reseat_players()

assert Mjw.GameState.state(game) == :rolling_for_deal
end

test "discarding" do
game =
%Mjw.Game{}
|> Mjw.Game.seat_player("id0", "name0")
|> Mjw.Game.seat_player("id1", "name1")
|> Mjw.Game.seat_player("id2", "name2")
|> Mjw.Game.seat_player("id3", "name3")
|> Mjw.Game.pick_random_available_wind(0)
|> Mjw.Game.pick_random_available_wind(1)
|> Mjw.Game.pick_random_available_wind(2)
|> Mjw.Game.pick_random_available_wind(3)
|> Mjw.Game.roll_dice_and_reseat_players()
|> Mjw.Game.roll_dice_and_deal()

assert Mjw.GameState.state(game) == :discarding
end

test "drawing" do
{:ok, game} =
Mjw.Game.new()
|> Mjw.Game.seat_player("id0", "name0")
|> Mjw.Game.seat_player("id1", "name1")
|> Mjw.Game.seat_player("id2", "name2")
|> Mjw.Game.seat_player("id3", "name3")
|> Mjw.Game.pick_random_available_wind(0)
|> Mjw.Game.pick_random_available_wind(1)
|> Mjw.Game.pick_random_available_wind(2)
|> Mjw.Game.pick_random_available_wind(3)
|> Mjw.Game.roll_dice_and_reseat_players()
|> Mjw.Game.roll_dice_and_deal()
|> Mjw.Game.discard(0, "n1-1")

assert Mjw.GameState.state(game) == :drawing
end

test "win_declared" do
game =
%Mjw.Game{}
|> Mjw.Game.seat_player("id0", "name0")
|> Mjw.Game.seat_player("id1", "name1")
|> Mjw.Game.seat_player("id2", "name2")
|> Mjw.Game.seat_player("id3", "name3")
|> Mjw.Game.pick_random_available_wind(0)
|> Mjw.Game.pick_random_available_wind(1)
|> Mjw.Game.pick_random_available_wind(2)
|> Mjw.Game.pick_random_available_wind(3)
|> Mjw.Game.roll_dice_and_reseat_players()
|> Mjw.Game.roll_dice_and_deal()
|> Mjw.Game.declare_win_from_hand(0, "n1-0")

assert Mjw.GameState.state(game) == :win_declared
end
end
Loading

0 comments on commit 75a0623

Please sign in to comment.