GuessingGame completed

This commit is contained in:
Danil Negrienko 2023-12-17 01:14:53 -05:00
parent dc082ec91f
commit eaf2ddb913
11 changed files with 368 additions and 0 deletions

View File

@ -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."
}

View File

@ -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}

View File

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

24
elixir/guessing-game/.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").
multiple_clause_functions-*.tar

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/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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

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