August 2017 · 8 minute read
I have recently challenged my skills with the infamous toy robot simulator challenge and thought it would be great to document my thought process throughout the build. Not only can other see how I’ve gone about moving from A to B, but I can also gain a better insight into my own processes.
The completed project can be viewed at https://github.com/SelenaSmall/toy_robot
The criteria for this program rings a bell, so the first thing I’m going to do is look back through some of own work to review two projects I’ve started working on in the past which can be directly useful to this challenge.
- Ordering app with RSpec tests https://github.com/SelenaSmall/classorder
- Elevator app which moves based on user inputs https://github.com/SelenaSmall/Elevator
The first thing I’ve done is made some notes about what I’m trying to achieve. How the app might be structured and where methods might occur within that structure.
What methods belong where:
valid input coordinates x & y == 0..4
Valid if == “PLACE X,Y,F” || “MOVE” || “LEFT” || RIGHT” || “REPORT” X & Y == INT (0..4) F == “NORTH” || “SOUTH” || “EAST” || “WEST”
Initialised at position == NULL*
Place robot on table at position x,y,f Check: Is the position valid?
Move forward 1 space in the direction I am facing Check: If I move forward, will the new position be valid?
Turn 90 degress to left
Turn 90 degrees to right
output current coordinates
- /lib - table.rb (define table vars) - robot.rb (define robot vars) - position.rb (define robots position on the table) - place.rb (place robot and validate the new position is valid) - handle_input.rb (manage the place and move commands) - direction.rb (manage the robots facing direction) - move.rb (move robot to new coordinates if new position is valid) - report.rb (print our report data) - /spec - .rspec - .ruby-version - toy_robot.rb - Gemfile - Gemfile.lock - README.md
First thing with any project, of course is to create a new repo on Github with a README.md, clone it to your local and you’re good to go!
I’ve already thought about how the app will structured, so I can start by generating the appropriate root files.
- Create a .ruby-version file - Create Gemfile with rspec spec - Generate your gemfile.lock $ gem install bundle && bundle install - Initialise Rspec $ rspec --init - Create the lib dir for the project files - Create the project root file toy_robot.rb
First thing I think of is that I need a table to put the robot on. For this I simply need to specify an area that is 5x5. Now is also a good time add in a simple validation checking that any position on the table is within that 5x5 area.
For me, I like to have some sort of visuals so the next thing I’ll do is initialise the main loop to get user inputs. I’ve don’t this before both with the Elevator and Ordering programs I wrote when I was learning Ruby, so it should be a breeze.
!!! TESTING SHOULD START HERE !!!
Now that I have a table class and loop for reading user inputs that work successfully, it’s time to start testing!
The rest of the project will be test-driven, meaning I will try to write tests ahead of or at the same time as code. I will then ensure that all existing tests pass before committing anything else to my repo.
I’ll now need to define a robot to be ‘placed’ on the table. The first thing I’ll need to know is what coordinates (x & y) is the robot being place at?
I need to define position coordinates x, y and update the robot’s position when a user inputs “PLACE x,y”
- Define position coordinates x and y - Update robot.position - Check that the robot’s position is now not nil
The only way to be sure that the robot’s new position is valid within the table’s 5x5 area is to check it against the Table#is_valid method.
I need to handle user inputs and ensure the commands being parsed are valid
- Array of valid command patterns - Match input to one of those patterns - If input matches, do the action - else return - if robot.not_in_place? - the only valid command is ‘place’
One of the criteria for ‘placing’ a robot is to also tell it what direction it needs to be facing. I’ll start by updating the test code to expect a direction - this will help me quickly pinpoint the locations that need to be updated through out my code base.
I’m not sure yet how the #left and #right methods are going to fit in with the rest of the app, so I’ll start by defining an array of valid directions which I will iterate over to get the next or previous value, depending on which starting position was parsed.
- Define turn_right to check that the direction is valid and locate that direction in the directions_array - Find the index of the current direction in the directions_array - Iterate the directions_array to find the next direction’s value
Right turn method
The right method needs to check whether the current direction is the last item in the array, if it is I can instead simply return the first value in the directions_array.
- Check if the current direction is the last value in the array * If it is the next direction will be the first value in the array.
Once the turn right method is working, I can then integrate my direction class with user inputs to update the robot’s position when the right command is parsed.
Left turn method
Essentially the reverse of the right_turn method. I can also refactor here since two methods are almost identical.
- Define turn_left method ( essentially the reverse of turn_right ) - Refactor theses two methods to increase flexibility
To move the object, I’ll need to know: What is my current position, made up of x,y,f and what are the rules for moving in each direction?
- If direction == North - x stays the same - y increase by 1 - If direction == East - x increases by 1 - y stays the same - If direction == South - x stays the same - y decreases by 1 - If direction == West - x decreases by 1 - y stays the same
The report method simply needs to display the current position of the robot at the time when when the method is called.
Having implemented all of the spec features and ensured that the tests pass, I have reviewed the specs and tested the program manually to ensure everything is working as it should. I’ve found an issue where the MOVE action returns a false position validation but still updates the robot position.
To fix this, the best solution is to break down the problem and code to see where it’s going wrong. At this point, I have just commited a big chunck of unrefined code because you can’t refine code until you get it working in the first place!
Now that I have working code which meets all of the project specs, I have a valid MVP (minimum viable product). Now is the time to go through, tidy up refactor and optimise my code.
https://github.com/SelenaSmall/toy_robot/commit/5152d5937e471c3be0bb639f8690a2b759306054 https://github.com/SelenaSmall/toy_robot/commit/ceed3ffc273abf2ae1642f7abd3e301e1062778a https://github.com/SelenaSmall/toy_robot/commit/5047899aa3bff4c6ad856e2122e7cf5299d69b59
Now that my project is looking more complete, I will revise my top level documentation and update the README.
Before passing code on for someone else to look at, it’s always good practice to review it yourself and see if there are any more improvements that you can make or if you have any specific areas that you think could be improved but your not sure how to implement. I will also review each method to ensure that any possible exceptions are being handled.
I have written tests throughout the build process and ensured that they pass before committing any new changes. To ensure the robustness of this product, I’m going to review all of the test code to see if there’s anything I’ve missed and to make sure that the tests are relevant. I’ll add any additional tests I can think of.
Upon testing, I have found that I missed an exception - when user presses enter without inputting a command, the program exits. This empty command should be ignored.
The final criteria in the project spec is to produce some sample data output and show that the program works according to specs
Like with any project, this can undoubtedly be improved. At this stage, however, I am pleased with what I have produced. I feel that my code is clean and simple with smart methods that don’t try to take on too much.
In saying that, I am also eager to hear feedback which will hopefully open my eyes to something I hadn’t previously thought of.