Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 23 additions & 8 deletions apps/go_fish/lib/go_fish/controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,17 @@ defmodule GoFish.Controller do
GenServer.call(__MODULE__, :get_players)
end

def get_player_states() do
players = GoFish.Controller.get_players()

player_states_list =
Enum.reduce(players, [], fn player, acc ->
[{player, GoFish.Player.get_state(player)} | acc]
end)

Map.new(player_states_list)
end

def start_game(player_list) do
GenServer.call(__MODULE__, {:start_game, player_list})
end
Expand All @@ -33,8 +44,7 @@ defmodule GoFish.Controller do
GenServer.cast(__MODULE__, :stop)
end


#Helper functions
# Helper functions

# def name_to_id(name) do
# String.to_atom(name)
Expand All @@ -53,7 +63,7 @@ defmodule GoFish.Controller do
end

def most_books(list_of_tuples) do
most_books([hd(list_of_tuples),most_books(tl(list_of_tuples))])
most_books([hd(list_of_tuples), most_books(tl(list_of_tuples))])
end

def game_over_state(state) do
Expand All @@ -64,14 +74,14 @@ defmodule GoFish.Controller do
%{:players => [], :game_state => :uninitialized, :total_books => 0, :winner => :undetermined}
end


# Callback
def init(_arg) do
{:ok, initial_game_state()}
end

def handle_call({:new_player, player_name}, _from, state) do
{:reply, {:new_player_added, player_name}, Map.update!(state, :players, fn x -> [player_name|x] end)}
{:reply, {:new_player_added, player_name},
Map.update!(state, :players, fn x -> [player_name | x] end)}
end

def handle_call(:get_state, _from, state) do
Expand All @@ -82,23 +92,27 @@ defmodule GoFish.Controller do
{:reply, Map.get(state, :players, []), state}
end

def handle_call({:start_game, player_list}, _from, state) when state.game_state == :uninitialized do
def handle_call({:start_game, player_list}, _from, state)
when state.game_state == :uninitialized do
for name <- player_list do
if length(player_list)>2 do
if length(player_list) > 2 do
GoFish.Player.draw_cards(name, 5)
else
GoFish.Player.draw_cards(name, 7)
end
end

GoFish.Player.give_turn_to(hd(player_list))
{:reply, :ok, Map.put(state, :game_state, :in_progress)}
end

def handle_call(:game_over, _from, state) when state.game_state == :in_progress do
final_state = game_over_state(state)

for name <- state.players do
GoFish.Player.stop(name)
end

GoFish.Ocean.stop()
{:reply, :game_terminated, final_state}
end
Expand All @@ -110,9 +124,11 @@ defmodule GoFish.Controller do
def handle_cast(:book_made, state) when state.total_books == 12 do
IO.puts("Final book")
final_state = game_over_state(state)

for name <- state.players do
GoFish.Player.stop(name)
end

GoFish.Ocean.stop()
{:noreply, final_state}
end
Expand All @@ -125,5 +141,4 @@ defmodule GoFish.Controller do
def handle_cast(:stop, _state) do
{:stop, :normal}
end

end
29 changes: 28 additions & 1 deletion apps/go_fish_web/assets/css/go_fish.css
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
}

