app/robot.js

const Position = require('./position.js')
const { BLANK_SPACE, COORDS, INSTRUCTIONS } = require('../utils')

// Used to set the new robot's orientation
const right = {
  [COORDS.N]: COORDS.E,
  [COORDS.E]: COORDS.S,
  [COORDS.S]: COORDS.W,
  [COORDS.W]: COORDS.N
}
const left = {
  [COORDS.N]: COORDS.W,
  [COORDS.W]: COORDS.S,
  [COORDS.S]: COORDS.E,
  [COORDS.E]: COORDS.N
}

const isLeftTurn = (instruction) => {
  return instruction === INSTRUCTIONS.LEFT
}
const isRightTurn = (instruction) => {
  return instruction === INSTRUCTIONS.RIGHT
}
const isForwardMovement = (instruction) => {
  return instruction === INSTRUCTIONS.FORWARD
}

/**
 * Creates a new Robot 🤖 which is able to follow instructions from Earth 🌍
 */
class Robot {
  /**
   * The robot uses the {@link Grid} and a default {@link Position}
   * @param grid {Grid}
   */
  constructor (grid) {
    this.grid = grid
    this.position = new Position()
  }

  /**
   * Gets the robot position
   * @returns {Position}
   */
  getPosition () {
    return this.position
  }

  /**
   * Given a Position (x, y, orientation), sets the robot position and orientation
   * @param startPosition {String}
   */
  setPosition (startPosition) {
    const [x, y, orientation] = startPosition.split(BLANK_SPACE)
    this.position.x = x
    this.position.y = y
    this.position.orientation = orientation
  }

  /**
   * Perform all the movements. In the moment robot is lost, does nothing
   *
   * @param {String} instructions
   * @returns {string} All the instructions
   */
  move (instructions) {
    for (let i = 0; i < instructions.length; i++) {
      if (this.position.lost) {
        break
      }

      // Get current instruction
      const instruction = instructions.charAt(i)

      if (isLeftTurn(instruction)) {
        this.turnLeft()
      }

      if (isRightTurn(instruction)) {
        this.turnRight()
      }

      if (isForwardMovement(instruction)) {
        this.moveForwards()
      }
    }
    return this.position.toString()
  }

  /**
   * Move one step forward, according to the orientation and change the position:
   * - Orientation (N) means `y + 1`
   * - Orientation (S) means `y - 1`
   * - Orientation (E) means `x + 1`
   * - Orientation (W) means `x - 1`
   */
  moveForwards () {
    const startingPosition = this.position.toString()

    // Do nothing if the robot is lost or it's a forbidden position
    if (this.position.lost || this.grid.hasForbidden(startingPosition)) {
      return
    }

    if (this.position.orientation === COORDS.N) {
      this.position.y++
    }

    if (this.position.orientation === COORDS.E) {
      this.position.x++
    }

    if (this.position.orientation === COORDS.S) {
      this.position.y--
    }

    if (this.position.orientation === COORDS.W) {
      this.position.x--
    }

    // After the robot has moved, check if it's off the grid
    if (this.position.isOffTheGrid(this.grid)) {
      this.grid.addForbiddenPosition(startingPosition)
    }
  }

  /**
   * Changes the orientation of the robot 90º left, according to the current orientation
   * @example
   * Robot is facing North (N)
   * Turn Left
   * Now, robot is facing West (W)
   */
  turnLeft () {
    this.position.orientation = left[this.position.orientation]
  }

  /**
   * Changes the orientation of the robot 90º right, according to the current orientation
   * @example
   * Robot is facing South (S)
   * Turn Right
   * Now, robot is facing West (W)
   */
  turnRight () {
    this.position.orientation = right[this.position.orientation]
  }

  /**
   * Check if robot is lost
   * @returns {boolean}
   */
  isLost () {
    return this.position.lost
  }
}

module.exports = Robot