diff --git a/lib/zombie_survivor.ex b/lib/zombie_survivor.ex index 34a4819..e89d1f0 100644 --- a/lib/zombie_survivor.ex +++ b/lib/zombie_survivor.ex @@ -2,4 +2,16 @@ defmodule ZombieSurvivor do @moduledoc """ Documentation for ZombieSurvivor. """ + + @type level :: :blue | :yellow | :orange | :red + + @spec level(non_neg_integer) :: level + def level(experience) do + case experience do + x when x > 42 -> :red + x when x > 18 -> :orange + x when x > 6 -> :yellow + _ -> :blue + end + end end diff --git a/lib/zombie_survivor/game.ex b/lib/zombie_survivor/game.ex index 76c004e..a4e5eba 100644 --- a/lib/zombie_survivor/game.ex +++ b/lib/zombie_survivor/game.ex @@ -27,4 +27,11 @@ defmodule ZombieSurvivor.Game do Survivor.dead?(survivor) end) end + + @spec level(Game.t()) :: ZombieSurvivor.level() + def level(game) do + game.survivors + |> Enum.reduce(0, fn {_, s}, acc -> max(s.experience, acc) end) + |> ZombieSurvivor.level() + end end diff --git a/lib/zombie_survivor/survivor.ex b/lib/zombie_survivor/survivor.ex index 999274c..7968f26 100644 --- a/lib/zombie_survivor/survivor.ex +++ b/lib/zombie_survivor/survivor.ex @@ -4,10 +4,11 @@ defmodule ZombieSurvivor.Survivor do @type t :: %__MODULE__{ name: String.t(), wounds: non_neg_integer, - equipment: [String.t()] + equipment: [String.t()], + experience: non_neg_integer } - defstruct name: "", wounds: 0, equipment: [] + defstruct name: "", wounds: 0, equipment: [], experience: 0 @spec new([{atom, any}]) :: Survivor.t() def new(opts \\ []), do: struct(__MODULE__, opts) @@ -15,6 +16,9 @@ defmodule ZombieSurvivor.Survivor do @spec dead?(Survivor.t()) :: boolean def dead?(survivor), do: survivor.wounds >= 2 + @spec level(Survivor.t()) :: ZombieSurvivor.level() + def level(survivor), do: ZombieSurvivor.level(survivor.experience) + @spec max_actions(Survivor.t()) :: non_neg_integer def max_actions(_survivor) do 3 @@ -26,6 +30,11 @@ defmodule ZombieSurvivor.Survivor do |> discard_equipment() end + @spec kill_zombies(Survivor.t(), non_neg_integer) :: Survivor.t() + def kill_zombies(survivor, count \\ 1) do + %{survivor | experience: survivor.experience + count} + end + @spec max_equipment(Survivor.t()) :: non_neg_integer def max_equipment(%Survivor{} = survivor) do 5 - survivor.wounds diff --git a/test/game_test.exs b/test/game_test.exs index 63aac40..f54d03d 100644 --- a/test/game_test.exs +++ b/test/game_test.exs @@ -11,6 +11,10 @@ defmodule GameTest do test "that has 0 survivors" do assert Map.size(Game.new().survivors) == 0 end + + test "that's at level blue" do + assert Game.level(Game.new()) == :blue + end end describe "add_survivor/1" do @@ -69,4 +73,32 @@ defmodule GameTest do refute Game.ended?(g) end end + + describe "level/1" do + test "returns the level of the highest levelled survivor" do + g = + Game.new() + |> Game.add_survivor(@new_survivor) + + assert Game.level(g) == :blue + + g = + g + |> Game.add_survivor(Survivor.new(name: "Eric", experience: 10)) + + assert Game.level(g) == :yellow + + g = + g + |> Game.add_survivor(Survivor.new(name: "Jack", experience: 20)) + + assert Game.level(g) == :orange + + g = + g + |> Game.add_survivor(Survivor.new(name: "Liru", experience: 1_000_000)) + + assert Game.level(g) == :red + end + end end diff --git a/test/survivor_test.exs b/test/survivor_test.exs index 4bf445c..e1f9d1e 100644 --- a/test/survivor_test.exs +++ b/test/survivor_test.exs @@ -14,6 +14,14 @@ defmodule SurvivorTest do test "gives a survivor with 0 wounds" do assert %Survivor{wounds: 0} = Survivor.new() end + + test "gives a survivor with 0 experience" do + assert %Survivor{experience: 0} = Survivor.new() + end + + test "gives a survivor at level blue" do + assert Survivor.level(Survivor.new()) == :blue + end end describe "dead?/1" do @@ -35,6 +43,38 @@ defmodule SurvivorTest do end end + describe "kill_zombies/1" do + # TODO: Property testing, always increments + test "increases experience by 1" do + s = + Survivor.new() + |> Survivor.kill_zombies() + + assert s.experience == 1 + end + end + + describe "level/1" do + [ + {:blue, :yellow, 6}, + {:yellow, :orange, 18}, + {:orange, :red, 42} + ] + |> Enum.each(fn {old, new, threshold} -> + @old old + @new new + @threshold threshold + test "upgrades from #{@old} to #{@new} when exceeding #{@threshold} experience" do + s = Survivor.new(experience: @threshold) + assert Survivor.level(s) == @old + + s = s |> Survivor.kill_zombies() + + assert Survivor.level(s) == @new + end + end) + end + describe "max_actions/1" do test "returns 3 for a new survivor" do s = Survivor.new()