city-office & high-score

This commit is contained in:
2023-12-18 04:03:01 -05:00
parent b8a7845967
commit 5f3aa01e4d
22 changed files with 1321 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
{
"authors": [
"neenjaw"
],
"files": {
"solution": [
"lib/high_score.ex"
],
"test": [
"test/high_score_test.exs"
],
"exemplar": [
".meta/exemplar.ex"
]
},
"language_versions": ">=1.10",
"icon": "high-scores",
"blurb": "Learn about maps by keeping track of the high scores in your local arcade hall."
}

View File

@@ -0,0 +1 @@
{"track":"elixir","exercise":"high-score","id":"7ad834a7e1364fd2b9244ec879f0ee69","url":"https://exercism.org/tracks/elixir/exercises/high-score","handle":"negrienko","is_requester":true,"auto_approve":false}

View File

@@ -0,0 +1,4 @@
# Used by "mix format"
[
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
]

24
elixir/high-score/.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# The directory Mix will write compiled artifacts to.
/_build/
# If you run "mix test --cover", coverage assets end up here.
/cover/
# The directory Mix downloads your dependencies sources to.
/deps/
# Where third-party dependencies like ExDoc output generated docs.
/doc/
# Ignore .fetch files in case you like to edit your project deps locally.
/.fetch
# If the VM crashes, it generates a dump, let's ignore it too.
erl_crash.dump
# Also ignore archive artifacts (built via "mix archive.build").
*.ez
# Ignore package tarball (built via "mix hex.build").
maps-*.tar

75
elixir/high-score/HELP.md Normal file
View File

