Toy Robot Simulator

Exercise instructions can be found in PROBLEM.md file

LANGUAGE This challenge was solved using Ruby (version:2.3.1p112) and MiniTest was used for testing.

Here you can get the latest Ruby version: https://www.ruby-lang.org/en/downloads/

To run the TOY ROBOT SIMULATOR:

  • type in bash: ruby ToyRobotSimulator.rb
  • follow instructions :)
  • to exit -> simply type exit
  • // To customize board size provide x and y arguments for board object in ToyRobotSimulator.rb file//
  • To run the TESTS:

  • In tests directory:
  • type in bash: ruby test_robot_movements.rb
  • type in bash: ruby test_user_commands.rb
  • type in bash: ruby test_customized_board.rb
  • Image of Toy Robots

    APPROACH TAKEN:

    1. I strongly focused on TDD when solving this challenge, although I am aware that design and architecture is as important. For almost all the time I firstly wrote tests, failed and passed them trying to write as little code as possible and finally refactored my code (red green refactor). This approach resulted and helped me to design methods that are as small as possible and easy to test.

    2. At the start I simply wanted to 'solve' robot's movements on the board and then I restricted them so that robot could not fall out. I created test_robot_movements.rb and robot_movements.rb files. Robot class is responsible for the robot movements logic. I also decided that maximal values for the board size should not be hardcoded in case that someone would like to extend them in the future. As some of the methods should not be accessible by the Robot I hide them (encapsulation).

    3. Secondly I created test_user_commands.rb and execute_user_commands.rb files that are responsible for commands provided by the user. gets accepts data from the standard input - the keyboard in this solution.

    4. Getting user input was a little bit more tricky for testing.

    5. As it's impossible to predict commands provided by the user I needed to create Dummy Class: DummyUserInput for my TestExecuteUserCommands class.

    6. Place method was a little tricky to test as well.. I overwrote it for testing purposes with the hardcoded and expected values so that other methods could be tested.

    7. To test private methods in ExecuteUserCommands class I opened that class in test files and tested each of them separately.

    8. I wasn't sure how to test place_command method from the ExecuteUserCommands class. First test checks if that method works for the exit command. Second test (that is actually commented out) works when loop do has break in it but then expected value is equal to nil.

    9. User commands and arguments (x,y,face) validation was improved. I allowed users to use either upcase or lowercase commands and forgiven them using spaces 😉 Yet, that can be changed in:

      • robot_movements file upcase_face_and_remove_spaces(face) method (gsub(/\s+/ , '') and/or upcase
      • execute_user_commands file, class UserInput and save_command method (upcase) and perform_commands method and regular expression /PLACE\s[0-9],[0-9],(NORTH|SOUTH|WEST|EAST)/ Image of Toy Robots
    10. In the end I thought that it's more logical to create separate Board class for customizing it's size rather than providing arguments in the Robot object.

    CHALLENGES

    1. Deciding how to build and separate classes.

    2. Design and architecture, trying to follow SRP and SoC.

    3. Rotating and moving the robot. Methods for left and right movements are quite similar, faces are hardcoded. As the exercise has only few choices (4 for face // 5 for movements) using case statement works but it's not perfect. If assignment would extend and there would be a need for more directions/movements solution should be probably redesigned.

    4. Testing user input for place and perform_commands method.

    THIGNS TO CONSIDER and what I would like to have done differently

    Once I have solved this challenge, I see some of its limitations😓. As mentioned, if the problem grows and more directions/movements would be added this solution is not ideal. If I had a little bit more time, I would probably think about changing case statements and think about polymorphism instead? I would have probably redesign Robot class. Perform_commands method could take an argument - command like this: robot.perform_command(command_name, arguments) (Some of the ideas can be find in things_to_consider.rb file.)

    LESSONS LEARNED

    This coding challenge was a great fun and lesson for me.

    Many thanks for taking the time to check my solution and the feedback.
    Cheers! 😊