need_for_speed

This commit is contained in:
Danil Negrienko 2024-03-07 00:52:53 -05:00
parent c62233f40f
commit 0c15aedc09
13 changed files with 499 additions and 0 deletions

View File

@ -0,0 +1,28 @@
{
"authors": [
"angelikatyborska"
],
"contributors": [
"neenjaw"
],
"files": {
"solution": [
"lib/need_for_speed.ex"
],
"test": [
"test/need_for_speed_test.exs"
],
"exemplar": [
".meta/exemplar.ex"
],
"editor": [
"lib/need_for_speed/race.ex",
"lib/need_for_speed/remote_control_car.ex"
]
},
"language_versions": ">=1.10",
"forked_from": [
"csharp/need-for-speed"
],
"blurb": "Learn about reusing code with alias and import by racing remote controlled cars."
}

View File

@ -0,0 +1 @@
{"track":"elixir","exercise":"need-for-speed","id":"082ffb90c57f4558b617e4fd2b89ca7a","url":"https://exercism.org/tracks/elixir/exercises/need-for-speed","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/need-for-speed/.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").
structs-*.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/need_for_speed.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,31 @@
# Hints
## General
- Read about [`alias` and `import`][getting-started] in the Getting Started guide.
- Read about [module composition][elixir-school-module-composition] on elixirschool.com.
## 1. Fix compilation error `Race.__struct__/0 is undefined`
- An alias can be created with the [`alias`][alias] special form.
## 2. Fix compilation error `Car.__struct__/0 is undefined`
- An alias can be created with the [`alias`][alias] special form.
- The `alias` special form accepts an `:as` option.
## 3. Fix compilation error `undefined function puts/1`
- Functions can be imported with the [`import`][import] special form.
- The [`import`][import] special form accepts an `:only` option. Its value should be a keyword list with function name as keys and function arities as values.
## 4. Fix compilation error `undefined function default_color/0`
- Functions can be imported with the [`import`][import] special form.
- The [`import`][import] special form accepts an `:except` option. Its value should be a keyword list with function name as keys and function arities as values.
[alias]: https://hexdocs.pm/elixir/Kernel.SpecialForms.html#alias/2
[import]: https://hexdocs.pm/elixir/Kernel.SpecialForms.html#import/2
[elixir-school-module-composition]: https://elixirschool.com/en/lessons/basics/modules/#composition
[getting-started]: https://hexdocs.pm/elixir/alias-require-and-import.html

View File

@ -0,0 +1,71 @@
# Need For Speed
Welcome to Need For Speed 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
## Alias
To share code between different Elixir modules within the same project, you need to reference the outside module by its full name. But what if that name is too long or confusing?
The special form `alias` allows you to shorten or change the name by which you reference an outside module. When used without any arguments, it trims down the module name to its last segment, e.g. `MyApp.Logger.Settings` becomes `Settings`. A custom name can be specified with the `:as` option.
Usually aliases are added at the beginning of the module definition.
```elixir
defmodule Square do
alias Integer, as: I
def area(a), do: I.pow(a, 2)
end
```
## Import
The special form `import` allows you to use functions from an outside module without using the module's name.
Importing a whole outside module might create conflicts with existing local functions. To avoid this, two options are available: `:except` and `:only`. Both expect a keyword list, where the key is the function name, and the value is the function's arity.
Usually imports are added at the beginning of the module definition.
```elixir
defmodule Square do
import Integer, only: [pow: 2]
def area(a), do: pow(a, 2)
end
```
## Instructions
That remote controlled car that you bought recently has turned into a whole new hobby. You have been organizing remote control car races.
You were almost finished writing a program that would allow to run race simulations when your cat jumped at your keyboard and deleted a few lines of code. Now your program doesn't compile anymore...
## 1. Fix compilation error `Race.__struct__/0 is undefined`
Add an alias so that the module `NeedForSpeed.Race` can be referenced by the shorter name `Race`.
## 2. Fix compilation error `Car.__struct__/0 is undefined`
Add an alias so that the module `NeedForSpeed.RemoteControlCar` can be referenced by the shorter name `Car`.
## 3. Fix compilation error `undefined function puts/1`
The function `puts/1` comes from the module `IO`. Import it to be able to use it without referencing the module.
## 4. Fix compilation error `undefined function default_color/0`
The functions `default_color/0`, `red/0`, `cyan/0`, and `green/0` all come from the module `IO.ANSI`. You're planning to add support for other car colors, so you want to import the whole module. Unfortunately, the function `color/1` from the module `IO.ANSI` conflicts with one of your local functions. Import the whole `IO.ANSI` module except that one function.
## Source
### Created by
- @angelikatyborska
### Contributed to by
- @neenjaw

View File

@ -0,0 +1,43 @@
defmodule NeedForSpeed do
alias NeedForSpeed.Race
alias NeedForSpeed.RemoteControlCar, as: Car
import IO, only: [puts: 1]
import IO.ANSI, except: [color: 1]
# Add missing aliases and imports here.
# Do not edit the code below.
def print_race(%Race{} = race) do
puts("""
🏁 #{race.title} 🏁
Status: #{Race.display_status(race)}
Distance: #{Race.display_distance(race)}
Contestants:
""")
race.cars
|> Enum.sort_by(&(-1 * &1.distance_driven_in_meters))
|> Enum.with_index()
|> Enum.each(fn {car, index} -> print_car(car, index + 1) end)
end
defp print_car(%Car{} = car, index) do
color = color(car)
puts("""
#{index}. #{color}#{car.nickname}#{default_color()}
Distance: #{Car.display_distance(car)}
Battery: #{Car.display_battery(car)}
""")
end
defp color(%Car{} = car) do
case car.color do
:red -> red()
:blue -> cyan()
:green -> green()
end
end
end

