foldLeft

Robot Simulator
Robot Simulator in Scala
object Bearing extends Enumeration {
  val North = Value(0)
  val East = Value(1)
  val South = Value(2)
  val West = Value(3)
}

case class Robot(bearing: Bearing.Value, pos: (Int, Int)) {

  val DirectionCount = 4

  def turnRight: Robot =
    Robot(Bearing.apply((bearing.id + 1) % DirectionCount), pos)

  def turnLeft: Robot =
    Robot(
      Bearing.apply((DirectionCount + (bearing.id - 1)) % DirectionCount),
      pos
    )

  def coordinates: (Int, Int) = pos

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

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

This approach starts by defining the Bearing Enumeration in the Scala 2 way.

The Robot class starts by defining an immutable value that represents the count of the four directions being used in the program.

The turnRight() method uses the id member of the Value class to get the value of the Enumeration. So, the id of a variable set to Bearing.West would be 3. The apply() method is used to set a Bearing Enumeration from a value. So, if the robot were facing west and turned right to the north, 1 would be added to its value of 3, and the modulus operator would give a remainder of 0 when divided by the DirectionCount of 4. 0 would be passed to apply(), which would create a Bearing of North.

The turnLeft() method uses the same methods. So, if the robot were facing north and turned left to the west, 1 would be subracted from its value of 0, and -1 would be added to DirectionCount to get 3. The modulus operator would give a remainder of 3 when divided by the DirectionCount of 4, and 3 would be passed to apply(), which would create a Bearing of West.

if the robot were facing east and turned left to the north, 1 would be subracted from its value of 1, and 0 would be added to DirectionCount to get 4. The modulus operator would give a remainder of 0 when divided by the DirectionCount of 4. and 0 would be passed to apply(), which would create a Bearing of North.

At the time of the writing, Scala 3 is not yet supported on the Scala track. The Scala 3 way of enums would be handled similarly, with the ordinal() method functioning like id and the fromOrdinal() method functioning like apply(), like so:

enum Bearing {
  case North, East, South, West
}

// code snipped

def turnRight: Robot =
  Robot(Bearing.fromOrdinal((bearing.ordinal + 1) % DirectionCount), pos)

def turnLeft: Robot =
  Robot(
    Bearing.fromOrdinal(
      (DirectionCount + (bearing.ordinal - 1)) % DirectionCount
    ),
    pos
  )

The simulate() method uses the foldLeft() method to iterate the characters of the orders String.

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

The initial value for foldLeft() is set to the Robot instance. The first iteration passes the Robot instance and the first character of the orders String into a lambda which uses a match for pattern matching on the command letter. The match returns a new Robot which is passed to the next iteration of foldLeft along with the next character of the orders String. When foldLeft() is done iterating through all of the letters, it returns the Robot instance from its last iteration.

11th Sep 2024 · Found it useful?