robot-simulator 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b9cb140774f5e37640651be65f0875aeef72f8e1
4
+ data.tar.gz: f7fde3553a045be18db5292044d22fc9027e7ab9
5
+ SHA512:
6
+ metadata.gz: b3ffb093b24db252b7af2a8ecb8e1a4ab734220dc545f29e9c0a5e9e78513e8c62b313be398b85d8ab4938d1a4f95a8086dc2730fb0fa17b7b351580236d6a74
7
+ data.tar.gz: 187f05a2b7767b5ee1c16e7633eec89423a09be55e3bfb53009fc464ba39f761980c2d15719bebde28035ccc0aeb5bf7868852d247a376092a3212477ef1dcce
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
@@ -0,0 +1 @@
1
+ 2.1.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in robot-simulator.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Andrew Feng
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,61 @@
1
+ # Robot::Simulator
2
+
3
+ A robot simulator that can receive commands and move around on a table surface. It will prevent falling from the table, and can report its position.
4
+
5
+
6
+ ## Installation
7
+
8
+ Install it using gem:
9
+
10
+ $ gem install robot-simulator
11
+
12
+ Or directly from git repository:
13
+
14
+ $ git clone https://mingliangfeng@bitbucket.org/mingliangfeng/robot-simulator.git
15
+ $ cd robot-simulator
16
+ $ bundle install
17
+ $ bundle exec rspec
18
+
19
+
20
+ ## Environment
21
+
22
+ * Ruby version: `ruby 2.1.2p95`, best to use rbenv to set the ruby version.
23
+
24
+
25
+ ## Dependencies
26
+
27
+ * [thor](https://github.com/erikhuda/thor)
28
+ * [RSpec](https://github.com/rspec/rspec) for testing
29
+ * [rbenv](https://github.com/sstephenson/rbenv) for seting the right ruby version
30
+
31
+
32
+ ## Usage
33
+
34
+ #### Use installation
35
+ Once installed, try the following, you will be asked for commands to instruct the robot to move:
36
+
37
+ $ robot-simulator
38
+
39
+ Or alternatively run robot simulator with commands from a text file:
40
+
41
+ $ robot-simulator -f /path/to/commands/text/file
42
+
43
+ #### Use source code
44
+ Get the source code from bitbucket as instructed in Installation section, go to robot-simulator folder and play straightaway:
45
+
46
+ $ bundle exec bin/robot-simulator
47
+ $ bundle exec bin/robot-simulator -f /path/to/commands/text/file
48
+
49
+
50
+ ## Design
51
+
52
+ Please see the class diagram [here](https://repository.genmymodel.com/mingliangfeng/robot-simulator).
53
+
54
+
55
+ ## Contributing
56
+
57
+ 1. Fork it
58
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
59
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
60
+ 4. Push to the branch (`git push origin my-new-feature`)
61
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require 'robot/simulator/ui/cli'
3
+
4
+ Robot::Simulator::UI::CLI.start ARGV
@@ -0,0 +1,7 @@
1
+ require "robot/simulator/version"
2
+ require "robot/simulator/direction"
3
+ require 'robot/simulator/coordinate'
4
+ require 'robot/simulator/track'
5
+ require 'robot/simulator/robot'
6
+ require 'robot/simulator/table'
7
+ require 'robot/simulator/controller'
@@ -0,0 +1,34 @@
1
+ require 'robot/simulator'
2
+
3
+ module Robot
4
+ module Simulator
5
+ # Controller of the game, initialize the game and receive commands from UI
6
+ class Controller
7
+ attr_reader :robot
8
+
9
+ def initialize
10
+ @robot = Robot.new Table.new(5, 5)
11
+ end
12
+
13
+ def place(x, y, facing)
14
+ raise "Invalid PLACE command: PLACE #{x},#{y},#{facing}" unless robot.place Coordinate.new(x, y), Direction.from_str(facing)
15
+ end
16
+
17
+ def move
18
+ raise "Invalid MOVE command: robot is not on the table or could fall if move" unless robot.move
19
+ end
20
+
21
+ def left
22
+ raise "Invalid LEFT command: robot is not on the table" unless robot.turn_left
23
+ end
24
+
25
+ def right
26
+ raise "Invalid RIGHT command: robot is not on the table" unless robot.turn_right
27
+ end
28
+
29
+ def report
30
+ [robot.current_track.coordinate.x, robot.current_track.coordinate.y, Direction.to_str(robot.current_track.facing)] if robot.current_track
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,12 @@
1
+ module Robot
2
+ module Simulator
3
+ # Coordinate on the table, starting from (0, 0) on south west corner.
4
+ class Coordinate
5
+ attr_reader :x, :y
6
+
7
+ def initialize(x, y)
8
+ @x, @y = x, y
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,43 @@
1
+ module Robot
2
+ module Simulator
3
+ module Direction
4
+ DIRECTIONS = [EAST = 0, NORTH = 1, WEST = 2, SOUTH = 3]
5
+ TO_STRS = { EAST => 'EAST', NORTH => 'NORTH', WEST => 'WEST', SOUTH => 'SOUTH' }
6
+ TO_CONSTANTS = TO_STRS.invert
7
+
8
+ # Direction on the left when facing current direction
9
+ # @return [Direction]
10
+ def self.left(direction)
11
+ validate direction
12
+
13
+ DIRECTIONS[(direction + 1) % 4]
14
+ end
15
+
16
+ # Direction on the right when facing current direction
17
+ # @return [Direction]
18
+ def self.right(direction)
19
+ validate direction
20
+
21
+ DIRECTIONS[(direction - 1 + 4) % 4]
22
+ end
23
+
24
+ def self.to_str(direction)
25
+ validate direction
26
+
27
+ TO_STRS[direction]
28
+ end
29
+
30
+ def self.from_str(direction_str)
31
+ key = (direction_str || "").upcase
32
+ raise(ArgumentError, "Direction string '#{direction_str}' must be one of #{TO_CONSTANTS.keys}") unless TO_CONSTANTS.keys.include? key
33
+ TO_CONSTANTS[key]
34
+ end
35
+
36
+ def self.validate(direction)
37
+ raise(ArgumentError, "Direction must be one of #{DIRECTIONS}") unless DIRECTIONS.include?(direction)
38
+ end
39
+
40
+ private_class_method :validate
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,60 @@
1
+ module Robot
2
+ module Simulator
3
+ # Robot class corresponds to robot which can move around on the table.
4
+ class Robot
5
+ attr_reader :current_track, :table
6
+
7
+ def initialize(table)
8
+ @table = table
9
+ end
10
+
11
+ def place(coordinate, direction)
12
+ return false unless is_valid_coordinate?(coordinate)
13
+
14
+ self.current_track = Track.new coordinate, direction
15
+ end
16
+
17
+ def move
18
+ return false unless is_on_table?
19
+
20
+ newX = current_track.coordinate.x
21
+ newX += 1 if current_track.facing == Direction::EAST
22
+ newX -= 1 if current_track.facing == Direction::WEST
23
+
24
+ newY = current_track.coordinate.y
25
+ newY += 1 if current_track.facing == Direction::NORTH
26
+ newY -= 1 if current_track.facing == Direction::SOUTH
27
+
28
+ new_coordinate = Coordinate.new(newX, newY)
29
+ return false unless is_valid_coordinate? new_coordinate
30
+
31
+ self.current_track = Track.new new_coordinate, current_track.facing
32
+ end
33
+
34
+ def turn_left
35
+ return false unless is_on_table?
36
+
37
+ self.current_track = Track.new current_track.coordinate, Direction.left(current_track.facing)
38
+ end
39
+
40
+ def turn_right
41
+ return false unless is_on_table?
42
+
43
+ self.current_track = Track.new current_track.coordinate, Direction.right(current_track.facing)
44
+ end
45
+
46
+ private
47
+ def current_track=(current_track)
48
+ @current_track = current_track
49
+ end
50
+
51
+ def is_valid_coordinate?(coordinate)
52
+ table.is_valid_coordinate? coordinate
53
+ end
54
+
55
+ def is_on_table?
56
+ self.current_track != nil
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,18 @@
1
+ module Robot
2
+ module Simulator
3
+ # Table with dimension, in which a robot can be placed and roam around.
4
+ class Table
5
+ attr_reader :dimensionX, :dimensionY
6
+
7
+ def initialize(dimensionX, dimensionY)
8
+ @dimensionX, @dimensionY = dimensionX, dimensionY
9
+ end
10
+
11
+ def is_valid_coordinate?(coordinate)
12
+ coordinate and
13
+ coordinate.x >= 0 and coordinate.x < dimensionX and
14
+ coordinate.y >= 0 and coordinate.y < dimensionY
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,12 @@
1
+ module Robot
2
+ module Simulator
3
+ # Track of a robot, contains coordinate and facing direction.
4
+ class Track
5
+ attr_reader :coordinate, :facing
6
+
7
+ def initialize(coordinate, facing)
8
+ @coordinate, @facing = coordinate, facing
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,86 @@
1
+ require "thor"
2
+ require 'robot/simulator'
3
+
4
+ module Robot
5
+ module Simulator
6
+ module UI
7
+ class CLI < Thor
8
+
9
+ desc "start_game", "Start the robot simulator game"
10
+ method_option :file, :aliases => "-f", :desc => "path to a text file containing robot commands"
11
+ def start_game
12
+ @controller = Controller.new
13
+
14
+ if options[:file]
15
+ commands = read_commands_from_file options[:file]
16
+ else
17
+ commands = read_commands_from_console
18
+ end
19
+
20
+ commands.each {|command| dispatch_command command }
21
+ end
22
+
23
+ default_task :start_game
24
+
25
+ private
26
+ def read_commands_from_console
27
+ say "Please input robot commands, end with a blank line ...", :green
28
+ commands = []
29
+ loop do
30
+ command = ask('>').strip
31
+ break if command.empty?
32
+ commands << command
33
+ end
34
+ commands
35
+ end
36
+
37
+ def read_commands_from_file(file_name)
38
+ say "Reading robot commands from file #{file_name} ...", :green
39
+ commands = []
40
+ File.foreach(file_name) do |line|
41
+ line = line.strip
42
+ commands << line unless line.empty?
43
+ end
44
+ commands
45
+ end
46
+
47
+ def dispatch_command(command)
48
+ action, param = command.split(/\s+/)
49
+ say "Will execute command: #{command}", :cyan
50
+ begin
51
+ case action.upcase
52
+ when 'PLACE'
53
+ execute_place_command action, param
54
+ when 'MOVE'
55
+ @controller.move
56
+ when 'LEFT'
57
+ @controller.left
58
+ when 'RIGHT'
59
+ @controller.right
60
+ when 'REPORT'
61
+ report = @controller.report
62
+ say report ? report.join(',') : 'Not on table'
63
+ else
64
+ error "Unknow command is ignored: #{command}"
65
+ end
66
+ rescue StandardError => err
67
+ error err.message
68
+ end
69
+ end
70
+
71
+ def execute_place_command(action, param)
72
+ if param
73
+ x, y, facing = param.split(/\s*\,\s*/)
74
+ if x and y and facing
75
+ @controller.place x.to_i, y.to_i, facing
76
+ else
77
+ error "Command PLACE needs 3 parameters in format of x,y,facing, got #{param}"
78
+ end
79
+ else
80
+ error "Command PLACE is ignored because of lack parameters"
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,5 @@
1
+ module Robot
2
+ module Simulator
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'robot/simulator/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "robot-simulator"
8
+ spec.version = Robot::Simulator::VERSION
9
+ spec.authors = ["Andrew Feng"]
10
+ spec.email = ["mingliangfeng@gmail.com"]
11
+ spec.description = %q{Robot Simulator Code Exercise}
12
+ spec.summary = %q{A robot simulator that can receive commands and move around on a table surface. It will prevent falling from the table, and can report its position.}
13
+ spec.homepage = "https://github.com/mingliangfeng"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "thor", '~> 0.19'
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.3"
24
+ spec.add_development_dependency "rake", '~> 10.3'
25
+ spec.add_development_dependency 'rspec', '3.1'
26
+ end
@@ -0,0 +1,72 @@
1
+ Robot Simulator
2
+
3
+ ## Description:
4
+
5
+ The application is a simulation of a toy robot moving on a square tabletop, of dimensions 5 units x 5 units.
6
+
7
+ There are no other obstructions on the table surface.
8
+
9
+ 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.
10
+
11
+ Create an application that can read in commands of the following form:
12
+
13
+ ```
14
+ PLACE X,Y,F
15
+ MOVE
16
+ LEFT
17
+ RIGHT
18
+ REPORT
19
+ ```
20
+
21
+ - `PLACE` will put the toy robot on the table in position X,Y and facing NORTH, SOUTH, EAST or WEST.
22
+
23
+ - The origin (0,0) can be considered to be the SOUTH WEST most corner.
24
+
25
+ - The first valid command to the robot is a `PLACE` command, after 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.
26
+
27
+ - `MOVE` will move the toy robot one unit forward in the direction it is currently facing.
28
+
29
+ - `LEFT` and `RIGHT` will rotate the robot 90 degrees in the specified direction without changing the position of the robot.
30
+
31
+ - `REPORT` will announce the X,Y and F of the robot. This can be in any form, but standard output is sufficient.
32
+
33
+ - A robot that is not on the table can choose the ignore the MOVE, LEFT, RIGHT and REPORT commands.
34
+
35
+ - Input can be from a file, or from standard input, as the developer chooses.
36
+
37
+ - Provide test data to exercise the application.
38
+
39
+ ## Constraints:
40
+
41
+ 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.
42
+
43
+ ## Example Input and Output:
44
+
45
+ ```
46
+ a)
47
+ PLACE 0,0,NORTH
48
+ MOVE
49
+ REPORT
50
+ Output: 0,1,NORTH
51
+
52
+ b)
53
+ PLACE 0,0,NORTH
54
+ LEFT
55
+ REPORT
56
+ Output: 0,0,WEST
57
+
58
+ c)
59
+ PLACE 1,2,EAST
60
+ MOVE
61
+ MOVE
62
+ LEFT
63
+ MOVE
64
+ REPORT
65
+ Output: 3,3,NORTH
66
+ ```
67
+
68
+ ## Deliverables:
69
+
70
+ The source files, the test data and any test code.
71
+
72
+ It is not required to provide any graphical output showing the movement of the toy robot.
@@ -0,0 +1,97 @@
1
+ require 'spec_helper'
2
+
3
+ module Robot
4
+ module Simulator
5
+ describe Controller do
6
+ context 'Robot simulator controller' do
7
+ let!(:controller) { Controller.new }
8
+
9
+ it 'controller should init the game with a robot and a 5 x 5 dimension table' do
10
+ expect( controller.robot ).not_to be nil
11
+ expect( controller.robot.table ).not_to be nil
12
+ expect( controller.robot.table.dimensionX ).to eq(5)
13
+ expect( controller.robot.table.dimensionY ).to eq(5)
14
+ end
15
+
16
+ context 'test place' do
17
+ it 'controller should receive PLACE command and set robot at correct coordinate' do
18
+ controller.place 0, 1, 'EAST'
19
+ expect( controller.robot.current_track ).not_to be nil
20
+ expect( controller.robot.current_track.coordinate.x ).to eq(0)
21
+ expect( controller.robot.current_track.coordinate.y ).to eq(1)
22
+ end
23
+
24
+ it 'controller should raise error for invalid PLACE command' do
25
+ expect{ controller.place 6, 6, 'EAST' }.to raise_error
26
+ end
27
+ end
28
+
29
+ context 'test move' do
30
+ it 'controller should receive MOVE command and move robot to correct coordinate' do
31
+ controller.place 0, 1, 'EAST'
32
+ controller.move
33
+ expect( controller.robot.current_track.coordinate.x ).to eq(1)
34
+ expect( controller.robot.current_track.coordinate.y ).to eq(1)
35
+ end
36
+
37
+ it 'controller should raise error for MOVE command before valid PLACE command' do
38
+ expect{ controller.move }.to raise_error
39
+ end
40
+
41
+ it 'controller should ingore MOVE command if doing so will cause robot to fall' do
42
+ controller.place 4, 4, 'EAST'
43
+ expect{ controller.move }.to raise_error
44
+ expect( controller.robot.current_track.coordinate.x ).to eq(4)
45
+ expect( controller.robot.current_track.coordinate.y ).to eq(4)
46
+ end
47
+ end
48
+
49
+ context 'test left' do
50
+ it 'controller should receive LEFT command and change robot facing direction' do
51
+ controller.place 0, 1, 'EAST'
52
+ controller.left
53
+ expect( controller.robot.current_track.facing ).to eq(Direction::NORTH)
54
+ controller.left
55
+ expect( controller.robot.current_track.facing ).to eq(Direction::WEST)
56
+ controller.left
57
+ expect( controller.robot.current_track.facing ).to eq(Direction::SOUTH)
58
+ controller.left
59
+ expect( controller.robot.current_track.facing ).to eq(Direction::EAST)
60
+ end
61
+
62
+ it 'controller should raise error for LEFT command before valid PLACE command' do
63
+ expect{ controller.left }.to raise_error
64
+ end
65
+ end
66
+
67
+ context 'test right' do
68
+ it 'controller should receive RIGHT command and change robot facing direction' do
69
+ controller.place 0, 1, 'EAST'
70
+ controller.right
71
+ expect( controller.robot.current_track.facing ).to eq(Direction::SOUTH)
72
+ controller.right
73
+ expect( controller.robot.current_track.facing ).to eq(Direction::WEST)
74
+ controller.right
75
+ expect( controller.robot.current_track.facing ).to eq(Direction::NORTH)
76
+ controller.right
77
+ expect( controller.robot.current_track.facing ).to eq(Direction::EAST)
78
+ end
79
+
80
+ it 'controller should raise error for RIGHT command before valid PLACE command' do
81
+ expect{ controller.right }.to raise_error
82
+ end
83
+ end
84
+
85
+ it 'test commands sequence' do
86
+ controller.place 1, 2, 'EAST'
87
+ controller.move
88
+ controller.move
89
+ controller.left
90
+ controller.move
91
+ expect( controller.report ).to eq [3, 3, "NORTH"]
92
+ end
93
+
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ module Robot
4
+ module Simulator
5
+ describe Coordinate do
6
+
7
+ it 'constructor and getters' do
8
+ coordinate = Coordinate.new 0, 1
9
+ expect( coordinate.x ).to eq(0)
10
+ expect( coordinate.y ).to eq(1)
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,72 @@
1
+ require 'spec_helper'
2
+
3
+ module Robot
4
+ module Simulator
5
+ describe Direction do
6
+
7
+ context 'test left' do
8
+
9
+ it 'expect ArgumentError if not called by passing Direction constants value' do
10
+ expect { Direction.left(-1) }.to raise_error(ArgumentError)
11
+ expect { Direction.left(5) }.to raise_error(ArgumentError)
12
+ end
13
+
14
+ it 'left should return correct value' do
15
+ expect( Direction.left(Direction::EAST) ).to eq(Direction::NORTH)
16
+ expect( Direction.left(Direction::NORTH) ).to eq(Direction::WEST)
17
+ expect( Direction.left(Direction::WEST) ).to eq(Direction::SOUTH)
18
+ expect( Direction.left(Direction::SOUTH) ).to eq(Direction::EAST)
19
+ end
20
+
21
+ end
22
+
23
+ context 'test right' do
24
+
25
+ it 'expect ArgumentError if not called by passing Direction constants value' do
26
+ expect { Direction.right(-1) }.to raise_error(ArgumentError)
27
+ expect { Direction.right(5) }.to raise_error(ArgumentError)
28
+ end
29
+
30
+ it 'right should return correct value' do
31
+ expect( Direction.right(Direction::EAST) ).to eq(Direction::SOUTH)
32
+ expect( Direction.right(Direction::SOUTH) ).to eq(Direction::WEST)
33
+ expect( Direction.right(Direction::WEST) ).to eq(Direction::NORTH)
34
+ expect( Direction.right(Direction::NORTH) ).to eq(Direction::EAST)
35
+ end
36
+
37
+ end
38
+
39
+ context 'test to_str' do
40
+
41
+ it 'expect ArgumentError if not called by passing Direction constants value' do
42
+ expect { Direction.to_str(-1) }.to raise_error(ArgumentError)
43
+ expect { Direction.to_str(5) }.to raise_error(ArgumentError)
44
+ end
45
+
46
+ it 'to_str should return correct string value' do
47
+ expect( Direction.to_str(Direction::EAST) ).to eq('EAST')
48
+ expect( Direction.to_str(Direction::SOUTH) ).to eq('SOUTH')
49
+ expect( Direction.to_str(Direction::WEST) ).to eq('WEST')
50
+ expect( Direction.to_str(Direction::NORTH) ).to eq('NORTH')
51
+ end
52
+
53
+ end
54
+
55
+ context 'test from_str' do
56
+
57
+ it 'expect ArgumentError if not called by passing right string' do
58
+ expect { Direction.from_str('east-weast') }.to raise_error(ArgumentError)
59
+ end
60
+
61
+ it 'from_str should return correct Direction constant' do
62
+ expect( Direction.from_str('EAST') ).to eq(Direction::EAST)
63
+ expect( Direction.from_str('south') ).to eq(Direction::SOUTH)
64
+ expect( Direction.from_str('West') ).to eq(Direction::WEST)
65
+ expect( Direction.from_str('NorTH') ).to eq(Direction::NORTH)
66
+ end
67
+
68
+ end
69
+
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,115 @@
1
+ require 'spec_helper'
2
+
3
+ module Robot
4
+ module Simulator
5
+ describe Robot do
6
+
7
+ let(:robot) { Robot.new Table.new(5, 5) }
8
+
9
+ context 'test place method' do
10
+ it 'expect robot current track to be nil before valid PLACE command' do
11
+ expect( robot.current_track ).to be_nil
12
+ end
13
+
14
+ it 'expect robot current track NOT to be nil after valid PLACE command' do
15
+ expect( robot.place(Coordinate.new(0, 0), Direction::EAST) ).to be_truthy
16
+ expect( robot.current_track ).not_to be_nil
17
+ expect( robot.current_track.coordinate.x ).to eq(0)
18
+ expect( robot.current_track.coordinate.y ).to eq(0)
19
+ expect( robot.current_track.facing ).to eq(Direction::EAST)
20
+ end
21
+
22
+ it 'expect robot current track to be nil after invalid PLACE command' do
23
+ expect( robot.place(Coordinate.new(6, 6), Direction::EAST) ).to be_falsey
24
+ expect( robot.current_track ).to be_nil
25
+ end
26
+
27
+ it 'expect robot to stay on track and refuse subsequent invalid PLACE commands' do
28
+ robot.send :current_track=, Track.new(Coordinate.new(0, 0), Direction::EAST)
29
+
30
+ expect( robot.place(Coordinate.new(6, 6), Direction::EAST) ).to be_falsey
31
+ expect( robot.current_track ).not_to be_nil
32
+ expect( robot.current_track.coordinate.x ).to eq(0)
33
+ expect( robot.current_track.coordinate.y ).to eq(0)
34
+ expect( robot.current_track.facing ).to eq(Direction::EAST)
35
+ end
36
+ end
37
+
38
+ context 'test move method' do
39
+ it 'expect robot move to be failed before valid PLACE command' do
40
+ expect( robot.move() ).to be_falsey
41
+ expect( robot.current_track ).to be_nil
42
+ end
43
+
44
+ it 'expect robot move to next track after valid PLACE command' do
45
+ robot.send :current_track=, Track.new(Coordinate.new(0, 0), Direction::EAST)
46
+ expect( robot.move() ).to be_truthy
47
+ expect( robot.current_track.coordinate.x ).to eq(1)
48
+ expect( robot.current_track.coordinate.y ).to eq(0)
49
+ end
50
+
51
+ it 'expect robot to refuse to move to prevent falling from table' do
52
+ robot.send :current_track=, Track.new(Coordinate.new(4, 4), Direction::EAST)
53
+ expect( robot.move() ).to be_falsey
54
+ expect( robot.current_track.coordinate.x ).to eq(4)
55
+ expect( robot.current_track.coordinate.y ).to eq(4)
56
+ end
57
+ end
58
+
59
+ context 'test turn_left' do
60
+ it 'expect robot to ingore LEFT command before valid PLACE command' do
61
+ expect( robot.turn_left() ).to be_falsey
62
+ expect( robot.current_track ).to be_nil
63
+ end
64
+
65
+ it 'expect robot to change facing direction when receive LEFT command' do
66
+ robot.send :current_track=, Track.new(Coordinate.new(0, 0), Direction::EAST)
67
+
68
+ expect( robot.turn_left() ).to be_truthy
69
+ expect( robot.current_track ).not_to be_nil
70
+ expect( robot.current_track.facing ).to eq(Direction::NORTH)
71
+
72
+ expect( robot.turn_left() ).to be_truthy
73
+ expect( robot.current_track ).not_to be_nil
74
+ expect( robot.current_track.facing ).to eq(Direction::WEST)
75
+
76
+ expect( robot.turn_left() ).to be_truthy
77
+ expect( robot.current_track ).not_to be_nil
78
+ expect( robot.current_track.facing ).to eq(Direction::SOUTH)
79
+
80
+ expect( robot.turn_left() ).to be_truthy
81
+ expect( robot.current_track ).not_to be_nil
82
+ expect( robot.current_track.facing ).to eq(Direction::EAST)
83
+ end
84
+ end
85
+
86
+ context 'test turn_right' do
87
+ it 'expect robot to ingore RIGHT command before valid PLACE command' do
88
+ expect( robot.turn_right() ).to be_falsey
89
+ expect( robot.current_track ).to be_nil
90
+ end
91
+
92
+ it 'expect robot to change facing direction when receive RIGHT command' do
93
+ robot.send :current_track=, Track.new(Coordinate.new(0, 0), Direction::EAST)
94
+
95
+ expect( robot.turn_right() ).to be_truthy
96
+ expect( robot.current_track ).not_to be_nil
97
+ expect( robot.current_track.facing ).to eq(Direction::SOUTH)
98
+
99
+ expect( robot.turn_right() ).to be_truthy
100
+ expect( robot.current_track ).not_to be_nil
101
+ expect( robot.current_track.facing ).to eq(Direction::WEST)
102
+
103
+ expect( robot.turn_right() ).to be_truthy
104
+ expect( robot.current_track ).not_to be_nil
105
+ expect( robot.current_track.facing ).to eq(Direction::NORTH)
106
+
107
+ expect( robot.turn_right() ).to be_truthy
108
+ expect( robot.current_track ).not_to be_nil
109
+ expect( robot.current_track.facing ).to eq(Direction::EAST)
110
+ end
111
+ end
112
+
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ module Robot
4
+ module Simulator
5
+ describe Table do
6
+
7
+ let(:table) { Table.new 5, 5 }
8
+
9
+ it 'constructor and getters' do
10
+ expect( table.dimensionX ).to eq(5)
11
+ expect( table.dimensionY ).to eq(5)
12
+ end
13
+
14
+ it 'test is_valid_coordinate?' do
15
+ expect( table.is_valid_coordinate?(Coordinate.new(0, 0)) ).to be true
16
+ expect( table.is_valid_coordinate?(Coordinate.new(4, 0)) ).to be true
17
+ expect( table.is_valid_coordinate?(Coordinate.new(4, 4)) ).to be true
18
+ expect( table.is_valid_coordinate?(Coordinate.new(0, 4)) ).to be true
19
+
20
+ expect( table.is_valid_coordinate?(Coordinate.new(0, -1)) ).to be false
21
+ expect( table.is_valid_coordinate?(Coordinate.new(-1, 0)) ).to be false
22
+ expect( table.is_valid_coordinate?(Coordinate.new(4, -1)) ).to be false
23
+ expect( table.is_valid_coordinate?(Coordinate.new(5, 0)) ).to be false
24
+ expect( table.is_valid_coordinate?(Coordinate.new(4, 5)) ).to be false
25
+ expect( table.is_valid_coordinate?(Coordinate.new(5, 4)) ).to be false
26
+ expect( table.is_valid_coordinate?(Coordinate.new(0, 5)) ).to be false
27
+ expect( table.is_valid_coordinate?(Coordinate.new(-1, 4)) ).to be false
28
+ end
29
+
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ module Robot
4
+ module Simulator
5
+ describe Track do
6
+
7
+ it 'constructor and getters' do
8
+ track = Track.new Coordinate.new(0, 1), Direction::EAST
9
+ expect( track.coordinate.x ).to eq(0)
10
+ expect( track.coordinate.y ).to eq(1)
11
+ expect( track.facing ).to eq(Direction::EAST)
12
+ end
13
+
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,131 @@
1
+ require 'spec_helper'
2
+
3
+ module Robot
4
+ module Simulator
5
+ module UI
6
+ describe CLI do
7
+ let(:cli) { CLI.new }
8
+
9
+ it 'test commands from file' do
10
+ expect( cli ).to receive(:say).with(/Reading robot commands from file/, anything)
11
+ expect( cli ).to receive(:say).exactly 6
12
+ expect( cli ).to receive(:say).with "3,3,NORTH"
13
+
14
+ cli.options = { :file => File.expand_path('../commands.txt', __FILE__) }
15
+ cli.start_game
16
+ end
17
+
18
+ context 'test commands from console' do
19
+ let(:console_prompt) { "Please input robot commands, end with a blank line ..." }
20
+
21
+ it "should process the commands and output the results" do
22
+ expect( cli ).to receive(:say).with console_prompt, anything
23
+ expect( cli ).to receive(:ask).with('>').and_return 'PLACE 1,2,East'
24
+ expect( cli ).to receive(:ask).with('>').and_return 'MOVE'
25
+ expect( cli ).to receive(:ask).with('>').and_return 'MOVE'
26
+ expect( cli ).to receive(:ask).with('>').and_return 'LEFT'
27
+ expect( cli ).to receive(:ask).with('>').and_return 'MOVE'
28
+ expect( cli ).to receive(:ask).with('>').and_return 'REPORT'
29
+ expect( cli ).to receive(:ask).with('>').and_return ''
30
+
31
+ expect( cli ).to receive(:say).exactly 6
32
+ expect( cli ).to receive(:say).with "3,3,NORTH"
33
+
34
+ cli.start_game
35
+ end
36
+
37
+ context 'should report error for invalid commands' do
38
+ it 'report unknow command error' do
39
+ expect( cli ).to receive(:say).with console_prompt, anything
40
+ expect( cli ).to receive(:ask).with('>').and_return 'PLACE2'
41
+ expect( cli ).to receive(:ask).with('>').and_return ''
42
+
43
+ expect( cli ).to receive(:say).once
44
+ expect( cli ).to receive(:error).with "Unknow command is ignored: PLACE2"
45
+
46
+ cli.start_game
47
+ end
48
+
49
+ it 'report error when there is exception' do
50
+ expect( cli ).to receive(:say).with console_prompt, anything
51
+ expect( cli ).to receive(:ask).with('>').and_return 'PLACE 1,2,EastWest'
52
+ expect( cli ).to receive(:ask).with('>').and_return ''
53
+
54
+ expect( cli ).to receive(:say).once
55
+ expect( cli ).to receive(:error).with(/Direction string/)
56
+
57
+ cli.start_game
58
+ end
59
+
60
+ it 'report error when robot would fall' do
61
+ expect( cli ).to receive(:say).with console_prompt, anything
62
+ expect( cli ).to receive(:ask).with('>').and_return 'PLACE 4,4,East'
63
+ expect( cli ).to receive(:ask).with('>').and_return 'MOVE'
64
+ expect( cli ).to receive(:ask).with('>').and_return ''
65
+
66
+ expect( cli ).to receive(:say).twice
67
+ expect( cli ).to receive(:error).with(/Invalid MOVE command/)
68
+
69
+ cli.start_game
70
+ end
71
+
72
+ it 'report error if robot is not on the table' do
73
+ expect( cli ).to receive(:say).with console_prompt, anything
74
+ expect( cli ).to receive(:ask).with('>').and_return 'MOVE'
75
+ expect( cli ).to receive(:ask).with('>').and_return 'LEFT'
76
+ expect( cli ).to receive(:ask).with('>').and_return 'RIGHT'
77
+ expect( cli ).to receive(:ask).with('>').and_return ''
78
+
79
+ expect( cli ).to receive(:say).once
80
+ expect( cli ).to receive(:error).with(/Invalid MOVE command/)
81
+
82
+ expect( cli ).to receive(:say).once
83
+ expect( cli ).to receive(:error).with(/Invalid LEFT command/)
84
+
85
+ expect( cli ).to receive(:say).once
86
+ expect( cli ).to receive(:error).with(/Invalid RIGHT command/)
87
+
88
+ cli.start_game
89
+ end
90
+
91
+ context "should report invalid PLACE commands" do
92
+ it 'report if PLACE on invalid position' do
93
+ expect( cli ).to receive(:say).with console_prompt, anything
94
+ expect( cli ).to receive(:ask).with('>').and_return 'PLACE'
95
+ expect( cli ).to receive(:ask).with('>').and_return ''
96
+
97
+ expect( cli ).to receive(:say).once
98
+ expect( cli ).to receive(:error).with "Command PLACE is ignored because of lack parameters"
99
+
100
+ cli.start_game
101
+ end
102
+
103
+ it 'report no arguments error' do
104
+ expect( cli ).to receive(:say).with console_prompt, anything
105
+ expect( cli ).to receive(:ask).with('>').and_return 'PLACE 5,5,EAST'
106
+ expect( cli ).to receive(:ask).with('>').and_return ''
107
+
108
+ expect( cli ).to receive(:say).once
109
+ expect( cli ).to receive(:error).with(/Invalid PLACE command/)
110
+
111
+ cli.start_game
112
+ end
113
+
114
+ it 'report no enough arguments error' do
115
+ expect( cli ).to receive(:say).with console_prompt, anything
116
+ expect( cli ).to receive(:ask).with('>').and_return 'PLACE 1,2'
117
+ expect( cli ).to receive(:ask).with('>').and_return ''
118
+
119
+ expect( cli ).to receive(:say).once
120
+ expect( cli ).to receive(:error).with "Command PLACE needs 3 parameters in format of x,y,facing, got 1,2"
121
+
122
+ cli.start_game
123
+ end
124
+ end
125
+ end
126
+ end
127
+
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,8 @@
1
+ PLACE 1,2,EAST
2
+ MOVE
3
+ MOVE
4
+
5
+ LEFT
6
+
7
+ MOVE
8
+ REPORT
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ module Robot
4
+ describe Simulator do
5
+
6
+ it 'version is 0.0.1' do
7
+ expect(Simulator::VERSION).to eq('0.0.1')
8
+ end
9
+
10
+ end
11
+ end
@@ -0,0 +1,2 @@
1
+ require 'robot/simulator'
2
+ require 'robot/simulator/ui/cli'
metadata ADDED
@@ -0,0 +1,141 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: robot-simulator
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Andrew Feng
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-11-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.19'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.19'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.3'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: '3.1'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '='
67
+ - !ruby/object:Gem::Version
68
+ version: '3.1'
69
+ description: Robot Simulator Code Exercise
70
+ email:
71
+ - mingliangfeng@gmail.com
72
+ executables:
73
+ - robot-simulator
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - ".ruby-version"
79
+ - Gemfile
80
+ - LICENSE.txt
81
+ - README.md
82
+ - Rakefile
83
+ - bin/robot-simulator
84
+ - lib/robot/simulator.rb
85
+ - lib/robot/simulator/controller.rb
86
+ - lib/robot/simulator/coordinate.rb
87
+ - lib/robot/simulator/direction.rb
88
+ - lib/robot/simulator/robot.rb
89
+ - lib/robot/simulator/table.rb
90
+ - lib/robot/simulator/track.rb
91
+ - lib/robot/simulator/ui/cli.rb
92
+ - lib/robot/simulator/version.rb
93
+ - robot-simulator.gemspec
94
+ - robot-simulator.txt
95
+ - spec/robot/simulator/controller_spec.rb
96
+ - spec/robot/simulator/coordinate_spec.rb
97
+ - spec/robot/simulator/direction_spec.rb
98
+ - spec/robot/simulator/robot_spec.rb
99
+ - spec/robot/simulator/table_spec.rb
100
+ - spec/robot/simulator/track_spec.rb
101
+ - spec/robot/simulator/ui/cli_spec.rb
102
+ - spec/robot/simulator/ui/commands.txt
103
+ - spec/robot/simulator_spec.rb
104
+ - spec/spec_helper.rb
105
+ homepage: https://github.com/mingliangfeng
106
+ licenses:
107
+ - MIT
108
+ metadata: {}
109
+ post_install_message:
110
+ rdoc_options: []
111
+ require_paths:
112
+ - lib
113
+ required_ruby_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ required_rubygems_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ requirements: []
124
+ rubyforge_project:
125
+ rubygems_version: 2.4.2
126
+ signing_key:
127
+ specification_version: 4
128
+ summary: A robot simulator that can receive commands and move around on a table surface.
129
+ It will prevent falling from the table, and can report its position.
130
+ test_files:
131
+ - spec/robot/simulator/controller_spec.rb
132
+ - spec/robot/simulator/coordinate_spec.rb
133
+ - spec/robot/simulator/direction_spec.rb
134
+ - spec/robot/simulator/robot_spec.rb
135
+ - spec/robot/simulator/table_spec.rb
136
+ - spec/robot/simulator/track_spec.rb
137
+ - spec/robot/simulator/ui/cli_spec.rb
138
+ - spec/robot/simulator/ui/commands.txt
139
+ - spec/robot/simulator_spec.rb
140
+ - spec/spec_helper.rb
141
+ has_rdoc: