diff --git a/elixir/pacman-rules/.exercism/config.json b/elixir/pacman-rules/.exercism/config.json new file mode 100644 index 0000000..91ca433 --- /dev/null +++ b/elixir/pacman-rules/.exercism/config.json @@ -0,0 +1,21 @@ +{ + "blurb": "Learn about booleans by implementing the rules of the Pac-Man game.", + "authors": [ + "neenjaw" + ], + "contributors": [ + "Cohen-Carlisle" + ], + "files": { + "solution": [ + "lib/rules.ex" + ], + "test": [ + "test/rules_test.exs" + ], + "exemplar": [ + ".meta/exemplar.ex" + ] + }, + "language_versions": ">=1.10" +} diff --git a/elixir/pacman-rules/.exercism/metadata.json b/elixir/pacman-rules/.exercism/metadata.json new file mode 100644 index 0000000..be86cfd --- /dev/null +++ b/elixir/pacman-rules/.exercism/metadata.json @@ -0,0 +1 @@ +{"track":"elixir","exercise":"pacman-rules","id":"0e1b851e229a4567b01888d36d142a83","url":"https://exercism.org/tracks/elixir/exercises/pacman-rules","handle":"JBiason","is_requester":true,"auto_approve":false} \ No newline at end of file diff --git a/elixir/pacman-rules/.gitignore b/elixir/pacman-rules/.gitignore new file mode 100644 index 0000000..5e50eeb --- /dev/null +++ b/elixir/pacman-rules/.gitignore @@ -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"). +booleans-*.tar + diff --git a/elixir/pacman-rules/HELP.md b/elixir/pacman-rules/HELP.md new file mode 100644 index 0000000..8685c69 --- /dev/null +++ b/elixir/pacman-rules/HELP.md @@ -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/.exs:LINENUM` - runs only a single test, the test from `.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/rules.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) +- [Exercism's support channel on gitter](https://gitter.im/exercism/support) +- 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. +If you can't find what you're looking for in the documentation, feel free to ask help in the Exercism's BEAM [gitter channel](https://gitter.im/exercism/xerlang). \ No newline at end of file diff --git a/elixir/pacman-rules/HINTS.md b/elixir/pacman-rules/HINTS.md new file mode 100644 index 0000000..9d7e8f1 --- /dev/null +++ b/elixir/pacman-rules/HINTS.md @@ -0,0 +1,33 @@ +# Hints + +## General + +- Don't worry about how the arguments are derived, just focus on combining the arguments to return the intended result. + +## 1. Define if pac-man can eat a ghost + +- You need to define a [named function][named-function] with 2 arguments. The first argument is a [boolean][boolean] value, whether pac-man has a power pellet active. The second argument is a [boolean][boolean] value, whether pac-man is touching a ghost. +- The function must return a [boolean][boolean] value. +- You can use the [boolean][boolean] operator [`and/2`][boolean-function] to combine the arguments for a result. + +## 2. Define if pac-man scores + +- You need to define a [named function][named-function] with 2 arguments. The first argument is a [boolean][boolean] value, whether pac-man is touching a power pellet. The second argument is a [boolean][boolean] value, whether pac-man is touching a dot. +- The function must return a [boolean][boolean] value. +- You can use the [boolean][boolean] operator [`or/2`][boolean-function] to combine the arguments for a result. + +## 3. Define if pac-man loses + +- You need to define a [named function][named-function] with 2 arguments. The first argument is a [boolean][boolean] value, whether pac-man has a power pellet active. The second argument is a [boolean][boolean] value, whether pac-man is touching a ghost. +- The function must return a [boolean][boolean] value. +- You can use the [boolean][boolean] operators [`and/2`][boolean-function] and [`not/1`][boolean-function] to combine the arguments for a result. + +## 4. Define if pac-man wins + +- You need to define a [named function][named-function] with 3 arguments. The second argument is a [boolean][boolean] value, whether pac-man has eaten all of the dots. The first argument is a [boolean][boolean] value, whether pac-man has a power pellet active. The second argument is a [boolean][boolean] value, whether pac-man is touching a ghost. +- The function must return a [boolean][boolean] value. +- You can use the [boolean][boolean] operators [`and/2`][boolean-function] and [`not/1`][boolean-function] to combine the arguments and results of invoked functions. + +[named-function]: https://elixir-lang.org/getting-started/modules-and-functions.html#named-functions +[boolean]: https://elixir-lang.org/getting-started/basic-types.html#booleans +[boolean-function]: https://elixir-lang.org/getting-started/basic-operators.html \ No newline at end of file diff --git a/elixir/pacman-rules/README.md b/elixir/pacman-rules/README.md new file mode 100644 index 0000000..8f076fa --- /dev/null +++ b/elixir/pacman-rules/README.md @@ -0,0 +1,91 @@ +# Pacman Rules + +Welcome to Pacman Rules 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 + +## Booleans + +Elixir represents true and false values with the boolean type. There are only two values: _true_ and _false_. These values can be bound to a variable: + +```elixir +true_variable = true +false_variable = false +``` + +We can evaluate strict boolean expressions using the `and/2`, `or/2`, and `not/1` operator functions. + +```elixir +true_variable = true and true +false_variable = true and false + +true_variable = false or true +false_variable = false or false + +true_variable = not false +false_variable = not true +``` + +When writing a function that returns a boolean value, it is idiomatic to end the function name with a `?`. + +```elixir +def either_true?(a, b) do + a or b +end +``` + +## Instructions + +In this exercise, you need to translate some rules from the classic game Pac-Man into Elixir functions. + +You have four rules to translate, all related to the game states. + +> Don't worry about how the arguments are derived, just focus on combining the arguments to return the intended result. + +## 1. Define if Pac-Man eats a ghost + +Define the `Rules.eat_ghost?/2` function that takes two arguments (_if Pac-Man has a power pellet active_ and _if Pac-Man is touching a ghost_) and returns a boolean value if Pac-Man is able to eat the ghost. The function should return true only if Pac-Man has a power pellet active and is touching a ghost. + +```elixir +Rules.eat_ghost?(false, true) +# => false +``` + +## 2. Define if Pac-Man scores + +Define the `Rules.score?/2` function that takes two arguments (_if Pac-Man is touching a power pellet_ and _if Pac-Man is touching a dot_) and returns a boolean value if Pac-Man scored. The function should return true if Pac-Man is touching a power pellet or a dot. + +```elixir +Rules.score?(true, true) +# => true +``` + +## 3. Define if Pac-Man loses + +Define the `Rules.lose?/2` function that takes two arguments (_if Pac-Man has a power pellet active_ and _if Pac-Man is touching a ghost_) and returns a boolean value if Pac-Man loses. The function should return true if Pac-Man is touching a ghost and does not have a power pellet active. + +```elixir +Rules.lose?(false, true) +# => true +``` + +## 4. Define if Pac-Man wins + +Define the `Rules.win?/3` function that takes three arguments (_if Pac-Man has eaten all of the dots_, _if Pac-Man has a power pellet active_, and _if Pac-Man is touching a ghost_) and returns a boolean value if Pac-Man wins. The function should return true if Pac-Man has eaten all of the dots and has not lost based on the arguments defined in part 3. + +```elixir +Rules.win?(false, true, false) +# => false +``` + +## Source + +### Created by + +- @neenjaw + +### Contributed to by + +- @Cohen-Carlisle \ No newline at end of file diff --git a/elixir/pacman-rules/lib/rules.ex b/elixir/pacman-rules/lib/rules.ex new file mode 100644 index 0000000..abc7171 --- /dev/null +++ b/elixir/pacman-rules/lib/rules.ex @@ -0,0 +1,17 @@ +defmodule Rules do + def eat_ghost?(power_pellet_active, touching_ghost) do + power_pellet_active && touching_ghost + end + + def score?(touching_power_pellet, touching_dot) do + touching_power_pellet || touching_dot + end + + def lose?(power_pellet_active, touching_ghost) do + ! power_pellet_active && touching_ghost + end + + def win?(has_eaten_all_dots, power_pellet_active, touching_ghost) do + has_eaten_all_dots && ! lose?(power_pellet_active, touching_ghost) + end +end diff --git a/elixir/pacman-rules/mix.exs b/elixir/pacman-rules/mix.exs new file mode 100644 index 0000000..f592497 --- /dev/null +++ b/elixir/pacman-rules/mix.exs @@ -0,0 +1,28 @@ +defmodule Rules.MixProject do + use Mix.Project + + def project do + [ + app: :pacman_rules, + 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 diff --git a/elixir/pacman-rules/test/rules_test.exs b/elixir/pacman-rules/test/rules_test.exs new file mode 100644 index 0000000..f6c9536 --- /dev/null +++ b/elixir/pacman-rules/test/rules_test.exs @@ -0,0 +1,71 @@ +defmodule RulesTest do + use ExUnit.Case + + describe "eat_ghost?/2" do + @tag task_id: 1 + test "ghost gets eaten" do + assert Rules.eat_ghost?(true, true) + end + + @tag task_id: 1 + test "ghost does not get eaten because no power pellet active" do + refute Rules.eat_ghost?(false, true) + end + + @tag task_id: 1 + test "ghost does not get eaten because not touching ghost" do + refute Rules.eat_ghost?(true, false) + end + end + + describe "score?/2" do + @tag task_id: 2 + test "score when eating dot" do + assert Rules.score?(false, true) + end + + @tag task_id: 2 + test "score when eating power pellet" do + assert Rules.score?(true, false) + end + + @tag task_id: 2 + test "no score when nothing eaten" do + refute Rules.score?(false, false) + end + end + + describe "lose?/2" do + @tag task_id: 3 + test "lose if touching a ghost without a power pellet active" do + assert Rules.lose?(false, true) + end + + @tag task_id: 3 + test "don't lose if touching a ghost with a power pellet active" do + refute Rules.lose?(true, true) + end + + @tag task_id: 3 + test "don't lose if not touching a ghost" do + refute Rules.lose?(true, false) + end + end + + describe "win?/3" do + @tag task_id: 4 + test "win if all dots eaten" do + assert Rules.win?(true, false, false) + end + + @tag task_id: 4 + test "don't win if all dots eaten, but touching a ghost" do + refute Rules.win?(true, false, true) + end + + @tag task_id: 4 + test "win if all dots eaten and touching a ghost with a power pellet active" do + assert Rules.win?(true, true, true) + end + end +end diff --git a/elixir/pacman-rules/test/test_helper.exs b/elixir/pacman-rules/test/test_helper.exs new file mode 100644 index 0000000..e8677a3 --- /dev/null +++ b/elixir/pacman-rules/test/test_helper.exs @@ -0,0 +1,2 @@ +ExUnit.start() +ExUnit.configure(exclude: :pending, trace: true, seed: 0)