🎉 Exercism Research is now launched. Help Exercism, help science and have some fun at research.exercism.io 🎉
Avatar of SergiiVlasiuk

SergiiVlasiuk's solution

to Robot Simulator in the Scala Track

Published at Aug 18 2019 · 0 comments
Instructions
Test suite
Solution

Write a robot simulator.

A robot factory's test facility needs a program to verify robot movements.

The robots have three possible movements:

  • turn right
  • turn left
  • advance

Robots are placed on a hypothetical infinite grid, facing a particular direction (north, east, south, or west) at a set of {x,y} coordinates, e.g., {3,8}, with coordinates increasing to the north and east.

The robot then receives a number of instructions, at which point the testing facility verifies the robot's new position, and in which direction it is pointing.

  • The letter-string "RAALAL" means:
    • Turn right
    • Advance twice
    • Turn left
    • Advance once
    • Turn left yet again
  • Say a robot starts at {7, 3} facing north. Then running this stream of instructions should leave it at {9, 4} facing west.

The Scala exercises assume an SBT project scheme. The exercise solution source should be placed within the exercise directory/src/main/scala. The exercise unit tests can be found within the exercise directory/src/test/scala.

To run the tests simply run the command sbt test in the exercise directory.

For more detailed info about the Scala track see the help page.

Source

Inspired by an interview question at a famous company.

Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have completed the exercise.

RobotSimulatorTest.scala

import org.scalatest.{Matchers, FunSuite}

/** @version 2.2.0 */
class RobotSimulatorTest extends FunSuite with Matchers {

  test(
    "A robot is created with a position and a direction - Robots are created with a position and direction") {
    Robot(Bearing.North, (0, 0)) should be(Robot(Bearing.North, (0, 0)))
  }

  test(
    "A robot is created with a position and a direction - Negative positions are allowed") {
    pending
    Robot(Bearing.South, (-1, -1)) should be(Robot(Bearing.South, (-1, -1)))
  }

  test(
    "rotates the robot's direction 90 degrees clockwise - does not change the position") {
    pending
    Robot(Bearing.North, (0, 0)).turnRight.coordinates should be((0, 0))
  }

  test(
    "rotates the robot's direction 90 degrees clockwise - changes the direction from north to east") {
    pending
    Robot(Bearing.North, (0, 0)).turnRight.bearing should be(Bearing.East)
  }

  test(
    "rotates the robot's direction 90 degrees clockwise - changes the direction from east to south") {
    pending
    Robot(Bearing.East, (0, 0)).turnRight.bearing should be(Bearing.South)
  }

  test(
    "rotates the robot's direction 90 degrees clockwise - changes the direction from south to west") {
    pending
    Robot(Bearing.South, (0, 0)).turnRight.bearing should be(Bearing.West)
  }

  test(
    "rotates the robot's direction 90 degrees clockwise - changes the direction from west to north") {
    pending
    Robot(Bearing.West, (0, 0)).turnRight.bearing should be(Bearing.North)
  }

  test(
    "rotates the robot's direction 90 degrees counter-clockwise - does not change the position") {
    pending
    Robot(Bearing.North, (0, 0)).turnLeft.coordinates should be((0, 0))
  }

  test(
    "rotates the robot's direction 90 degrees counter-clockwise - changes the direction from north to west") {
    pending
    Robot(Bearing.North, (0, 0)).turnLeft.bearing should be(Bearing.West)
  }

  test(
    "rotates the robot's direction 90 degrees counter-clockwise - changes the direction from west to south") {
    pending
    Robot(Bearing.West, (0, 0)).turnLeft.bearing should be(Bearing.South)
  }

  test(
    "rotates the robot's direction 90 degrees counter-clockwise - changes the direction from south to east") {
    pending
    Robot(Bearing.South, (0, 0)).turnLeft.bearing should be(Bearing.East)
  }

  test(
    "rotates the robot's direction 90 degrees counter-clockwise - changes the direction from east to north") {
    pending
    Robot(Bearing.East, (0, 0)).turnLeft.bearing should be(Bearing.North)
  }

  test(
    "moves the robot forward 1 space in the direction it is pointing - does not change the direction") {
    pending
    Robot(Bearing.North, (0, 0)).advance.bearing should be(Bearing.North)
  }

  test(
    "moves the robot forward 1 space in the direction it is pointing - increases the y coordinate one when facing north") {
    pending
    Robot(Bearing.North, (0, 0)).advance.coordinates should be((0, 1))
  }

  test(
    "moves the robot forward 1 space in the direction it is pointing - decreases the y coordinate by one when facing south") {
    pending
    Robot(Bearing.South, (0, 0)).advance.coordinates should be((0, -1))
  }

  test(
    "moves the robot forward 1 space in the direction it is pointing - increases the x coordinate by one when facing east") {
    pending
    Robot(Bearing.East, (0, 0)).advance.coordinates should be((1, 0))
  }

  test(
    "moves the robot forward 1 space in the direction it is pointing - decreases the x coordinate by one when facing west") {
    pending
    Robot(Bearing.West, (0, 0)).advance.coordinates should be((-1, 0))
  }

  test(
    "Where R = Turn Right, L = Turn Left and A = Advance, the robot can follow a series of instructions and end up with the correct position and direction - instructions to move west and north") {
    pending
    Robot(Bearing.North, (0, 0)).simulate("LAAARALA") should be(
      Robot(Bearing.West, (-4, 1)))
  }

  test(
    "Where R = Turn Right, L = Turn Left and A = Advance, the robot can follow a series of instructions and end up with the correct position and direction - instructions to move west and south") {
    pending
    Robot(Bearing.East, (2, -7)).simulate("RRAAAAALA") should be(
      Robot(Bearing.South, (-3, -8)))
  }

  test(
    "Where R = Turn Right, L = Turn Left and A = Advance, the robot can follow a series of instructions and end up with the correct position and direction - instructions to move east and north") {
    pending
    Robot(Bearing.South, (8, 4)).simulate("LAAARRRALLLL") should be(
      Robot(Bearing.North, (11, 5)))
  }
}
import Bearing.{East, North, South, West}

case class Robot(bearing: Bearing, tuple: (Int, Int)) {

  def simulate(sim: String): Robot =
    sim.foldLeft(this) { (acc, op) =>
      op match {
        case 'L' => acc.turnLeft
        case 'R' => acc.turnRight
        case 'A' => acc.advance
      }
    }

  def turnRight(): Robot = Robot(bearing match {
      case North => East
      case East => South
      case South => West
      case West => North
    }, coordinates)

  def turnLeft(): Robot = Robot(bearing match {
      case North => West
      case East => North
      case South => East
      case West => South
    }, coordinates)

  def advance: Robot = bearing match {
    case Bearing.North => new Robot(bearing, (coordinates._1,  coordinates._2 + 1))
    case Bearing.East => new Robot(bearing, (coordinates._1 + 1,  coordinates._2))
    case Bearing.South => new Robot(bearing, (coordinates._1,  coordinates._2 - 1))
    case Bearing.West => new Robot(bearing, (coordinates._1 - 1,  coordinates._2 ))
  }

  def coordinates() = tuple

}

sealed class Bearing

object Bearing {

  case object North extends Bearing

  case object South extends Bearing

  case object East extends Bearing

  case object West extends Bearing

}

Community comments

Find this solution interesting? Ask the author a question to learn more.

What can you learn from this solution?

A huge amount can be learned from reading other people’s code. This is why we wanted to give exercism users the option of making their solutions public.

Here are some questions to help you reflect on this solution and learn the most from it.

  • What compromises have been made?
  • Are there new concepts here that you could read more about to improve your understanding?