/* highlight selected card */
input[type="radio"]:checked+div{ background-color: #95C8FF; }
input[type="radio"]:checked+div {
background-color: #95C8FF;
}

input.hide-radio-input {
/* Add if not using autoprefixer */
Expand Down Expand Up @@ -54,3 +56,28 @@ input.hide-radio-input {
display: inline;
}

.modal {
display: block;
position: fixed;
z-index: 1;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgb(0, 0, 0);
/* Fallback color */
background-color: rgba(0, 0, 0, 0.4);
/* Black w/ opacity */
}

.modal-content {
background: white;
margin: 15% auto;
padding: 20px;
border: 1px solid;
border-radius: 5px;
width: fit-content;
display: flex;
flex-direction: column;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
defmodule GoFishWeb.ChoosePlayerController do
use GoFishWeb, :controller

import GoFishWeb.GameView

def index(conn, _) do

render(conn, "index.html", players: GoFish.Controller.get_players())
end
end
34 changes: 17 additions & 17 deletions apps/go_fish_web/lib/go_fish_web/controllers/game_controller.ex
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
defmodule GoFishWeb.GameController do
use GoFishWeb, :controller

def index(conn, _params) do
if nil == Process.whereis(GoFish.Controller) do
redirect(conn, to: "/welcome")
else
players = GoFish.Controller.get_players()
player_states_list =
Enum.reduce(players, [], fn player, acc ->
[{player, GoFish.Player.get_state(player)} | acc]
end)
# def index(conn, _params) do
# if nil == Process.whereis(GoFish.Controller) do
# redirect(conn, to: "/welcome")
# else
# players = GoFish.Controller.get_players()
# player_states_list =
# Enum.reduce(players, [], fn player, acc ->
# [{player, GoFish.Player.get_state(player)} | acc]
# end)

player_states = Map.new(player_states_list)
# player_states = Map.new(player_states_list)

render(
conn,
"index.html",
player_states: player_states
)
end
end
# render(
# conn,
# "index.html",
# player_states: player_states
# )
# end
# end

def string_to_atom_list(s) do
s
Expand Down
64 changes: 64 additions & 0 deletions apps/go_fish_web/lib/go_fish_web/live/game_live.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
defmodule GoFishWeb.GameLive do
use GoFishWeb, :live_view

import GoFishWeb.GameView

def mount(_params, _, socket) do
if nil == Process.whereis(GoFish.Controller) do
{:ok, redirect(socket, to: "/welcome")}
else
# check every 0.1s if there is a change then send it
if connected?(socket), do: Process.send_after(self(), :update, 100)

{:ok,
socket
|> assign(:players, GoFish.Controller.get_players())
|> assign(:client_player, nil)}
end
end

def mount(_params, _, %{"assigns" => %{"client_player" => client_player}} = socket) do
if nil == Process.whereis(GoFish.Controller) do
{:ok, redirect(socket, to: "/welcome")}
else
# check every 0.1s if there is a change then send it
if connected?(socket), do: Process.send_after(self(), :update, 100)


{:ok,
socket
|> assign(:player_state, GoFish.Player.get_state(String.to_atom(client_player)))
|> assign(:opponents, Map.delete(GoFish.Controller.get_players(), client_player))
|> assign(:client_player, nil)}
end
end

def mount(_params, _, %{"assigns" => %{"client_player" => player}} = socket) do
{:ok,
socket
|> assign(:player_state, GoFish.Player.get_state(player))}
end

def handle_info(:update, %{"assigns" => %{"client_player" => client_player}} = socket) do
Process.send_after(self(), :update, 1)
player_state = GoFish.Player.get_state(client_player)
{:noreply, assign(socket, :player_state, player_state)}
end

def handle_info(:update, socket) do
Process.send_after(self(), :update, 1)
{:noreply, socket}
end

#FIXME seems like it is not handling this event properly
def handle_event("choose_player", %{"value" => client_player}, socket) do
{:noreply,
socket
|> assign(:client_player, client_player)
|> assign(:player_state, GoFish.Player.get_state(String.to_atom(client_player))
|> assign(:opponents, Map.delete(GoFish.Controller.get_players(), client_player))
)
}
end

end
88 changes: 88 additions & 0 deletions apps/go_fish_web/lib/go_fish_web/live/game_live.html.heex
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<%= if @client_player == nil do %>
<div class="modal">
<div class="modal-content">
<h1>Who are you?</h1>

<%= for player <- @players do %>
<button phx-click="choose_player" value={player}>
<%= uppercase(player) %>
</button>
<% end %>
</div>
</div>
<% else %>
<section class="row">
<article class="column">
<div style="display:flex; justify-content: space-between; flex-flow: wrap; ">

<div style="width:100%; margin: 10px; border: 1px solid black; border-radius: 15px; padding: 10px">
<h1>Hey <%= uppercase(@client_player) %></h1>
<form action="/ask_for_card">
<%= if (get_in(@player_state, :is_my_turn)) do %>
It's your turn! <br />
<%= if !Enum.empty?(get_in(@player_states, :hand)) do %>
Ask the following player: <br />
<%= for opponent <- @opponents do %>
<div>
<label class="opponent-label form-control">
<input type="radio" name="giver" value={opponent} required />
<%= uppercase(opponent) %>
</label>
</div>
<% end %>
<input type="hidden" name="taker" value={@client_player} />
For all their cards with the same value as: <br />
<% end %>
<% end %>
<div style="display:flex; flex-flow:wrap; row-gap: 10px; padding-right: 30px; column-gap:-20px">
<%= for card <- get_in(@player_state, :hand) |> Enum.sort(&(&1.value >=
&2.value)) # Sort by value
do %>
<label class="form-control">
<input
type="radio"
name="value"
required
class="hide-radio-input"
value={to_string(card.value)}
disabled={!get_in(@player_state, :is_my_turn)}
/>
<div
class="playing-card card-hover"
disabled={!get_in(@player_state, :is_my_turn)}
>
<div class="playing-card-inner">
<span style={
"line-height:1; font-weight:500; color:" <> suit_to_color(card.suit)
}>
<%= card.value %>
</span>
<br />
<%= suit_to_emoji(card.suit) %>
</div>
</div>
</label>
<% end %>
</div>
<br />
<input
type="submit"
value="Ask for card"
disabled={!get_in(@player_state, :is_my_turn)}
/>
</form>
<br />
<%= if (get_in(@player_state, :is_my_turn)) and Enum.empty?(get_in(@player_state, :hand)) do %>
<button style="background-color:#bddef6;">
<%= link("Draw Card", to: "draw_card/#{@client_player}", method: :get) %>
</button>
<% end %>
<br /> Collected books: <br />
<%= for book <- get_in(@player_state, :books) do %>
<%= book %>'s
<% end %>
</div>
</div>
</article>
</section>
<% end %>
5 changes: 4 additions & 1 deletion apps/go_fish_web/lib/go_fish_web/router.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
defmodule GoFishWeb.Router do
use GoFishWeb, :router

import Phoenix.LiveView.Router

pipeline :browser do
plug :accepts, ["html"]
plug :fetch_session
Expand All @@ -16,9 +18,10 @@ defmodule GoFishWeb.Router do

scope "/", GoFishWeb do
pipe_through :browser
live "/", GameLive
get "/welcome", WelcomeController, :index
get "/", GameController, :index
get "/start_game", GameController, :start_game
get "/choose_player", ChoosePlayerController, :index
get "/draw_card/:player", GameController, :draw_card
get "/ask_for_card/", GameController, :ask_for_card
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<%= live_title_tag assigns[:page_title] || "GoFish", suffix: " · Phoenix Framework" %>
<link phx-track-static rel="stylesheet" href={Routes.static_path(@conn, "/assets/app.css")}/>
<script defer phx-track-static type="text/javascript" src={Routes.static_path(@conn, "/assets/app.js")}></script>
<script src="https://twemoji.maxcdn.com/v/latest/twemoji.min.js" crossorigin="anonymous"></script>
</head>
<body>
<header>
Expand Down
5 changes: 5 additions & 0 deletions apps/go_fish_web/lib/go_fish_web/views/choose_player_view.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
defmodule GoFishWeb.ChoosePlayerView do
import GoFishWeb.GameView, only: [uppercase: 1] #TODO: refactor this

use GoFishWeb, :view
end