GuessingGame completed
This commit is contained in:
parent
dc082ec91f
commit
eaf2ddb913
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"authors": [
|
||||||
|
"neenjaw"
|
||||||
|
],
|
||||||
|
"contributors": [
|
||||||
|
"angelikatyborska"
|
||||||
|
],
|
||||||
|
"files": {
|
||||||
|
"solution": [
|
||||||
|
"lib/guessing_game.ex"
|
||||||
|
],
|
||||||
|
"test": [
|
||||||
|
"test/guessing_game_test.exs"
|
||||||
|
],
|
||||||
|
"exemplar": [
|
||||||
|
".meta/exemplar.ex"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"language_versions": ">=1.10",
|
||||||
|
"forked_from": [
|
||||||
|
"fsharp/guessing-game"
|
||||||
|
],
|
||||||
|
"blurb": "Learn about multiple clause functions, guards, and default arguments by implementing a simple game in which the player needs to guess a secret number."
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
{"track":"elixir","exercise":"guessing-game","id":"fd0a7283b9544f48a3ac133d8ea3b420","url":"https://exercism.org/tracks/elixir/exercises/guessing-game","handle":"negrienko","is_requester":true,"auto_approve":false}
|
|
@ -0,0 +1,4 @@
|
||||||
|
# Used by "mix format"
|
||||||
|
[
|
||||||
|
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
|
||||||
|
]
|
|
@ -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").
|
||||||
|
multiple_clause_functions-*.tar
|
||||||
|
|
|
@ -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/guessing_game.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.
|
|
@ -0,0 +1,33 @@
|
||||||
|
# Hints
|
||||||
|
|
||||||
|
## General
|
||||||
|
|
||||||
|
- In Elixir's ['Getting Started Guide'][guide] there is a nice refresher about named functions.
|
||||||
|
|
||||||
|
## 1. Make the response when the guess matches the secret number
|
||||||
|
|
||||||
|
- You can use a [guard][guard] to check if the numbers are the same with `===/2`.
|
||||||
|
|
||||||
|
## 2. Make the response when the guess is greater than the secret number
|
||||||
|
|
||||||
|
- You can add a [function clause][multiple-fn-clauses] and [guards][guard] to check if the guess is greater `>/2` than the secret number.
|
||||||
|
|
||||||
|
## 3. Make the response when the guess is less than the secret number
|
||||||
|
|
||||||
|
- You can add a [function clause][multiple-fn-clauses] and [guards][guard] to check if the guess is less than `</2` the secret number.
|
||||||
|
|
||||||
|
## 4. Make the responses when the guess is one more or one less than the secret number
|
||||||
|
|
||||||
|
- You can add a [function clause][multiple-fn-clauses] and [guards][guard] to check if the guess is one less or one more than the secret number.
|
||||||
|
- Guards expressions can use `and/2`, or `or/2` to combine boolean expressions.
|
||||||
|
- Pay attention to the order of the function clauses.
|
||||||
|
|
||||||
|
## 5. Make the response when there is no guess
|
||||||
|
|
||||||
|
- You can make use of a [default argument][default-arg] for a guess. The default value does not have to be an integer.
|
||||||
|
- Use a function header before all the other function clauses to define the default argument.
|
||||||
|
|
||||||
|
[default-arg]: https://elixir-lang.org/getting-started/modules-and-functions.html#default-arguments
|
||||||
|
[guard]: https://hexdocs.pm/elixir/Kernel.html#guards
|
||||||
|
[guide]: https://elixir-lang.org/getting-started/modules-and-functions.html#named-functions
|
||||||
|
[multiple-fn-clauses]: https://elixir-lang.org/getting-started/modules-and-functions.html#named-functions
|
|
@ -0,0 +1,131 @@
|
||||||
|
# Guessing Game
|
||||||
|
|
||||||
|
Welcome to Guessing Game 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
|
||||||
|
|
||||||
|
## Multiple Clause Functions
|
||||||
|
|
||||||
|
Elixir facilitates **Open-Closed Principle** practices by allowing functions to have multiple clauses, so instead of sprawling and hard-coded control-logic, pointed functions can be written to add/remove behavior easily.
|
||||||
|
|
||||||
|
Elixir offers _multiple function clauses_ and _guards_ to write:
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
def number(n) when n == 7 do
|
||||||
|
"Awesome, that's my favorite"
|
||||||
|
end
|
||||||
|
def number(_n) do
|
||||||
|
"That's not my favorite"
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
At run-time, Elixir will test, from top to bottom of the source file, which function clause to invoke.
|
||||||
|
|
||||||
|
Variables that are unused should be prefixed with an underscore.
|
||||||
|
|
||||||
|
## Guards
|
||||||
|
|
||||||
|
Guards are used to prevent Elixir from invoking functions based on evaluation of the arguments by guard functions. Guards begin with the `when` keyword, followed by a boolean expression. Guard functions are special functions which:
|
||||||
|
|
||||||
|
- Must be pure and not mutate any global states.
|
||||||
|
- Must return strict `true` or `false` values.
|
||||||
|
|
||||||
|
A list of common guards can be found in the [Elixir documentation][kernel-guards]. It includes type checks, basic arithmetic, comparisons, and strictly boolean operators.
|
||||||
|
|
||||||
|
## Default Arguments
|
||||||
|
|
||||||
|
Functions may declare default values for one or more arguments. Let's consider this function:
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
def number(n \\ 13), do: "That's not my favorite"
|
||||||
|
```
|
||||||
|
|
||||||
|
When compiled, Elixir creates a function definition for `number/0` (no arguments), and `number/1` (one argument).
|
||||||
|
|
||||||
|
If more than one argument has default values, the default values will be applied to the function from left to right to fill in for missing arguments.
|
||||||
|
|
||||||
|
If the function has more than one clause, the default arguments should be defined in a function header (a function without a body) before the function clauses:
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
def number(n \\ 13)
|
||||||
|
def number(n) when n < 10, do: "Dream bigger!"
|
||||||
|
def number(n) when n > 100, do: "Not that big..."
|
||||||
|
```
|
||||||
|
|
||||||
|
[kernel-guards]: https://hexdocs.pm/elixir/Kernel.html#guards
|
||||||
|
|
||||||
|
## Instructions
|
||||||
|
|
||||||
|
You are creating a trivial online game where a friend can guess a secret number. You want to give some feedback, but not give away the answer with a guess. You need to devise a function to provide different responses depending on how the guess relates to the secret number.
|
||||||
|
|
||||||
|
| Condition | Response |
|
||||||
|
| ------------------------------------------------------------- | -------------- |
|
||||||
|
| When the guess matches the secret number | "Correct" |
|
||||||
|
| When the guess is one more or one less than the secret number | "So close" |
|
||||||
|
| When the guess is greater than the secret number | "Too high" |
|
||||||
|
| When the guess is less than the secret number | "Too low" |
|
||||||
|
| When a guess isn't made | "Make a guess" |
|
||||||
|
|
||||||
|
All guesses and secret numbers are integer numbers.
|
||||||
|
|
||||||
|
## 1. Make the response when the guess matches the secret number
|
||||||
|
|
||||||
|
Implement the `compare/2` function which takes two arguments, `secret_number` and `guess`, which are both integers.
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
GuessingGame.compare(5, 5)
|
||||||
|
# => "Correct"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. Make the response when the guess is greater than the secret number
|
||||||
|
|
||||||
|
Modify the `compare` function to respond to guesses that are higher than the secret number.
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
GuessingGame.compare(5, 8)
|
||||||
|
# => "Too high"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. Make the response when the guess is less than the secret number
|
||||||
|
|
||||||
|
Modify the `compare` function to respond to guesses that are lower than the secret number.
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
GuessingGame.compare(5, 2)
|
||||||
|
# => "Too low"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. Make the responses when the guess is one more or one less than the secret number
|
||||||
|
|
||||||
|
Modify the `compare` function to respond to guesses that are close to the secret number.
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
GuessingGame.compare(5, 6)
|
||||||
|
# => "So close"
|
||||||
|
GuessingGame.compare(5, 4)
|
||||||
|
# => "So close"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. Make the response when there is no guess
|
||||||
|
|
||||||
|
Modify the `compare` function to respond to a lack of guess.
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
GuessingGame.compare(5)
|
||||||
|
# => "Make a guess"
|
||||||
|
|
||||||
|
GuessingGame.compare(5, :no_guess)
|
||||||
|
# => "Make a guess"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Source
|
||||||
|
|
||||||
|
### Created by
|
||||||
|
|
||||||
|
- @neenjaw
|
||||||
|
|
||||||
|
### Contributed to by
|
||||||
|
|
||||||
|
- @angelikatyborska
|
|
@ -0,0 +1,8 @@
|
||||||
|
defmodule GuessingGame do
|
||||||
|
def compare(secret_number, guess \\ :no_guess)
|
||||||
|
def compare(_secret_number, :no_guess), do: "Make a guess"
|
||||||
|
def compare(secret_number, guess) when secret_number == guess, do: "Correct"
|
||||||
|
def compare(secret_number, guess) when abs(secret_number - guess) == 1, do: "So close"
|
||||||
|
def compare(secret_number, guess) when guess > secret_number, do: "Too high"
|
||||||
|
def compare(secret_number, guess) when guess < secret_number, do: "Too low"
|
||||||
|
end
|
|
@ -0,0 +1,28 @@
|
||||||
|
defmodule GuessingGame.MixProject do
|
||||||
|
use Mix.Project
|
||||||
|
|
||||||
|
def project do
|
||||||
|
[
|
||||||
|
app: :guessing_game,
|
||||||
|
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
|
|
@ -0,0 +1,38 @@
|
||||||
|
defmodule GuessingGameTest do
|
||||||
|
use ExUnit.Case
|
||||||
|
|
||||||
|
@tag task_id: 1
|
||||||
|
test "correct when the guessed number equals secret" do
|
||||||
|
assert GuessingGame.compare(7, 7) == "Correct"
|
||||||
|
end
|
||||||
|
|
||||||
|
@tag task_id: 2
|
||||||
|
test "too high when guessed number is greater than the secret" do
|
||||||
|
assert GuessingGame.compare(9, 18) == "Too high"
|
||||||
|
end
|
||||||
|
|
||||||
|
@tag task_id: 3
|
||||||
|
test "too low when guessed number is less than the secret" do
|
||||||
|
assert GuessingGame.compare(42, 30) == "Too low"
|
||||||
|
end
|
||||||
|
|
||||||
|
@tag task_id: 4
|
||||||
|
test "so close when guess differs from secret by -1" do
|
||||||
|
assert GuessingGame.compare(64, 63) == "So close"
|
||||||
|
end
|
||||||
|
|
||||||
|
@tag task_id: 4
|
||||||
|
test "so close when guess differs from secret by +1" do
|
||||||
|
assert GuessingGame.compare(52, 53) == "So close"
|
||||||
|
end
|
||||||
|
|
||||||
|
@tag task_id: 5
|
||||||
|
test "when no guess is supplied, ask the player to make a guess" do
|
||||||
|
assert GuessingGame.compare(15) == "Make a guess"
|
||||||
|
end
|
||||||
|
|
||||||
|
@tag task_id: 5
|
||||||
|
test "when the atom :no_guess is supplied, ask the player to make a guess" do
|
||||||
|
assert GuessingGame.compare(16, :no_guess) == "Make a guess"
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,2 @@
|
||||||
|
ExUnit.start()
|
||||||
|
ExUnit.configure(exclude: :pending, trace: true, seed: 0)
|
Loading…
Reference in New Issue