Skip to content

Commit

Permalink
wip: cancel power
Browse files Browse the repository at this point in the history
  • Loading branch information
dennyabrain committed Feb 6, 2025
1 parent 4308ccb commit 0b807e7
Show file tree
Hide file tree
Showing 10 changed files with 189 additions and 8 deletions.
13 changes: 13 additions & 0 deletions lib/viral_spiral/entity/player.ex
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,19 @@ defmodule ViralSpiral.Entity.PlayerMap do
|> Enum.filter(&(players[&1].identity != identity))
end

@doc """
Return ids of players who like/dislike the same thing.
For instance, to get all players who like cats, call `of_same_affinity_polarity(players, :cat, :positive)`.
To get all players who hate skubs, call `of_same_affinity_polarity(players, :skub, :negative)`
"""
def of_same_affinity_polarity(players, affinity, polarity)
when is_map(players) and is_affinity(affinity) and polarity in [:positive, :negative] do
end

def of_opposite_affinity_polarity(players, affinity) when is_affinity(affinity) do
end

@doc """
Return all players whose identity is different from the passed player
"""
Expand Down
26 changes: 23 additions & 3 deletions lib/viral_spiral/entity/power_cancel_player.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ defmodule ViralSpiral.Entity.PowerCancelPlayer do
When a Player meets certain criteria, they are allowed to cancel another Player. This takes the following form :
A player initiates cancellation by choosing which player they want to cancel and which of their affinity they want to use for it. The affinity they choose determines who can vote to cancel this player. If the majority votes to cancel the targetted Player, that player will skip a turn.
"""
alias ViralSpiral.Entity.Change
alias ViralSpiral.Entity.PowerCancelPlayer
alias ViralSpiral.Affinity
import ViralSpiral.Room.EngineConfig.Guards

defstruct state: :idle,
from: nil,
target: nil,
affinity: nil,
allowed_voters: [],
votes: [],
result: nil

Expand All @@ -22,15 +25,18 @@ defmodule ViralSpiral.Entity.PowerCancelPlayer do

@type t :: %__MODULE__{
state: states(),
from: String.t(),
target: String.t(),
affinity: Affinity.t(),
allowed_voters: list(String.t()),
votes: list(vote()),
result: boolean()
}

@spec start_vote(t(), String.t(), Affinity.target()) :: t()
def start_vote(%PowerCancelPlayer{} = power, target, affinity) when is_affinity(affinity) do
%{power | target: target, affinity: affinity, state: :waiting}
@spec start_vote(t(), String.t(), String.t(), Affinity.target()) :: t()
def start_vote(%PowerCancelPlayer{} = power, from, target, affinity)
when is_affinity(affinity) do
%{power | from: from, target: target, affinity: affinity, state: :waiting}
end

@spec vote(t(), String.t(), boolean()) :: t()
Expand All @@ -40,6 +46,10 @@ defmodule ViralSpiral.Entity.PowerCancelPlayer do
%{power | votes: power.votes ++ [%{id: player, vote: vote}], state: state}
end

def allowed_voters(%PowerCancelPlayer{} = power, players) when is_list(players) do
%{power | allowed_voters: players}
end

@spec put_result(t()) :: t()
def put_result(%PowerCancelPlayer{} = power) do
total_votes = length(power.votes)
Expand All @@ -48,7 +58,17 @@ defmodule ViralSpiral.Entity.PowerCancelPlayer do
%{power | result: result, state: :done}
end

@spec reset(t()) :: t()
def reset(%PowerCancelPlayer{} = _power) do
%PowerCancelPlayer{}
end

defimpl Change do
def apply_change(state, change_desc) do
case change_desc[:type] do
:initiate -> state
:add_vote -> state
end
end
end
end
26 changes: 26 additions & 0 deletions lib/viral_spiral/room/actions.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ defmodule ViralSpiral.Room.Actions do
@moduledoc """
Instances of Action triggered by a Player or Game Engine .
"""
alias ViralSpiral.Room.Actions.Player.VoteToCancel
alias ViralSpiral.Room.Actions.Player.InitiateCancel
alias ViralSpiral.Room.Actions.Player.TurnToFake
alias ViralSpiral.Canon.Card.Sparse
alias ViralSpiral.Room.Action
Expand Down Expand Up @@ -105,4 +107,28 @@ defmodule ViralSpiral.Room.Actions do
payload: action
}
end

def initiate_cancel(attrs) do
action =
%InitiateCancel{}
|> InitiateCancel.changeset(attrs)
|> apply_changes()

%Action{
type: :initiate_cancel,
payload: action
}
end

def vote_to_cancel(attrs) do
action =
%VoteToCancel{}
|> VoteToCancel.changeset(attrs)
|> apply_changes()

%Action{
type: :vote_to_cancel,
payload: action
}
end
end
16 changes: 16 additions & 0 deletions lib/viral_spiral/room/actions/player/initiate_cancel.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
defmodule ViralSpiral.Room.Actions.Player.InitiateCancel do
use Ecto.Schema
import Ecto.Changeset

@primary_key false
embedded_schema do
field :from, :string
field :target, :string
field :affinity, Ecto.Enum, values: [:houseboat, :skub, :cat, :highfive, :socks]
end

def changeset(initiate_cancel, attrs) do
initiate_cancel
|> cast(attrs, [:from, :target, :affinity])
end
end
16 changes: 16 additions & 0 deletions lib/viral_spiral/room/actions/player/vote_to_cancel.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
defmodule ViralSpiral.Room.Actions.Player.VoteToCancel do
use Ecto.Schema
import Ecto.Changeset

embedded_schema do
field :player, :string
field :vote, :boolean
end

def changeset(vote, attrs) do
vote
|> cast(attrs, [:player, :vote])

# todo add tests that player should start with `player_`?
end
end
18 changes: 18 additions & 0 deletions lib/viral_spiral/room/change_descriptions.ex
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,24 @@ defmodule ViralSpiral.Room.ChangeDescriptions do
end
end

defmodule PowerCancelPlayer do
def initiate(from, target, affinity) do
Keyword.new()
|> Keyword.put(:type, :initiate)
|> Keyword.put(:from, from)
|> Keyword.put(:target, target)
|> Keyword.put(:affinity, affinity)
end

def vote(from, vote, opts) do
Keyword.new()
|> Keyword.put(:type, :vote)
|> Keyword.put(:from, from)
|> Keyword.put(:vote, vote)
|> Keyword.put(:opts, opts)
end
end

defmodule Room do
def join(player_name) do
[type: :join, player_name: player_name]
Expand Down
7 changes: 5 additions & 2 deletions lib/viral_spiral/room/state.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ defmodule ViralSpiral.Room.State do
When a round begins, we also start a Turn. Within each Round there's a turn that includes everyone except the person who started the turn.
"""

