Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

High Score

high_score.livemd

High Score

Resolving

defmodule HighScore do
  @start_score 0

  def new() do
    Map.new()
  end

  def add_player(scores, name, score \\ @start_score) do
    Map.merge(scores, %{name => score})
  end

  def remove_player(scores, name) do
    Map.delete(scores, name)
  end

  def reset_score(scores, name) do
    if Map.has_key?(scores, name) do
      Map.update!(scores, name, &(&1 = @start_score))
    else
      add_player(scores, name, @start_score)
    end
  end

  def update_score(scores, name, score) do
    if Map.has_key?(scores, name) do
      Map.update!(scores, name, &(&1 + score))
    else
      add_player(scores, name, score)
    end
  end

  def get_players(scores) do
    Map.keys(scores)
  end
end
scores =
  HighScore.new()
  |> HighScore.update_score("José Valim", 486_373)

scores == %{"José Valim" => 486_373}

Testing

ExUnit.start(auto_run: false)

defmodule HighScoreTest do
  use ExUnit.Case

  # Trivia: Scores used in this test suite are based on lines of code
  # added to the elixir-lang/elixir github repository as of Apr 27, 2020.

  @tag task_id: 1
  test "new/1 result in empty score map" do
    assert HighScore.new() == %{}
  end

  describe "add_player/2" do
    @tag task_id: 2
    test "add player without score to empty score map" do
      scores = HighScore.new()

      assert HighScore.add_player(scores, "José Valim") == %{"José Valim" => 0}
    end

    @tag task_id: 2
    test "add two players without score to empty map" do
      scores =
        HighScore.new()
        |> HighScore.add_player("José Valim")
        |> HighScore.add_player("Chris McCord")

      assert scores == %{"Chris McCord" => 0, "José Valim" => 0}
    end

    @tag task_id: 2
    test "add player with score to empty score map" do
      scores =
        HighScore.new()
        |> HighScore.add_player("José Valim", 486_373)

      assert scores == %{"José Valim" => 486_373}
    end

    @tag task_id: 2
    test "add players with scores to empty score map" do
      scores =
        HighScore.new()
        |> HighScore.add_player("José Valim", 486_373)
        |> HighScore.add_player("Dave Thomas", 2_374)

      assert scores == %{"José Valim" => 486_373, "Dave Thomas" => 2_374}
    end
  end

  describe "remove_player/2" do
    @tag task_id: 3
    test "remove from empty score map results in empty score map" do
      scores =
        HighScore.new()
        |> HighScore.remove_player("José Valim")

      assert scores == %{}
    end

    @tag task_id: 3
    test "remove player after adding results in empty score map" do
      map =
        HighScore.new()
        |> HighScore.add_player("José Valim")
        |> HighScore.remove_player("José Valim")

      assert map == %{}
    end

    @tag task_id: 3
    test "remove first player after adding two results in map with remaining player" do
      scores =
        HighScore.new()
        |> HighScore.add_player("José Valim")
        |> HighScore.add_player("Chris McCord")
        |> HighScore.remove_player("José Valim")

      assert scores == %{"Chris McCord" => 0}
    end

    @tag task_id: 3
    test "remove second player after adding two results in map with remaining player" do
      scores =
        HighScore.new()
        |> HighScore.add_player("José Valim")
        |> HighScore.add_player("Chris McCord")
        |> HighScore.remove_player("Chris McCord")

      assert scores == %{"José Valim" => 0}
    end
  end

  describe "reset_score/2" do
    @tag task_id: 4
    test "resetting score for non-existent player sets player score to 0" do
      scores =
        HighScore.new()
        |> HighScore.reset_score("José Valim")

      assert scores == %{"José Valim" => 0}
    end

    @tag task_id: 4
    test "resetting score for existing player sets previous player score to 0" do
      scores =
        HighScore.new()
        |> HighScore.add_player("José Valim", 486_373)
        |> HighScore.reset_score("José Valim")

      assert scores == %{"José Valim" => 0}
    end
  end

  describe "update_score/3" do
    @tag task_id: 5
    test "update score for non existent player initializes value" do
      scores =
        HighScore.new()
        |> HighScore.update_score("José Valim", 486_373)

      assert scores == %{"José Valim" => 486_373}
    end

    @tag task_id: 5
    test "update score for existing player adds score to previous" do
      scores =
        HighScore.new()
        |> HighScore.add_player("José Valim")
        |> HighScore.update_score("José Valim", 486_373)

      assert scores == %{"José Valim" => 486_373}
    end

    @tag task_id: 5
    test "update score for existing player with non-zero score adds score to previous" do
      scores =
        HighScore.new()
        |> HighScore.add_player("José Valim")
        |> HighScore.update_score("José Valim", 1)
        |> HighScore.update_score("José Valim", 486_373)

      assert scores == %{"José Valim" => 486_374}
    end
  end

  describe "get_players/1" do
    @tag task_id: 6
    test "empty score map gives empty list" do
      scores_by_player =
        HighScore.new()
        |> HighScore.get_players()

      assert scores_by_player == []
    end

    @tag task_id: 6
    test "score map with one entry gives one result" do
      players =
        HighScore.new()
        |> HighScore.add_player("José Valim")
        |> HighScore.update_score("José Valim", 486_373)
        |> HighScore.get_players()

      assert players == ["José Valim"]
    end

    @tag task_id: 6
    test "score map with multiple entries gives results in unknown order" do
      players =
        HighScore.new()
        |> HighScore.add_player("José Valim", 486_373)
        |> HighScore.add_player("Dave Thomas", 2_374)
        |> HighScore.add_player("Chris McCord", 0)
        |> HighScore.add_player("Saša Jurić", 762)
        |> HighScore.get_players()
        |> Enum.sort()

      assert players == [
               "Chris McCord",
               "Dave Thomas",
               "José Valim",
               "Saša Jurić"
             ]
    end
  end
end

ExUnit.run()