Skip to content

Commit

Permalink
feat: add power viral spiral
Browse files Browse the repository at this point in the history
- create reducers and actions
- create new entity and changes, with tests
  • Loading branch information
dennyabrain committed Nov 27, 2024
1 parent 31c9a19 commit c2370e8
Show file tree
Hide file tree
Showing 10 changed files with 416 additions and 154 deletions.
9 changes: 9 additions & 0 deletions lib/viral_spiral/canon/card.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,12 @@ defmodule ViralSpiral.Canon.Card do

@type t :: Affinity.t() | Bias.t()
end

defmodule ViralSpiral.Canon.Card.Sparse do
defstruct id: nil, veracity: nil

@type t :: %__MODULE__{
id: String.t(),
veracity: boolean()
}
end
105 changes: 105 additions & 0 deletions lib/viral_spiral/entity/power_viral_spiral.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
defmodule ViralSpiral.Entity.PowerViralSpiral do
@moduledoc """
Struct used to conduct special power of viral spiral.
When a user uses the power of viral spiral, they can pass a card from their hand to multiple players. This is a very special case of the game where a player can hold multiple cards and pass to other players.
"""

alias ViralSpiral.Canon.Card.Sparse
alias ViralSpiral.Entity.Change
alias ViralSpiral.Entity.Player
alias ViralSpiral.Entity.PowerViralSpiral
alias ViralSpiral.Entity.Turn
defstruct turns: nil

@type t :: %__MODULE__{
turns: list(Turn.t())
}

def new(%Sparse{} = card, players) when is_list(players) do
Enum.map(
players,
&%Turn{
card: %Sparse{id: card.id, veracity: card.veracity},
current: &1,
pass_to: players
}
)
end

def get_turn(%PowerViralSpiral{} = power, player_id) do
power.turns
|> Enum.filter(&(&1.current == player_id))
|> hd
end

@doc """
Return turns other than the one of the passed player_id
"""
def other_turns(%PowerViralSpiral{} = power, player_id) do
Enum.filter(power.turns, &(&1.current != player_id))
end

def put_turn(%PowerViralSpiral{} = power, player_id, %Turn{} = turn) do
ix = Enum.find_index(power.turns, &(&1.current == player_id))

new_turns = List.replace_at(power.turns, ix, turn)
%{power | turns: new_turns}
end

def pass_to(%PowerViralSpiral{} = power, %Player{id: player_id}) do
power.turns
|> Enum.filter(&(&1.current == player_id))
|> hd
|> Map.get(:pass_to)
end

def pass_to(%PowerViralSpiral{} = power, player_id) when is_bitstring(player_id) do
power.turns
|> Enum.filter(&(&1.current == player_id))
|> hd
|> Map.get(:pass_to)
end

def reset(%PowerViralSpiral{}) do
nil
end

defimpl Change do
alias ViralSpiral.Canon.Card.Sparse
alias ViralSpiral.Room.ChangeDescriptions

def apply_change(state, change_desc) do
case change_desc[:type] do
:set ->
players = change_desc[:players]
card = change_desc[:card]

PowerViralSpiral.new(card, players)

:reset ->
PowerViralSpiral.reset(state)

:pass ->
from = change_desc[:from]
to = change_desc[:to]
turn = PowerViralSpiral.get_turn(state, from)

turn = Change.apply_change(turn, ChangeDescriptions.pass_turn_to(to))

# remove options from other player's turn
other_turns =
PowerViralSpiral.other_turns(state, from)
|> Enum.map(&Map.put(&1, :pass_to, &1.pass_to -- [to]))

new_turn = [turn] ++ other_turns

# check if this no possible pass options remain
case length(turn.pass_to) do
0 -> nil
_ -> %{state | turns: new_turn}
end
end
end
end
end
5 changes: 4 additions & 1 deletion lib/viral_spiral/entity/turn.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@ defmodule ViralSpiral.Entity.Turn do
todo : could the field be actions and not tied to every concrete thing like pass, discard etc.
"""
alias ViralSpiral.Canon.Card.Sparse
alias ViralSpiral.Entity.Change
alias ViralSpiral.Entity.Turn
alias ViralSpiral.Entity.Round

defstruct current: nil,
defstruct card: nil,
current: nil,
pass_to: []

@type t :: %__MODULE__{
card: Sparse.t(),
current: String.t() | nil,
pass_to: list(String.t())
}
Expand Down
18 changes: 17 additions & 1 deletion lib/viral_spiral/room/change_descriptions.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ defmodule ViralSpiral.Room.ChangeDescriptions do
def next_round(), do: [type: :next]
def skip_player(player_id), do: [type: :skip, player_id: player_id]
def new_turn(), do: []
def pass_turn_to(player) when is_binary(player), do: []
def pass_turn_to(player_id) when is_binary(player_id), do: [type: :next, target: player_id]
def pass_turn_to(players) when is_list(players), do: []

def draw_new_card(), do: []
Expand All @@ -30,4 +30,20 @@ defmodule ViralSpiral.Room.ChangeDescriptions do
"""
def remove_card(draw_type, card),
do: [type: :remove_card, draw_type: draw_type, card_in_set: card]

defmodule PowerViralSpiral do
alias ViralSpiral.Canon.Card.Sparse

def set(players, %Sparse{} = card) do
[type: :set, players: players, card: card]
end

def reset() do
[type: :reset]
end

