System designed to control exploration rovers.
A set of exploration rovers has been sent by NASA to Mars and will land on a ground. This ground, must be explored by the rovers so that its built-in cameras can have a complete view of the area and send the images back to Earth.
The position and direction of the exploration rover are represented by a combination of x-y coordinates and a letter representing the cardinal direction in which the rover points, following the Wind Rose Compass
.
The highground is divided into a grid to simplify navigation. An example of a position would be (0, 0, N), which indicates that the rover is in the lower left corner and pointing north.
To control the rovers, NASA sends a simple sequence of letters. The letters are "L" (rotate the rover 90 degrees to Left), "R" (rotate the rover 90 degrees to Right) and "M" (Move the rover keeping it's direction).
In the grid, the point north of (x, y) is always (x, y + 1) and the point east of (x, y) is always (x + 1, y). I think you got the logic.
If you didn't understand for the first time do not feel dumd, I had to read it a few times and to draw it to understand. 😂
NASA engineers creates a file with all the exploration rovers instructions. This file is read by the system and is sent to exploration rovers.
This files also has to include at the first line the ground size. This is important to the system be able to predict if any rover will be off the ground after the commands are executed. If that's the case, the commands will not be sent to the specific rover and a friendly error will be outputed instead of the new rover coordinates.
It seems a little complex, but don't worry if you make some mistakes when writing this file, the system can handle most of them. But you will have a chance to review the instructions before confirming the submission.
This confirmation is important because the commands after being sent take about 8 minutes to reach the rovers if you are exploring Mars.
The first line is the ground size, the following lines are the exploration rover initial position and followed by another line with the commands that will be performed.
Example:
3 8
0 0 N
MMM
3 8 N
LMMM
Explaining the example:
3 8 (ground size)
0 0 N (Rover 1 initial position and direction)
MMM (Rover 1 commands to be performed)
3 8 N (Rover 2 initial position and direction)
LMMM (Rover 2 commands to be performed)
... (And so on, you can add many rovers as you want, the system can take care of it)
The output has in each line the new rover predicted position and direction (remember that it takes some time to the rovers to receive the commads, and the output will not wait for it).
Example:
0 3 N
0 8 W
Explaining the example:
0 3 N (Rover 1 new predicted position and direction)
0 8 W (Rover 2 new predicted position and direction)
... (And so on)
To interact with the system, for now we have a terminal interface but other ways can be added later (see the architecture decisions section). You can run using your favorite linux terminal. Maybe it works on Windows and Mac too, but has not been tested.
The predefined commands input file from the option 2 is the only one that is 100% valid. The others will return some errors. You can play it around creating new files at this folder, it will be automatically detected, just don't forget to adjust the unit tests after placing a new file to this folder. To avoid breaking the tests, you can place your file elsewhere and use the last option informing the file path.
Most of the architecture decisions was followed by conventions from Clean Architecture.
Clean architecture helps to isolate the business logic from the rest and let us developers to focus on what really matters, the business.
There is a clear separation between these two points of view in the project folder:
lib/nasa_exploration_rovers_control
is for the business logiclib/nasa_exploration_rovers_control_terminal_interface
is for the terminal interface, our delivery mechanism. Is the way our users will interact with the business logic
At the business logic part, the file lib/nasa_exploration_rovers_control.ex
is responsible to expose all the system use cases according to the business view for the product. This layer is known as our bounded context. It defines the boundaries, this should be the only file called by the delivery mechanisms.
At lib/nasa_exploration_rovers_control/interactors
we can find all the use cases and it's rules.
lib/exploration_rover
is our entity, that represents a real Exploration Rover from the real world.
Another structure that was made, following another patterns, was the folder lib/nasa_exploration_rovers_control/celestial_bodies
that contains all the specific celestial bodies business rules, like the rule that validates that the Mars
ground is rectangular.
There is also another folder that worth to mention that is lib/mix/tasks
. This folder just followed the Elixir conventions to create an automated task to start some things. We are using for now just to start our terminal interface.
- Explore Celestial Body Using Commands From File
- Interpret Exploration Commands Input File
- Execute Exploration Rover Commands
- Rotate Exploration Rover
- Move Exploration Rover
You can see all of them with their docs and some examples in the file lib/nasa_exploration_rovers_control.ex
.
Exploration Rover entity, it represents a real exploration rover that is exploring another planet or satellite.
It has three attributes:
- position (required): A tuble with x and y axis.
- direction (required): A direction according to the Wind Rose Compass represented by a single character string. Valid values are N, S, W, E.
- commands: A list of commands to be performed by rover. Valid values for each command in the list are L (left), R (right) or M (move).
It also has a bunch of validations to prevent invalid values to be placed in these attributes.
- A structure of celestial bodies was created to keep some specific logics. For now there's just
Mars
, but other celestial bodies can be easily added in the future. After all, after conquering Mars, we won't want to stop here, will we? Mars
also has a specific characteristic of having its grounds to be explored as being rectangular highlands, so a specific Mars validation has been added to not allow square grounds.- Some fake modules was created to be able to mock some Elixir and Erlang modules like
System
and:timer
. The decision of which module will be used is made by the environment (seeconfig
folder files). The default is to use the language modules, but this default is overwriten at the test environment.
This project requires Elixir version 1.11 and nothing more. If you have an older version of Elixir you can change it at mix.exs
and probably it will still work fine.
The system was tested on Linux, but probably will work on MacOS and Windows too.
- Install the project dependencies. The project has 5 dependencies:
mock
that is being used for mocking at tests;ex_doc
that was used to generate the documentation;credo
used as code linter;excoveralls
for test coverage checks;junit_formatter
used for properly store CI artifacts.
$ mix deps.get
- Start the Terminal Interface and follow the instructions.
$ mix start_terminal_interface
More information can be obtained by running:
$ mix help start_terminal_interface
$ mix test
Or running with coverage check:
$ MIX_ENV=test mix coveralls.html
$ mix docs
$ mix credo
$ mix hex.outdated
There are a few configuration options at config/config.exs
that can be changed:
- typing_effect_print_time: There is a typing effect used by Terminal Interface. I found cool but if you get bored just change it to 0.
- user_reading_time: This is a time preconfigured to wait between some steps at the Terminal Interface to make the interface more user friendly.
You don't need to change the other settings, changing it will affect the functioning of the Terminal Interface.
We encourage you to contribute to NASA Exploration Rovers Control! Just submit a PR that we will be happy to review.
Wanna contribute and don't know where to start? There are some cool features in my mind:
- Colorize the terminal interface messages. That would be cool, han?
- The terminal interface could have the option to also receive the input commands by STDIO instead of only reading from files
- The terminal interface could print the commands that will be performed in a more readable and humanized format, this could help to prevent some mistakes
If you liked those ideas feel free to open a feature request issue or a PR. 😛
If you came this far and does not know why this crazy project was made, I'm happy that I've got your attention. 😆
This was just an exercise made, and (at least for now 😝) it has no real uses.
Thank you for the challenge, was real nice to work on it. 😍