 paulfioravanti's solution

to Robot Simulator in the Ruby Track

Published at May 24 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

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
• Turn left
• 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.

For installation and learning resources, refer to the Ruby resources page.

For running the tests provided, you will need the Minitest gem. Open a terminal window and run the following command to install minitest:

gem install minitest

If you would like color output, you can require 'minitest/pride' in the test file, or note the alternative instruction, below, for running the test file.

Run the tests from the exercise directory using the following command:

ruby robot_simulator_test.rb

To include color from the command line:

ruby -r minitest/pride robot_simulator_test.rb

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.

robot_simulator_test.rb

require 'minitest/autorun'
require_relative 'robot_simulator'

class RobotTurningTest < Minitest::Test

def setup
@robot = Robot.new
end

def test_robot_bearing
[:east, :west, :north, :south].each do |direction|
robot.orient(direction)
assert_equal direction, robot.bearing
end
end

def test_invalid_robot_bearing
skip
assert_raises ArgumentError do
robot.orient(:crood)
end
end

def test_turn_right_from_north
skip
robot.orient(:north)
robot.turn_right
assert_equal :east, robot.bearing
end

def test_turn_right_from_east
skip
robot.orient(:east)
robot.turn_right
assert_equal :south, robot.bearing
end

def test_turn_right_from_south
skip
robot.orient(:south)
robot.turn_right
assert_equal :west, robot.bearing
end

def test_turn_right_from_west
skip
robot.orient(:west)
robot.turn_right
assert_equal :north, robot.bearing
end

def test_turn_left_from_north
skip
robot.orient(:north)
robot.turn_left
assert_equal :west, robot.bearing
end

def test_turn_left_from_east
skip
robot.orient(:east)
robot.turn_left
assert_equal :north, robot.bearing
end

def test_turn_left_from_south
skip
robot.orient(:south)
robot.turn_left
assert_equal :east, robot.bearing
end

def test_turn_left_from_west
skip
robot.orient(:west)
robot.turn_left
assert_equal :south, robot.bearing
end

def test_robot_coordinates
skip
robot.at(3, 0)
assert_equal [3, 0], robot.coordinates
end

def test_other_robot_coordinates
skip
robot.at(-2, 5)
assert_equal [-2, 5], robot.coordinates
end

skip
robot.at(0, 0)
robot.orient(:north)
assert_equal [0, 1], robot.coordinates
end

skip
robot.at(0, 0)
robot.orient(:east)
assert_equal [1, 0], robot.coordinates
end

skip
robot.at(0, 0)
robot.orient(:south)
assert_equal [0, -1], robot.coordinates
end

skip
robot.at(0, 0)
robot.orient(:west)
assert_equal [-1, 0], robot.coordinates
end
end

class RobotSimulatorTest < Minitest::Test
def simulator
@simulator ||= Simulator.new
end

def test_instructions_for_turning_left
skip
assert_equal [:turn_left], simulator.instructions('L')
end

def test_instructions_for_turning_right
skip
assert_equal [:turn_right], simulator.instructions('R')
end

skip
end

def test_series_of_instructions
skip
assert_equal commands, simulator.instructions('RAAL')
end

def test_instruct_robot
skip
robot = Robot.new
simulator.place(robot, x: -2, y: 1, direction: :east)
simulator.evaluate(robot, 'RLAALAL')
assert_equal [0, 2], robot.coordinates
assert_equal :west, robot.bearing
end

def test_instruct_many_robots
skip
robot1 = Robot.new
robot2 = Robot.new
robot3 = Robot.new
simulator.place(robot1, x: 0, y: 0, direction: :north)
simulator.place(robot2, x: 2, y: -7, direction: :east)
simulator.place(robot3, x: 8, y: 4, direction: :south)
simulator.evaluate(robot1, 'LAAARALA')
simulator.evaluate(robot2, 'RRAAAAALA')
simulator.evaluate(robot3, 'LAAARRRALLLL')

assert_equal [-4, 1], robot1.coordinates
assert_equal :west, robot1.bearing

assert_equal [-3, -8], robot2.coordinates
assert_equal :south, robot2.bearing

assert_equal [11, 5], robot3.coordinates
assert_equal :north, robot3.bearing
end
end
# frozen_string_literal: true

class Robot
BEARINGS = %i[north east south west].freeze
private_constant :BEARINGS
LEFT = -1
private_constant :LEFT
RIGHT = 1
private_constant :RIGHT

def orient(direction)
raise ArgumentError unless BEARINGS.include?(direction)

self.bearing = direction
end

def at(x_coord, y_coord)
self.coordinates = [x_coord, y_coord]
self
end

x, y = coordinates
case bearing
when :north then at(x, y + 1)
when :east then at(x + 1, y)
when :south then at(x, y - 1)
else at(x - 1, y)
end
end

def turn_left
turn(LEFT)
end

def turn_right
turn(RIGHT)
end

private

attr_writer :bearing, :coordinates

def turn(direction)
bearing
.then(&BEARINGS.method(:index))
.to_enum(:then)
.with_object(direction)
.next
.then(&method(:determine_new_direction))
.then(&method(:orient))
end

def determine_new_direction((index, direction))
direction
.then(&BEARINGS.method(:rotate))
.fetch(index)
end
end

class Simulator
INSTRUCTIONS = {
"L" => :turn_left,
"R" => :turn_right
}.freeze
private_constant :INSTRUCTIONS

def instructions(input)
input
.chars
.map(&INSTRUCTIONS)
end

# rubocop:disable Naming/UncommunicativeMethodParamName
def place(robot, x:, y:, direction:)
robot
.at(x, y)
.orient(direction)
end
# rubocop:enable Naming/UncommunicativeMethodParamName

def evaluate(robot, input)
input
.then(&method(:instructions))
.each
.with_object(robot, &method(:instruct_robot))
end

private

def instruct_robot(instruction, robot)
robot.public_send(instruction)
end
end