def pass(from, to) do
[type: :pass, from: from, to: to]
end
end
end
19 changes: 17 additions & 2 deletions lib/viral_spiral/room/reducer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ defmodule ViralSpiral.Room.Reducer do
@moduledoc """
"""
alias ViralSpiral.Entity.PowerViralSpiral
alias ViralSpiral.Canon.Encyclopedia
alias ViralSpiral.Gameplay.Factory
alias ViralSpiral.Playable
Expand All @@ -13,10 +14,9 @@ defmodule ViralSpiral.Room.Reducer do

# @spec reduce(State.t(), Action.t()) :: State.t()
def reduce(%State{} = state, %{type: :draw_card} = action) do
draw_type = action.payload.draw_type

current_player = State.current_round_player(state)

draw_type = action.payload.draw_type
sets = state.deck.available_cards
draw_result = Deck.draw_card(sets, draw_type)

Expand Down Expand Up @@ -91,4 +91,19 @@ defmodule ViralSpiral.Room.Reducer do
# {state.players[player.id], ChangeDescriptions.turn_to_fake()}
# ]
end

def reduce(%State{} = state, %{type: :viral_spiral_pass, to: players} = action)
when is_list(players) do
card = action.payload.card

changes = [
{%PowerViralSpiral{}, ChangeDescriptions.PowerViralSpiral.set(players, card)}
]

State.apply_changes(state, changes)
end

def reduce(%State{} = state, %{type: :viral_spiral_pass, to: player})
when is_bitstring(player) do
end
end
18 changes: 12 additions & 6 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.PowerViralSpiral
alias ViralSpiral.Gameplay.Factory
alias ViralSpiral.Entity.Deck
alias ViralSpiral.Entity.Room
Expand All @@ -27,15 +28,17 @@ defmodule ViralSpiral.Room.State do
turn: nil,
turns: %{},
deck: nil,
articles: nil
articles: nil,
power_viralspiral: nil

@type t :: %__MODULE__{
room: Room.t(),
players: %{String.t() => Player.t()},
round: Round.t(),
turn: Turn.t(),
deck: Deck.t(),
articles: map()
articles: map(),
power_viralspiral: PowerViralSpiral.t()
}

def empty() do
Expand All @@ -54,10 +57,6 @@ defmodule ViralSpiral.Room.State do
turn = Turn.new(round)
deck = Factory.new_deck(room)

turns =
Map.keys(players)
|> Enum.map()

%State{
room: room,
players: players,
Expand Down Expand Up @@ -113,6 +112,9 @@ defmodule ViralSpiral.Room.State do
state
end

defp get_target(%State{} = state, %PowerViralSpiral{} = power) do
end

@doc """
Generalized way to get a nested entity from state.
Expand Down Expand Up @@ -143,6 +145,10 @@ defmodule ViralSpiral.Room.State do
Map.put(state, :deck, deck)
end

defp put_target(%State{} = state, %PowerViralSpiral{} = power) do
Map.put(state, :power_viralspiral, power)
end

def current_turn_player(%State{} = state), do: state.players[state.turn.current]

def current_round_player(%State{} = state),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
defmodule StoreFixtures do
defmodule StateFixtures do
alias ViralSpiral.Room.State
alias ViralSpiral.Entity.Room

def new_store() do
def new_state() do
room = Room.reserve("test-room") |> Room.start(4)
state = State.new(room, ["adhiraj", "krys", "aman", "farah"])

Expand Down
84 changes: 84 additions & 0 deletions test/viral_spiral/entity/power_viral_spiral_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
defmodule ViralSpiral.Entity.PowerViralSpiralTest do
alias ViralSpiral.Room.ChangeDescriptions
alias ViralSpiral.Entity.Change
alias ViralSpiral.Canon.Card.Sparse
alias ViralSpiral.Entity.Turn
alias ViralSpiral.Entity.PowerViralSpiral
use ExUnit.Case

describe "entity" do
setup do
power = %PowerViralSpiral{
turns: [
%Turn{
card: %Sparse{id: "card_pqr", veracity: true},
current: "player_abc",
pass_to: ["player_def", "player_ghi"]
},
%Turn{
card: %Sparse{id: "card_pqr", veracity: true},
current: "player_jkl",
pass_to: ["player_def", "player_ghi"]
}
]
}

%{power: power}
end

test "new", %{power: power} do
assert power.turns |> length() == 2
assert PowerViralSpiral.get_turn(power, "player_jkl").current == "player_jkl"
assert PowerViralSpiral.get_turn(power, "player_abc").current == "player_abc"

assert PowerViralSpiral.pass_to(power, "player_jkl") ==
["player_def", "player_ghi"]
end
end

describe "changes" do
setup do
power = %PowerViralSpiral{
turns: [
%Turn{
card: %Sparse{id: "card_pqr", veracity: true},
current: "player_abc",
pass_to: ["player_def", "player_ghi"]
},
%Turn{
card: %Sparse{id: "card_pqr", veracity: true},
current: "player_jkl",
pass_to: ["player_def", "player_ghi"]
}
]
}

%{power: power}
end

test "reset", %{power: power} do
power = Change.apply_change(power, ChangeDescriptions.PowerViralSpiral.reset())
assert power == nil
end

test "pass to someone", %{power: power} do
power =
Change.apply_change(
power,
ChangeDescriptions.PowerViralSpiral.pass("player_jkl", "player_ghi")
)

assert PowerViralSpiral.get_turn(power, "player_ghi").current == "player_ghi"
assert PowerViralSpiral.pass_to(power, "player_ghi") == ["player_def"]
assert PowerViralSpiral.pass_to(power, "player_abc") == ["player_def"]

power =
Change.apply_change(
power,
ChangeDescriptions.PowerViralSpiral.pass("player_abc", "player_def")
)

assert power == nil
end
end
end
Loading

0 comments on commit c2370e8

Please sign in to comment.