alias ViralSpiral.Entity.PowerCancelPlayer
alias ViralSpiral.Canon.Card.Sparse
alias ViralSpiral.Entity.CheckSource
alias ViralSpiral.Entity.Article
Expand All @@ -34,7 +35,8 @@ defmodule ViralSpiral.Room.State do
deck: nil,
articles: %{},
power_viralspiral: nil,
power_check_source: CheckSource.new()
power_check_source: CheckSource.new(),
power_cancel_player: %PowerCancelPlayer{}

@type t :: %__MODULE__{
room: Room.t(),
Expand All @@ -44,7 +46,8 @@ defmodule ViralSpiral.Room.State do
deck: Deck.t(),
articles: map(),
power_viralspiral: PowerViralSpiral.t(),
power_check_source: CheckSource.t()
power_check_source: CheckSource.t(),
power_cancel_player: PowerCancelPlayer.t()
}

def empty() do
Expand Down
17 changes: 14 additions & 3 deletions test/viral_spiral/entity/power_cancel_player_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,40 @@ defmodule ViralSpiral.Entity.PowerCancelPlayerTest do
power = %PowerCancelPlayer{}
assert power.state == :idle

power = power |> start_vote("player_abc", :cat)
power = power |> start_vote("player_abc", "player_mno", :cat)
assert power.state == :waiting

power =
power
|> vote("player_def", true)
|> vote("player_jkl", true)
|> vote("player_ghi", true)

assert power.votes |> length() == 2

power = power |> vote("player_lmn", false, done: true)
power = power |> vote("player_jkl", false, done: true)
assert power.votes |> length() == 3
assert power.state == :done

power = power |> put_result()
assert power.result == true

IO.inspect(power)

power = power |> reset()
assert power.state == :idle
assert length(power.votes) == 0
end
end

describe "changes" do
setup do
%{}
end

test "initiate" do
end

test "vote" do
end
end
end
30 changes: 30 additions & 0 deletions test/viral_spiral/room/actions/player/initiate_cancel_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
defmodule ViralSpiral.Room.Actions.Player.InitiateCancelTest do
alias ViralSpiral.Room.Actions.Player.InitiateCancel
use ExUnit.Case

@valid_attr %{
"from" => "player_abc",
"target" => "player_def",
"affinity" => "cat"
}

@invaid_attr %{
"from" => "player_abc",
"target" => "player_def",
"affinity" => "Cat"
}

test "validation" do
changeset =
%InitiateCancel{}
|> InitiateCancel.changeset(@valid_attr)

assert changeset.valid? == true

changeset =
%InitiateCancel{}
|> InitiateCancel.changeset(@invaid_attr)

assert changeset.valid? == false
end
end
28 changes: 28 additions & 0 deletions test/viral_spiral/room/actions/player/vote_to_cancel_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
defmodule ViralSpiral.Room.Actions.Player.VoteToCancelTest do
alias ViralSpiral.Room.Actions.Player.VoteToCancel
use ExUnit.Case

@valid_attr %{
"player" => "player_abc",
"vote" => "true"
}

@invalid_attr %{
"player" => "player_abc",
"vote" => "NaN"
}

test "validation" do
changeset =
%VoteToCancel{}
|> VoteToCancel.changeset(@valid_attr)

assert changeset.valid? == true

changeset =
%VoteToCancel{}
|> VoteToCancel.changeset(@invalid_attr)

assert changeset.valid? == false
end
end

0 comments on commit 0b807e7

Please sign in to comment.