@@ -0,0 +1,75 @@
# Help
## Running the tests
From the terminal, change to the base directory of the exercise then execute the tests with:
```bash
$ mix test
```
This will execute the test file found in the `test` subfolder -- a file ending in `_test.exs`
Documentation:
* [`mix test` - Elixir's test execution tool](https://hexdocs.pm/mix/Mix.Tasks.Test.html)
* [`ExUnit` - Elixir's unit test library](https://hexdocs.pm/ex_unit/ExUnit.html)
## Pending tests
In test suites of practice exercises, all but the first test have been tagged to be skipped.
Once you get a test passing, you can unskip the next one by commenting out the relevant `@tag :pending` with a `#` symbol.
For example:
```elixir
# @tag :pending
test "shouting" do
assert Bob.hey("WATCH OUT!") == "Whoa, chill out!"
end
```
If you wish to run all tests at once, you can include all skipped test by using the `--include` flag on the `mix test` command:
```bash
$ mix test --include pending
```
Or, you can enable all the tests by commenting out the `ExUnit.configure` line in the file `test/test_helper.exs`.
```elixir
# ExUnit.configure(exclude: :pending, trace: true)
```
## Useful `mix test` options
* `test/<FILE>.exs:LINENUM` - runs only a single test, the test from `<FILE>.exs` whose definition is on line `LINENUM`
* `--failed` - runs only tests that failed the last time they ran
* `--max-failures` - the suite stops evaluating tests when this number of test failures
is reached
* `--seed 0` - disables randomization so the tests in a single file will always be ran
in the same order they were defined in
## Submitting your solution
You can submit your solution using the `exercism submit lib/high_score.ex` command.
This command will upload your solution to the Exercism website and print the solution page's URL.
It's possible to submit an incomplete solution which allows you to:
- See how others have completed the exercise
- Request help from a mentor
## Need to get help?
If you'd like help solving the exercise, check the following pages:
- The [Elixir track's documentation](https://exercism.org/docs/tracks/elixir)
- The [Elixir track's programming category on the forum](https://forum.exercism.org/c/programming/elixir)
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
If you're stuck on something, it may help to look at some of the [available resources](https://exercism.org/docs/tracks/elixir/resources) out there where answers might be found.

View File

@@ -0,0 +1,42 @@
# Hints
## General
- A [map][maps] is an associative data structure of key-value pairs.
- Elixir offers [many useful Map module functions in the standard library][map-module].
## 1. Define a new high score map
- It should return an empty [map][maps].
- [Map module][map-module] functions or literal forms can be useful.
## 2. Add players to the high score map
- The resulting map should be returned.
- [Map module][map-module] contains functions useful for manipulating maps. [One of them][map-put] puts a value in a map under a given key.
## 3. Remove players from the score map
- The resulting map should be returned.
- [Map module][map-module] contains functions useful for manipulating maps. [One of them][map-delete] deletes a key from a map.
## 4. Reset a player's score
- The resulting map should be returned with the player's score reset to an initial value.
- [Map module][map-module] contains functions useful for manipulating maps. [One of them][map-put] puts a value in a map under a given key.
## 5. Update a player's score
- The resulting map should be returned with the player's updated score.
- [Map module][map-module] contains functions useful for manipulating maps. [One of them][map-update] updates a value in a map under a given key.
## 6. Get a list of players
- [Map module][map-module] contains functions useful for manipulating maps. [One of them][map-keys] returns a list of all keys in a map.
[maps]: https://elixir-lang.org/getting-started/keywords-and-maps.html#maps
[map-module]: https://hexdocs.pm/elixir/Map.html
[map-put]: https://hexdocs.pm/elixir/Map.html#put/3
[map-delete]: https://hexdocs.pm/elixir/Map.html#delete/2
[map-update]: https://hexdocs.pm/elixir/Map.html#update/4
[map-keys]: https://hexdocs.pm/elixir/Map.html#keys/1

161
elixir/high-score/README.md Normal file
View File

@@ -0,0 +1,161 @@
# High Score
Welcome to High Score on Exercism's Elixir Track.
If you need help running the tests or submitting your code, check out `HELP.md`.
If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :)
## Introduction
## Maps
Maps in Elixir are the data structure for storing information in key-value pairs. In other languages, these might also be known as associative arrays (PHP), hashes (Perl 5, Raku), or dictionaries (Python).
Keys and values can be of any data type, but if the key is an atom we can use a shorthand syntax. Maps do not guarantee the order of their entries when accessed or returned.
### Literal forms
An empty map is simply declared with `%{}`. If we want to add items to a map literal, we can use two forms:
```elixir
# If the key is an atom:
%{atom_key: 1}
# If the key is a different type:
%{1 => :atom_value}
# You can even mix these if the atom form comes second:
%{"first_form" => :a, atom_form: :b}
```
While there is no canonical format, choose a consistent way to represent the key-value literal pairs.
### Map module functions
Elixir provides many functions for working with maps in the _Map module_. Some _Map module_ functions require an _anonymous_ function to be passed into the function to assist with the map transformation.
## Module Attributes As Constants
In Elixir, we can define module attributes which can be used as constants in our functions.
```elixir
defmodule Example do
# Defines the attribute as the value 1
@constant_number 1
def example_value() do
# Returns the value 1
@constant_number
end
end
```
When used to define module constants, attributes can be any expression which can be evaluated at compilation time. After compilation, module attributes are not accessible since they are expanded during compilation, similar to defined macros in languages like C.
## Instructions
In this exercise, you're implementing a way to keep track of the high scores for the most popular game in your local arcade hall.
## 1. Define a new high score map
To make a new high score map, define the `HighScore.new/0` function which doesn't take any arguments and returns a new, empty map of high scores.
```elixir
HighScore.new()
# => %{}
```
## 2. Add players to the high score map
To add a player to the high score map, define `HighScore.add_player/3`, which is a function which takes 3 arguments:
- The first argument is the map of scores.
- The second argument is the name of a player as a string.
- The third argument is the score as an integer. The argument is optional, implement the third argument with a default value of 0.
Store the default initial score in a module attribute. It will be needed again.
```elixir
score_map = HighScore.new()
# => %{}
score_map = HighScore.add_player(score_map, "Dave Thomas")
# => %{"Dave Thomas" => 0}
score_map = HighScore.add_player(score_map, "José Valim", 486_373)
# => %{"Dave Thomas" => 0, "José Valim"=> 486_373}
```
## 3. Remove players from the score map
To remove a player from the high score map, define `HighScore.remove_player/2`, which takes 2 arguments:
- The first argument is the map of scores.
- The second argument is the name of the player as a string.
```elixir
score_map = HighScore.new()
# => %{}
score_map = HighScore.add_player(score_map, "Dave Thomas")
# => %{"Dave Thomas" => 0}
score_map = HighScore.remove_player(score_map, "Dave Thomas")
# => %{}
```
## 4. Reset a player's score
To reset a player's score, define `HighScore.reset_score/2`, which takes 2 arguments:
- The first argument is the map of scores.
- The second argument is the name of the player as a string, whose score you wish to reset.
The function should also work if the player doesn't have a score.
```elixir
score_map = HighScore.new()
# => %{}
score_map = HighScore.add_player(score_map, "José Valim", 486_373)
# => %{"José Valim"=> 486_373}
score_map = HighScore.reset_score(score_map, "José Valim")
# => %{"José Valim"=> 0}
```
## 5. Update a player's score
To update a player's score by adding to the previous score, define `HighScore.update_score/3`, which takes 3 arguments:
- The first argument is the map of scores.
- The second argument is the name of the player as a string, whose score you wish to update.
- The third argument is the score that you wish to **add** to the stored high score.
The function should also work if the player doesn't have a previous score - assume the previous score is 0.
```elixir
score_map = HighScore.new()
# => %{}
score_map = HighScore.add_player(score_map, "José Valim", 486_373)
# => %{"José Valim"=> 486_373}
score_map = HighScore.update_score(score_map, "José Valim", 5)
# => %{"José Valim"=> 486_378}
```
## 6. Get a list of players
To get a list of players, define `HighScore.get_players/1`, which takes 1 argument:
- The first argument is the map of scores.
```elixir
score_map = HighScore.new()
# => %{}
score_map = HighScore.add_player(score_map, "Dave Thomas", 2_374)
# => %{"Dave Thomas" => 2_374}
score_map = HighScore.add_player(score_map, "José Valim", 486_373)
# => %{"Dave Thomas" => 2_374, "José Valim"=> 486_373}
HighScore.get_players(score_map)
# => ["Dave Thomas", "José Valim"]
```
## Source
### Created by
- @neenjaw

View File

@@ -0,0 +1,21 @@
defmodule HighScore do
@default_score 0
def new(),
do: %{}
def add_player(scores, name, score \\ @default_score),
do: Map.put(scores, name, score)
def remove_player(scores, name),
do: Map.delete(scores, name)
def reset_score(scores, name),
do: Map.put(scores, name, @default_score)
def update_score(scores, name, score),
do: Map.update(scores, name, score, &(&1 + score))
def get_players(scores),
do: Map.keys(scores)
end

28
elixir/high-score/mix.exs Normal file
View File

@@ -0,0 +1,28 @@
defmodule HighScore.MixProject do
use Mix.Project
def project do
[
app: :high_score,
version: "0.1.0",
# elixir: "~> 1.10",
start_permanent: Mix.env() == :prod,
deps: deps()
]
end
# Run "mix help compile.app" to learn about applications.
def application do
[
extra_applications: [:logger]
]
end
# Run "mix help deps" to learn about dependencies.
defp deps do
[
# {:dep_from_hexpm, "~> 0.3.0"},
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
]
end
end

View File

@@ -0,0 +1,186 @@
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

View File

@@ -0,0 +1,2 @@
ExUnit.start()
ExUnit.configure(exclude: :pending, trace: true, seed: 0)