diff --git a/lib/viral_spiral/canon/deck.ex b/lib/viral_spiral/canon/deck.ex index dd14489..ddfe8dc 100644 --- a/lib/viral_spiral/canon/deck.ex +++ b/lib/viral_spiral/canon/deck.ex @@ -570,7 +570,12 @@ defmodule ViralSpiral.Canon.Deck do Returns a tuple that should be a valid key of a Store. - [ + [type: :topical, veracity: false] + [type: :topical, veracity: true] + [type: :affinity, veracity: true, target: :skub] + [type: :bias, veracity: false, target: :yellow] and so on + + deprecated : [ {:conflated, false}, {:topical, false}, {:topical, true}, diff --git a/lib/viral_spiral/entity/check_source_map.ex b/lib/viral_spiral/entity/check_source_map.ex new file mode 100644 index 0000000..33c37b2 --- /dev/null +++ b/lib/viral_spiral/entity/check_source_map.ex @@ -0,0 +1,32 @@ +defmodule ViralSpiral.Entity.CheckSource do + alias ViralSpiral.Entity.Change + alias ViralSpiral.Entity.CheckSource + + defstruct [:map] + + @type t :: %__MODULE__{ + map: %{optional(String.t()) => CheckSource.t()} + } + + def new() do + %CheckSource{map: %{}} + end + + defimpl Change do + def apply_change(check_source, change_desc) do + case change_desc[:type] do + :put -> + %{ + check_source + | map: Map.put(check_source.map, change_desc[:key], change_desc[:source]) + } + + :drop -> + %{ + check_source + | map: Map.drop(check_source.map, [change_desc[:key]]) + } + end + end + end +end diff --git a/lib/viral_spiral/entity/source.ex b/lib/viral_spiral/entity/source.ex new file mode 100644 index 0000000..8c54a2e --- /dev/null +++ b/lib/viral_spiral/entity/source.ex @@ -0,0 +1,11 @@ +defmodule ViralSpiral.Entity.Source do + defstruct [:owner, :headline, :content, :author, :type] + + @type t :: %__MODULE__{ + owner: String.t(), + headline: String.t(), + content: String.t(), + author: String.t(), + type: String.t() + } +end diff --git a/lib/viral_spiral/room/actions.ex b/lib/viral_spiral/room/actions.ex index 9b9cab6..2f3a45c 100644 --- a/lib/viral_spiral/room/actions.ex +++ b/lib/viral_spiral/room/actions.ex @@ -58,10 +58,8 @@ defmodule ViralSpiral.Room.Actions do type: :view_source, payload: %{ player_id: player_id, - card: %{ - id: card_id, - veracity: card_veracity - } + card_id: card_id, + card_veracity: card_veracity } } end @@ -71,10 +69,8 @@ defmodule ViralSpiral.Room.Actions do type: :hide_source, payload: %{ player_id: player_id, - card: %{ - id: card_id, - veracity: card_veracity - } + card_id: card_id, + card_veracity: card_veracity } } end diff --git a/lib/viral_spiral/room/factory.ex b/lib/viral_spiral/room/factory.ex index aabc98a..2879218 100644 --- a/lib/viral_spiral/room/factory.ex +++ b/lib/viral_spiral/room/factory.ex @@ -119,16 +119,6 @@ defmodule ViralSpiral.Room.Factory do } end - def make_entity_article(%Article{} = article) do - %EntityArticle{ - headline: article.headline, - veracity: article.veracity, - type: article.type, - content: article.content, - author: article.author - } - end - def new_game() do %State{ room: Room.new() @@ -155,6 +145,15 @@ defmodule ViralSpiral.Room.Factory do Reducer.reduce(state, Actions.draw_card(draw_type)) end + @doc """ + draw_type is a tuple. + + For more, visit `ViralSpiral.Canon.Deck.draw_type/1` + """ + def draw_card(%State{} = state, draw_type) do + Reducer.reduce(state, Actions.draw_card(draw_type)) + end + def pass_card(%State{} = state, %Sparse{} = card, from, to) do Reducer.reduce(state, Actions.pass_card(card.id, card.veracity, from, to)) end @@ -165,4 +164,12 @@ defmodule ViralSpiral.Room.Factory do def discard_card(%State{} = state, %Sparse{} = card, from) do end + + def view_source(%State{} = state, player_id, card_id, card_veracity) do + Reducer.reduce(state, Actions.view_source(player_id, card_id, card_veracity)) + end + + def close_source(%State{} = state, player_id, card_id, card_veracity) do + Reducer.reduce(state, Actions.hide_source(player_id, card_id, card_veracity)) + end end diff --git a/lib/viral_spiral/room/reducer.ex b/lib/viral_spiral/room/reducer.ex index 5656bdc..2f35f64 100644 --- a/lib/viral_spiral/room/reducer.ex +++ b/lib/viral_spiral/room/reducer.ex @@ -2,6 +2,9 @@ defmodule ViralSpiral.Room.Reducer do @moduledoc """ """ + alias ViralSpiral.Entity.Source + alias ViralSpiral.Entity.CheckSource + alias ViralSpiral.Canon.Card.Sparse alias ViralSpiral.Entity.PowerViralSpiral alias ViralSpiral.Canon.Encyclopedia alias ViralSpiral.Room.Factory @@ -78,25 +81,35 @@ defmodule ViralSpiral.Room.Reducer do end def reduce(%State{} = state, %{type: :view_source} = action) do - %{card: card, player_id: player_id} = action.payload - - article_store = state.deck.article_store - article = Encyclopedia.get_article_by_card(article_store, card) - article_entity = Factory.make_entity_article(article) - - %{ - state - | articles: Map.put(state.articles, {player_id, card}, article_entity) + %{card_id: card_id, card_veracity: card_veracity, player_id: player_id} = action.payload + card = Sparse.new({card_id, card_veracity}) + article = Encyclopedia.get_article_by_card(state.deck.article_store, card) + + key = "#{player_id}_#{card_id}_#{card_veracity}" + + source = %Source{ + owner: player_id, + headline: article.headline, + content: article.content, + author: article.author, + type: article.type } + + state + |> State.apply_changes([ + {state.power_check_source, [type: :put, key: key, source: source]} + ]) end def reduce(%State{} = state, %{type: :hide_source} = action) do - %{card: card, player_id: player_id} = action.payload + %{card_id: card_id, card_veracity: card_veracity, player_id: player_id} = action.payload - %{ - state - | articles: Map.delete(state.articles, {player_id, card}) - } + key = "#{player_id}_#{card_id}_#{card_veracity}" + + state + |> State.apply_changes([ + {state.power_check_source, [type: :drop, key: key]} + ]) end def reduce(%State{} = state, %Action{type: :turn_card_to_fake} = action) do diff --git a/lib/viral_spiral/room/state.ex b/lib/viral_spiral/room/state.ex index cab1f33..025eb73 100644 --- a/lib/viral_spiral/room/state.ex +++ b/lib/viral_spiral/room/state.ex @@ -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.CheckSource alias ViralSpiral.Entity.Article alias ViralSpiral.Entity.PowerViralSpiral alias ViralSpiral.Room.Factory @@ -23,6 +24,7 @@ defmodule ViralSpiral.Room.State do alias ViralSpiral.Room.State alias ViralSpiral.Entity.Change + @derive {Inspect, limit: 2} defstruct room: nil, players: %{}, round: nil, @@ -30,7 +32,8 @@ defmodule ViralSpiral.Room.State do turns: %{}, deck: nil, articles: %{}, - power_viralspiral: nil + power_viralspiral: nil, + power_check_source: CheckSource.new() @type t :: %__MODULE__{ room: Room.t(), @@ -39,7 +42,8 @@ defmodule ViralSpiral.Room.State do turn: Turn.t(), deck: Deck.t(), articles: map(), - power_viralspiral: PowerViralSpiral.t() + power_viralspiral: PowerViralSpiral.t(), + power_check_source: CheckSource.t() } def empty() do @@ -84,6 +88,8 @@ defmodule ViralSpiral.Room.State do # list({:ok, message :: String.t()} | {:error, reason :: String.t()}) def apply_changes(state, changes) do Enum.reduce(changes, state, fn change, state -> + # require IEx + # IEx.pry() data = get_target(state, elem(change, 0)) change_desc = elem(change, 1) new_value = apply_change(data, change_desc) @@ -116,6 +122,10 @@ defmodule ViralSpiral.Room.State do defp get_target(%State{} = state, %PowerViralSpiral{} = power) do end + defp get_target(%State{} = state, %CheckSource{} = _check_source) do + state.power_check_source + end + defp get_target(%State{} = state, %Article{id: id} = article) do state.articles[id] end @@ -159,74 +169,78 @@ defmodule ViralSpiral.Room.State do Map.put(state, :power_viralspiral, power) end + defp put_target(%State{} = state, %CheckSource{} = check_source) do + Map.put(state, :power_check_source, check_source) + end + @spec current_round_player(State.t()) :: Player.t() def current_turn_player(%State{} = state), do: state.players[state.turn.current] def current_round_player(%State{} = state), do: state.players[Round.current_player_id(state.round)] - defimpl Inspect do - import Inspect.Algebra - alias Inspect.Opts - - def inspect(state, _opts) do - players = - Map.keys(state.players) - |> Enum.map(fn id -> - player = state.players[id] - - current_round = if player == State.current_round_player(state), do: "CR", else: "" - current_turn = if player == State.current_turn_player(state), do: "CT", else: "" - - header = "#{player.id} : #{player.name} : #{player.identity} : #{player.clout} " - - affinities = - Map.keys(player.affinities) - |> Enum.map(&"#{&1} : #{player.affinities[&1]}") - |> Enum.join(" | ") - - biases = - Map.keys(player.biases) - |> Enum.map(&"#{&1} : #{player.biases[&1]}") - |> Enum.join(" | ") - - active_cards = - player.active_cards - |> Enum.map(&"#{elem(&1, 0)} : #{elem(&1, 1)}") - |> IO.iodata_to_binary() - - # hand = - # player.hand - # |> Enum.map(&"#{elem(&1, 0)} : #{elem(&1, 1)}") - # |> IO.iodata_to_binary() - - [current_round <> current_turn, header, affinities, biases, active_cards] - |> Enum.join("\n") - |> IO.iodata_to_binary() - - # {player.id, player.name, player.clout, player.affinities, player.biases} - # |> IO.iodata_to_binary() - end) - |> Enum.intersperse(line()) - |> Enum.intersperse(line()) - |> concat() - - concat([ - nest(doc({state.room.id, state.room.name, state.room.chaos_counter}), 4), - line(), - players - ]) - end - - def doc(entity) do - to_doc(entity, %Opts{pretty: true}) - end - - def linebr() do - concat([ - String.duplicate("_", 50), - line() - ]) - end - end + # defimpl Inspect do + # import Inspect.Algebra + # alias Inspect.Opts + + # def inspect(state, _opts) do + # players = + # Map.keys(state.players) + # |> Enum.map(fn id -> + # player = state.players[id] + + # current_round = if player == State.current_round_player(state), do: "CR", else: "" + # current_turn = if player == State.current_turn_player(state), do: "CT", else: "" + + # header = "#{player.id} : #{player.name} : #{player.identity} : #{player.clout} " + + # affinities = + # Map.keys(player.affinities) + # |> Enum.map(&"#{&1} : #{player.affinities[&1]}") + # |> Enum.join(" | ") + + # biases = + # Map.keys(player.biases) + # |> Enum.map(&"#{&1} : #{player.biases[&1]}") + # |> Enum.join(" | ") + + # active_cards = + # player.active_cards + # |> Enum.map(&"#{elem(&1, 0)} : #{elem(&1, 1)}") + # |> IO.iodata_to_binary() + + # # hand = + # # player.hand + # # |> Enum.map(&"#{elem(&1, 0)} : #{elem(&1, 1)}") + # # |> IO.iodata_to_binary() + + # [current_round <> current_turn, header, affinities, biases, active_cards] + # |> Enum.join("\n") + # |> IO.iodata_to_binary() + + # # {player.id, player.name, player.clout, player.affinities, player.biases} + # # |> IO.iodata_to_binary() + # end) + # |> Enum.intersperse(line()) + # |> Enum.intersperse(line()) + # |> concat() + + # concat([ + # nest(doc({state.room.id, state.room.name, state.room.chaos_counter}), 4), + # line(), + # players + # ]) + # end + + # def doc(entity) do + # to_doc(entity, %Opts{pretty: true}) + # end + + # def linebr() do + # concat([ + # String.duplicate("_", 50), + # line() + # ]) + # end + # end end diff --git a/test/viral_spiral/gameplay_test.exs b/test/viral_spiral/gameplay_test.exs index 2349a67..31a12a6 100644 --- a/test/viral_spiral/gameplay_test.exs +++ b/test/viral_spiral/gameplay_test.exs @@ -4,6 +4,8 @@ defmodule ViralSpiral.GameTest do We only do 2 rounds for brevity. """ + alias ViralSpiral.Canon.Card.Sparse + alias ViralSpiral.Canon.Encyclopedia alias ViralSpiral.Entity.PlayerMap alias ViralSpiral.Room.Factory alias ViralSpiral.Entity.Player @@ -111,4 +113,107 @@ defmodule ViralSpiral.GameTest do # IO.inspect(state.room) # end) end + + describe "check source power" do + setup do + state = %State{ + room: %Room{ + id: "room_abc", + name: "crazy-house-3213", + state: :running, + unjoined_players: [], + affinities: [:skub, :houseboat], + communities: [:red, :yellow, :blue], + chaos: 0, + chaos_counter: 10, + volatality: :medium + }, + players: %{ + "player_abc" => %Player{ + id: "player_abc", + name: "farah", + biases: %{yellow: 0, blue: 0}, + affinities: %{skub: 0, houseboat: 0}, + clout: 0, + identity: :red, + hand: [], + active_cards: [] + }, + "player_def" => %Player{ + id: "player_def", + name: "aman", + biases: %{red: 0, blue: 0}, + affinities: %{skub: 0, houseboat: 0}, + clout: 0, + identity: :yellow, + hand: [], + active_cards: [] + }, + "player_ghi" => %Player{ + id: "player_ghi", + name: "krys", + biases: %{yellow: 0, blue: 0}, + affinities: %{skub: 0, houseboat: 0}, + clout: 0, + identity: :red, + hand: [], + active_cards: [] + }, + "player_jkl" => %Player{ + id: "player_jkl", + biases: %{yellow: 0, blue: 0}, + affinities: %{skub: 0, houseboat: 0}, + clout: 0, + name: "adhiraj", + identity: :red, + hand: [], + active_cards: [] + } + }, + round: %Round{ + order: ["player_jkl", "player_ghi", "player_def", "player_abc"], + count: 4, + current: 0, + skip: nil + }, + turn: %Turn{ + card: nil, + current: "player_jkl", + pass_to: ["player_abc", "player_def", "player_ghi"] + } + } + + state = %{state | deck: Factory.new_deck(state.room)} + + %{state: state} + end + + test "view/hide source for true affinity card", %{state: state} do + state = state |> Factory.draw_card(type: :affinity, target: :skub, veracity: true) + current_player = State.current_turn_player(state) + active_card = current_player.active_cards |> hd + card = Sparse.new(active_card) + + state = state |> Factory.view_source(current_player.id, card.id, card.veracity) + key = "#{current_player.id}_#{card.id}_#{card.veracity}" + assert state.power_check_source.map[key] != nil + + state = state |> Factory.close_source(current_player.id, card.id, card.veracity) + assert state.power_check_source.map[key] == nil + end + + test "view/hide source for false affinity card", %{state: state} do + state = state |> Factory.draw_card(type: :affinity, target: :houseboat, veracity: false) + current_player = State.current_turn_player(state) + active_card = current_player.active_cards |> hd + card = Sparse.new(active_card) + + state = state |> Factory.view_source(current_player.id, card.id, card.veracity) + key = "#{current_player.id}_#{card.id}_#{card.veracity}" + assert state.power_check_source.map[key] != nil + + state = state |> Factory.close_source(current_player.id, card.id, card.veracity) + assert state.power_check_source.map[key] == nil + end + end end