View File

@ -0,0 +1,24 @@
defmodule NeedForSpeed.Race do
defstruct [
:title,
:total_distance_in_meters,
cars: []
]
def display_status(%NeedForSpeed.Race{} = race) do
cond do
Enum.any?(race.cars, &(&1.distance_driven_in_meters >= race.total_distance_in_meters)) ->
"Finished"
Enum.any?(race.cars, &(&1.distance_driven_in_meters > 0)) ->
"In Progress"
true ->
"Not Started"
end
end
def display_distance(%NeedForSpeed.Race{total_distance_in_meters: d}) do
"#{d} meters"
end
end

View File

@ -0,0 +1,24 @@
defmodule NeedForSpeed.RemoteControlCar do
defstruct [
:nickname,
:color,
battery_percentage: 100,
distance_driven_in_meters: 0
]
def new(color, nickname) when color in [:red, :blue, :green] do
%NeedForSpeed.RemoteControlCar{nickname: nickname, color: color}
end
def display_distance(%NeedForSpeed.RemoteControlCar{distance_driven_in_meters: d}) do
"#{d} meters"
end
def display_battery(%NeedForSpeed.RemoteControlCar{battery_percentage: 0}) do
"Battery empty"
end
def display_battery(%NeedForSpeed.RemoteControlCar{battery_percentage: b}) do
"Battery at #{b}%"
end
end

View File

@ -0,0 +1,28 @@
defmodule RemoteControlCar.MixProject do
use Mix.Project
def project do
[
app: :need_for_speed,
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,144 @@
defmodule NeedForSpeedTest do
use ExUnit.Case
import ExUnit.CaptureIO
describe "print_race" do
@tag task_id: nil
test "prints a race with no cars" do
race = %NeedForSpeed.Race{
title: "Need For Speed 2021",
total_distance_in_meters: 350,
cars: []
}
io = capture_io(fn -> NeedForSpeed.print_race(race) end)
assert io == """
🏁 Need For Speed 2021 🏁
Status: Not Started
Distance: 350 meters
Contestants:
"""
end
@tag task_id: nil
test "prints a race with a red car" do
car = NeedForSpeed.RemoteControlCar.new(:red, "The Fox")
race = %NeedForSpeed.Race{
title: "Need For Speed 2021",
total_distance_in_meters: 500,
cars: [car]
}
io = capture_io(fn -> NeedForSpeed.print_race(race) end)
assert io == """
🏁 Need For Speed 2021 🏁
Status: Not Started
Distance: 500 meters
Contestants:
1. #{IO.ANSI.red()}The Fox#{IO.ANSI.default_color()}
Distance: 0 meters
Battery: Battery at 100%
"""
end
@tag task_id: nil
test "prints a race with a blue car" do
car = NeedForSpeed.RemoteControlCar.new(:blue, "The Hurricane")
race = %NeedForSpeed.Race{
title: "Need For Speed 2021",
total_distance_in_meters: 500,
cars: [car]
}
io = capture_io(fn -> NeedForSpeed.print_race(race) end)
assert io == """
🏁 Need For Speed 2021 🏁
Status: Not Started
Distance: 500 meters
Contestants:
1. #{IO.ANSI.cyan()}The Hurricane#{IO.ANSI.default_color()}
Distance: 0 meters
Battery: Battery at 100%
"""
end
@tag task_id: nil
test "prints a race with a green car" do
car = NeedForSpeed.RemoteControlCar.new(:green, "The Grasshopper")
race = %NeedForSpeed.Race{
title: "Need For Speed 2021",
total_distance_in_meters: 777,
cars: [car]
}
io = capture_io(fn -> NeedForSpeed.print_race(race) end)
assert io == """
🏁 Need For Speed 2021 🏁
Status: Not Started
Distance: 777 meters
Contestants:
1. #{IO.ANSI.green()}The Grasshopper#{IO.ANSI.default_color()}
Distance: 0 meters
Battery: Battery at 100%
"""
end
@tag task_id: nil
test "prints a race in progress with many cars" do
red_car = NeedForSpeed.RemoteControlCar.new(:red, "The Fox")
blue_car = NeedForSpeed.RemoteControlCar.new(:blue, "The Hurricane")
green_car = NeedForSpeed.RemoteControlCar.new(:green, "The Grasshopper")
red_car = %{red_car | distance_driven_in_meters: 305, battery_percentage: 70}
blue_car = %{blue_car | distance_driven_in_meters: 500, battery_percentage: 62}
green_car = %{green_car | distance_driven_in_meters: 235, battery_percentage: 60}
race = %NeedForSpeed.Race{
title: "Need For Speed 2021",
total_distance_in_meters: 1000,
cars: [red_car, blue_car, green_car]
}
io = capture_io(fn -> NeedForSpeed.print_race(race) end)
assert io == """
🏁 Need For Speed 2021 🏁
Status: In Progress
Distance: 1000 meters
Contestants:
1. #{IO.ANSI.cyan()}The Hurricane#{IO.ANSI.default_color()}
Distance: 500 meters
Battery: Battery at 62%
2. #{IO.ANSI.red()}The Fox#{IO.ANSI.default_color()}
Distance: 305 meters
Battery: Battery at 70%
3. #{IO.ANSI.green()}The Grasshopper#{IO.ANSI.default_color()}
Distance: 235 meters
Battery: Battery at 60%
"""
end
end
end

View File

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