Skip to content

Commit

Permalink
Optimise HandleInput#interpret method. Update specs and readme
Browse files Browse the repository at this point in the history
  • Loading branch information
SelenaSmall committed Aug 28, 2017
1 parent e921af6 commit e632750
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 105 deletions.
77 changes: 35 additions & 42 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
# Toy Robot
Toy robot simulator written in Ruby

## Table of Contents
1. [Installation](#Installation)
2. [Usage](#Usage)
3. [Testing](#Testing)
4. [Design](#Design)
5. [Specifications](#Specifications)
***

## Installation
Environment: Built on Mac OSx using Ruby -v 2.4.1
Expand All @@ -20,78 +15,77 @@ Clone this Repo
In root of the app run bundle install
> $ gem install bundler && bundle install
***

## Usage
Run the program from the app root
> $ toy_robot.rb
Expected terminal output:
> Options: PLACE X,Y,F; MOVE; LEFT; RIGHT; REPORT; EXIT
Available commands
| Command | Description |
| ------------- | ------------- |
| PLACE X,Y,F | Place the robot on the table at coordinates x,y and facing the direction f. Valid x andy values are between 0-4. Valid directions are WEST, NORTH, EAST, SOUTH. Example Input: "PLACE 0,0,NORTH"|
| MOVE | Move the robot forward 1 step in the direction it is facing. |
| LEFT | Turn the robot's direction 90 degress to the left. I.e. if the robot is facing NORTH, 1 left turn will turn the robot's direction to WEST. |
| RIGHT | Turn the robot's direction 90 degress to the right. I.e. if the robot is facing NORTH, 1 left turn will turn the robot's direction to EAST. |
| REPORT | Output the current position of the robot. Example Output: "Output: 0,0,NORTH" |
| EXIT | Gracefully exit the program. |
Commands | Description
--- | ---
PLACE X,Y,F | Place the robot on the table at coordinates x,y and facing the direction f. Valid x andy values are between 0-4. Valid directions are WEST, NORTH, EAST, SOUTH. Example Input: "PLACE 0,0,NORTH"
MOVE | Move the robot forward 1 step in the direction it is facing.
LEFT | Turn the robot's direction 90 degress to the left. I.e. if the robot is facing NORTH, 1 left turn will turn the robot's direction to WEST.
RIGHT | Turn the robot's direction 90 degress to the right. I.e. if the robot is facing NORTH, 1 left turn will turn the robot's direction to EAST.
REPORT | Output the current position of the robot. Example Output: "Output: 0,0,NORTH"
EXIT | Gracefully exit the program.

***

## Testing
Test by running rspec http://rspec.info/
> $ bundle exec rspec
Expected terminal output:
> ... ..Output: 0,0,NORTH
>
> ... ... ... ...Output: 1,2,NORTH
> ... ... ... ... ... .Output: 1,2,NORTH
>
> ... ... ... ... ... ...
> ... ... ... ... ... ... .
>
> Finished in 0.01938 seconds (files took 0.23698 seconds to load)
>
> 35 examples, 0 failures
## Speicifications
__Description:__
***

## Specifications
### Description

The application is a simulation of a toy robot moving on a square tabletop, of dimensions 5 units x 5 units.

There are no other obstructions on the table surface.

The robot is free to roam around the surface of the table, but must be prevented from falling to destruction. Any movement that would result in the robot falling from the table must be prevented, however further valid movement commands must still be allowed.

Create an application that can read in commands of the following form -
Create an application that can read in commands of the following form - PLACE X,Y,F | MOVE | LEFT | RIGHT | REPORT

* PLACE X,Y,F
__PLACE__

* MOVE
* Will put the toy robot on the table in position X,Y and facing NORTH, SOUTH, EAST or WEST.

* LEFT
* The origin (0,0) can be considered to be the SOUTH WEST most corner.

* RIGHT
* The first valid command to the robot is a PLACE command, aXer that, any sequence of commands may be issued, in any order, including another PLACE command. The application should discard all commands in the sequence until a valid PLACE command has been executed.

* REPORT

PLACE will put the toy robot on the table in position X,Y and facing NORTH, SOUTH, EAST or WEST.
__MOVE__

The origin (0,0) can be considered to be the SOUTH WEST most corner.
* Will move the toy robot one unit forward in the direction it is currently facing.

The first valid command to the robot is a PLACE command, aXer that, any sequence of commands may be issued, in any order, including another PLACE command. The application should discard all commands in the sequence until a valid PLACE command has been executed.
__LEFT and RIGHT__

MOVE will move the toy robot one unit forward in the direction it is currently facing.
* Will rotate the robot 90 degrees in the specified direction without changing the position of the robot.

LEFT and RIGHT will rotate the robot 90 degrees in the specified direction without changing the position of the robot.
__REPORT__

REPORT will announce the X,Y and F of the robot. This can be in any form, but standard output is sufficient.
* Will announce the X,Y and F of the robot. This can be in any form, but standard output is sufficient.

A robot that is not on the table can choose the ignore the MOVE, LEFT, RIGHT

and REPORT commands.
* A robot that is not on the table can choose the ignore the MOVE, LEFT, RIGHT and REPORT commands.

Input can be from a file, or from standard input, as the developer chooses. . Provide test data to exercise the application.
* Input can be from a file, or from standard input, as the developer chooses. . Provide test data to exercise the application.

__Constraints:__
### Constraints

The toy robot must not fall off the table during movement. This also includes the initial placement of the toy robot.
Any move that would cause the robot to fall must be ignored.
Expand Down Expand Up @@ -130,8 +124,7 @@ c)
>
> Output: 3,3,NORTH
__Deliverables:__
### Deliverables

The source files, the test data and any test code.
It is not required to provide any graphical output showing the movement of the toy robot.

8 changes: 2 additions & 6 deletions lib/action.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
# Action class
class Action
attr_reader :robot

def initialize(robot)
@robot = robot
end

# Place method
# @param command [String]
# @return Position
Expand Down Expand Up @@ -51,6 +45,8 @@ def right(position)
def report(position)
message = "Output: #{position.x},#{position.y},#{position.f} \n"
$stdout.print message

position
end

private
Expand Down
23 changes: 13 additions & 10 deletions lib/handle_input.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@ class HandleInput

# Command Options
PLACE = /^PLACE\s+\d+\s*,\s*\d+\s*,\s*(WEST||NORTH||EAST||SOUTH)$/
MOVE = /^MOVE$/
LEFT = /^LEFT$/
RIGHT = /^RIGHT$/
REPORT = /^REPORT$/
ACTIONS = %w[MOVE LEFT RIGHT REPORT].freeze

def initialize(robot, table, action)
@robot = robot
Expand All @@ -21,17 +18,23 @@ def initialize(robot, table, action)
# @param command [String]
# @return Position
def interpret(command)
return exec(action.place(command)) if PLACE.match?(command)
return unless command.match(PLACE) || ACTIONS.include?(command)

return exec(action.place(command)) if command.match?(PLACE)
return if robot.not_in_place?

return exec(action.move(robot.position)) if MOVE.match?(command)

return exec(action.left(robot.position)) if LEFT.match?(command)
exec(next_position(robot.position, command))
end

return exec(action.right(robot.position)) if RIGHT.match?(command)
private

return action.report(robot.position) if REPORT.match?(command)
# Next position method
# Find the robot's next position if an action was to be performed
# @param position [Object]
# @param command [String]
# @return Position
def next_position(position, command)
action.public_send ACTIONS.detect { |e| e == command }.downcase, position
end

# Exec method
Expand Down
30 changes: 11 additions & 19 deletions spec/action_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,9 @@
require './lib/action'

describe Action do
describe '#initialize' do
it 'should have a current_position attribute which is nil' do
instance = Action.new(@robot)

expect(instance.robot).to be_nil
end
end

describe '#place' do
it 'should return an instance of Position' do
instance = Action.new(@robot)
instance = Action.new
command = 'PLACE 0,0,NORTH'

expect(instance.place(command)).to be_a Position
Expand All @@ -22,7 +14,7 @@

describe '#move' do
it 'should return an instance of Position' do
instance = Action.new(@robot)
instance = Action.new
position = Position.new(0, 0, 'NORTH')

expect(instance.move(position)).to be_a Position
Expand All @@ -31,7 +23,7 @@

describe '#left' do
it 'should return an instance of Position' do
instance = Action.new(@robot)
instance = Action.new
position = Position.new(0, 0, 'NORTH')

expect(instance.left(position)).to be_a Position
Expand All @@ -40,47 +32,47 @@

describe '#right' do
it 'should return an instance of Position' do
instance = Action.new(@robot)
instance = Action.new
position = Position.new(0, 0, 'NORTH')

expect(instance.right(position)).to be_a Position
end
end

describe '#report' do
it 'should return nil' do
instance = Action.new(@robot)
it 'should return an instance of Position' do
instance = Action.new
position = Position.new(0, 0, 'NORTH')

expect(instance.report(position)).to be_nil
expect(instance.right(position)).to be_a Position
end
end

describe '#prev_option' do
it 'should return the previous value in the OPTIONS array' do
instance = Action.new(@robot)
instance = Action.new

expect(instance.send(:prev_option, 'NORTH')).to be_a String
expect(instance.send(:prev_option, 'NORTH')).to eq 'WEST'
end

it 'should return nil if the direction param is empty' do
instance = Action.new(@robot)
instance = Action.new

expect(instance.send(:prev_option, '')).to be_nil
end
end

describe '#next_option' do
it 'should return the next value in the OPTIONS array' do
instance = Action.new(@robot)
instance = Action.new

expect(instance.send(:next_option, 'NORTH')).to be_a String
expect(instance.send(:next_option, 'NORTH')).to eq 'EAST'
end

it 'should return nil if the direction param is empty' do
instance = Action.new(@robot)
instance = Action.new

expect(instance.send(:next_option, '')).to be_nil
end
Expand Down
52 changes: 27 additions & 25 deletions spec/handle_input_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,22 @@
end

describe '#interpret' do
it 'should return an instance of Position command matches PLACE pattern' do
robot = Robot.new
instance = HandleInput.new(robot, Table.new, Action.new(robot))
it 'should return an instance of Position when command matches PLACE pattern' do
instance = HandleInput.new(Robot.new, Table.new, Action.new)
command = 'PLACE 1,2,NORTH'

expect(instance.interpret(command)).to be_a Position
end

it 'should return nil if robot is not in place' do
robot = Robot.new
instance = HandleInput.new(robot, Table.new, Action.new(robot))
it 'should return nil when robot is not in place' do
instance = HandleInput.new(Robot.new, Table.new, Action.new)
command = ''

expect(instance.interpret(command)).to be_nil
end

it 'should return an instance of Position if command matches MOVE pattern' do
robot = Robot.new
instance = HandleInput.new(robot, Table.new, Action.new(robot))
it 'should return an instance of Position when command matches MOVE pattern' do
instance = HandleInput.new(Robot.new, Table.new, Action.new)
command1 = 'PLACE 1,2,NORTH'
command2 = 'MOVE'

Expand All @@ -45,9 +42,8 @@
expect(instance.interpret(command2)).to be_a Position
end

it 'should return an instance of Position if command matches LEFT pattern' do
robot = Robot.new
instance = HandleInput.new(robot, Table.new, Action.new(robot))
it 'should return an instance of Position when command matches LEFT pattern' do
instance = HandleInput.new(Robot.new, Table.new, Action.new)
command1 = 'PLACE 1,2,NORTH'
command2 = 'LEFT'

Expand All @@ -56,9 +52,8 @@
expect(instance.interpret(command2)).to be_a Position
end

it 'should return an instance of Position if command matches RIGHT pattern' do
robot = Robot.new
instance = HandleInput.new(robot, Table.new, Action.new(robot))
it 'should return an instance of Position when command matches RIGHT pattern' do
instance = HandleInput.new(Robot.new, Table.new, Action.new)
command1 = 'PLACE 1,2,NORTH'
command2 = 'RIGHT'

Expand All @@ -67,33 +62,40 @@
expect(instance.interpret(command2)).to be_a Position
end

it 'should return nil if command matches REPORT pattern' do
robot = Robot.new
instance = HandleInput.new(robot, Table.new, Action.new(robot))
it 'should return an instance of Position when command matches REPORT pattern' do
instance = HandleInput.new(Robot.new, Table.new, Action.new)
command1 = 'PLACE 1,2,NORTH'
command2 = 'REPORT'

instance.interpret(command1)

expect(instance.interpret(command2)).to be_nil
expect(instance.interpret(command2)).to be_a Position
end
end

describe '#next_position' do
it 'should return an instance of Position' do
instance = HandleInput.new(Robot.new, Table.new, Action.new)
position = Position.new(0, 0, 'NORTH')
command = 'MOVE'

expect(instance.send(:next_position, position, command)).to be_a Position
end
end

describe '#exec' do
it 'should return an instance of Position if position is a valid table position' do
robot = Robot.new
instance = HandleInput.new(robot, Table.new, Action.new(robot))
instance = HandleInput.new(Robot.new, Table.new, Action.new)
position = Position.new(0, 0, 'NORTH')

expect(instance.exec(position)).to be_a Position
expect(instance.send(:exec, position)).to be_a Position
end

it 'should return nil if position is not a valid table position' do
robot = Robot.new
instance = HandleInput.new(robot, Table.new, Action.new(robot))
instance = HandleInput.new(Robot.new, Table.new, Action.new)
position = Position.new(0, 6, 'NORTH')

expect(instance.exec(position)).to be_nil
expect(instance.send(:exec, position)).to be_nil
end
end
end
Loading

0 comments on commit e632750

Please sign in to comment.