robert2d-robot 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NWJhMzE5NDQ2Mjk2ZDI2MzFhOTQ5NzRjYmMwMWVkOWRjODIzOWU1Nw==
5
+ data.tar.gz: !binary |-
6
+ MDY3MzFmYTQ1YTVlYzZiYTgzYTdkMWQ4ODljOTYyNGRjYmY5N2M4ZQ==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ YTMwNTgyZDlmYjJkNTMwNDdkYjhjNjYyNjEzMDllZDUxNmFiNzA0ODI0NmVh
10
+ OGFjMjIxNDg4YzI3Mzc2MDU1NWU3NzRhNDQxMmRmZTEzYTc1OWU5YzkxYzVi
11
+ ZjMxZjhlMTBjMjIzYjE5YjM0NzMxMmZjNmE2ODFmZDk5YTJhYmI=
12
+ data.tar.gz: !binary |-
13
+ YWQ4ZDcwZWUzZGU2ZjYwMjU0YThiNTk1MWUyNDgzOTVhZmQzYmRlYzZlYzE0
14
+ MWIwMjYzNGVhYTlkY2E5ZDY4NzlhN2FlNTkwYzM5MzY2ZmQ5ODM1NzE2NTI2
15
+ Mzc3MzFjNDVjMzY1YjMyNzBhNDg4NzY1MTg3OTdjYzUwNGU5MjE=
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ coverage
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,7 @@
1
+ AllCops:
2
+ Exclude:
3
+ - '**/Guardfile'
4
+ - 'vendor/**/*'
5
+
6
+ Style/Documentation:
7
+ Enabled: false
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-2.1.2
data/.travis.yml ADDED
@@ -0,0 +1,14 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0
4
+ - 2.1
5
+ - 2.2
6
+ deploy:
7
+ provider: rubygems
8
+ api_key:
9
+ secure: GBnhPCF3UiTHXVjmVl/U4LOtRYKBg3iluB2YYWZuBt8LPRSdi41VI6TEVhJ9S7XpJQhmt9Nq3PGr2b74fqmBGAmSAaCKfnHHLrc5SqKdzc0zd9h5VORHGmUJWp/w1Qq/96fnpMfbNqlBIdAs1JaXVuwV0H8KAeTRASzLNjfahoeZR1uWzKLsvgHmJ17qAC/v3mZBfaYY6nnf1GBOs9N6WebaR/MC8SvTxmiLldl/QVuMUdiSJURlJ0dJnHMLq9P1LB0t4ECNLmC7hNKfnTJ3+CPq5Ow7go3Yt1/46liaa0edkXja05VHaBijEQColQqNkjfxL/Xb/n/QnHGIcZ4Au961Nw57/CZLmJ/U6bRXjamtkodKI8eYAS/3bAdVHr/266ZAHXj5jGqzjpIPqDueuOAR+b1iNlyCw5kKEcm2plPLIxslUmrZIPH4VHcPHvXcal46AX6BH399Mv2RZ9iyxyTZlrPQErl+urN8Gu+Zp2kmjtaA5QrUwaS50uj8eRL/tSz7mtrQn/2lw6JPcB1tzla5LpslDD39OZFa5Ejs8vNzbrYpSda5IY1bBjpqZ+O/3T1s54bqVNxWTLiGLXwWnexLldc6L5u+fgsUK0saQ8KlUQtSIVW+iVcVcWBhgkFN522S8EofF79AX2MtVJkw7679YywuLAplPbLyreunIsk=
10
+ gem: robert2d-robot
11
+ gemspec: robert2d-robot.gemspec
12
+ on:
13
+ tags: true
14
+ repo: robert2d/robot
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,99 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ robert2d-robot (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ ast (2.0.0)
10
+ astrolabe (1.3.1)
11
+ parser (~> 2.2)
12
+ codeclimate-test-reporter (0.2.0)
13
+ simplecov (>= 0.7.1, < 1.0.0)
14
+ coderay (1.1.0)
15
+ diff-lcs (1.2.5)
16
+ docile (1.1.5)
17
+ ffi (1.9.10)
18
+ formatador (0.2.5)
19
+ guard (2.12.8)
20
+ formatador (>= 0.2.4)
21
+ listen (>= 2.7, <= 4.0)
22
+ lumberjack (~> 1.0)
23
+ nenv (~> 0.1)
24
+ notiffany (~> 0.0)
25
+ pry (>= 0.9.12)
26
+ shellany (~> 0.0)
27
+ thor (>= 0.18.1)
28
+ guard-compat (1.2.1)
29
+ guard-rspec (4.6.2)
30
+ guard (~> 2.1)
31
+ guard-compat (~> 1.1)
32
+ rspec (>= 2.99.0, < 4.0)
33
+ json (1.8.3)
34
+ listen (3.0.2)
35
+ rb-fsevent (>= 0.9.3)
36
+ rb-inotify (>= 0.9)
37
+ lumberjack (1.0.9)
38
+ method_source (0.8.2)
39
+ nenv (0.2.0)
40
+ notiffany (0.0.6)
41
+ nenv (~> 0.1)
42
+ shellany (~> 0.0)
43
+ parser (2.2.2.6)
44
+ ast (>= 1.1, < 3.0)
45
+ powerpack (0.1.1)
46
+ pry (0.10.1)
47
+ coderay (~> 1.1.0)
48
+ method_source (~> 0.8.1)
49
+ slop (~> 3.4)
50
+ rainbow (2.0.0)
51
+ rake (10.4.2)
52
+ rb-fsevent (0.9.5)
53
+ rb-inotify (0.9.5)
54
+ ffi (>= 0.5.0)
55
+ rspec (3.3.0)
56
+ rspec-core (~> 3.3.0)
57
+ rspec-expectations (~> 3.3.0)
58
+ rspec-mocks (~> 3.3.0)
59
+ rspec-core (3.3.2)
60
+ rspec-support (~> 3.3.0)
61
+ rspec-expectations (3.3.1)
62
+ diff-lcs (>= 1.2.0, < 2.0)
63
+ rspec-support (~> 3.3.0)
64
+ rspec-mocks (3.3.2)
65
+ diff-lcs (>= 1.2.0, < 2.0)
66
+ rspec-support (~> 3.3.0)
67
+ rspec-support (3.3.0)
68
+ rubocop (0.32.1)
69
+ astrolabe (~> 1.3)
70
+ parser (>= 2.2.2.5, < 3.0)
71
+ powerpack (~> 0.1)
72
+ rainbow (>= 1.99.1, < 3.0)
73
+ ruby-progressbar (~> 1.4)
74
+ ruby-progressbar (1.7.5)
75
+ shellany (0.0.1)
76
+ simplecov (0.10.0)
77
+ docile (~> 1.1.0)
78
+ json (~> 1.8)
79
+ simplecov-html (~> 0.10.0)
80
+ simplecov-html (0.10.0)
81
+ slop (3.6.0)
82
+ thor (0.19.1)
83
+
84
+ PLATFORMS
85
+ ruby
86
+
87
+ DEPENDENCIES
88
+ bundler (~> 1.10.5)
89
+ codeclimate-test-reporter (~> 0.2.0)
90
+ guard (~> 2.12.8)
91
+ guard-rspec (~> 4.6.2)
92
+ rake (~> 10.4.2)
93
+ robert2d-robot!
94
+ rspec (~> 3.3.0)
95
+ rubocop (~> 0.32.1)
96
+ simplecov (~> 0.10.0)
97
+
98
+ BUNDLED WITH
99
+ 1.10.5
data/Guardfile ADDED
@@ -0,0 +1,13 @@
1
+ # To exclude automatic running of tests, use the group filter e.g.
2
+ # `be guard -g no-tests`
3
+ # The above will only run global definitions and the group 'no-tests',
4
+ # which doesn't exist. This is to support starting a guard process in one
5
+ # window and then manually running `bundle exec rspec spec/<spec>` in
6
+ # another.
7
+ group :tests do
8
+ guard :rspec, cmd: 'rspec --color' do
9
+ watch(%r{^spec/.+_spec\.rb$})
10
+ watch('spec/spec_helper.rb') { 'spec/' }
11
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
12
+ end
13
+ end
data/README.md ADDED
@@ -0,0 +1,33 @@
1
+ # TOY ROBOT TEST
2
+ [![Build Status](https://travis-ci.org/robert2d/robot.svg?branch=master)](https://travis-ci.org/robert2d/robot)
3
+ [![Test Coverage](https://codeclimate.com/github/robert2d/robot/badges/coverage.svg)](https://codeclimate.com/github/robert2d/robot/coverage)
4
+ [![Code Climate](https://codeclimate.com/github/robert2d/robot/badges/gpa.svg)](https://codeclimate.com/github/robert2d/robot)
5
+
6
+ See instructions.txt for the specification. If run on OSX you can turn your speakers on to get more feedback from the robot. There is also a visual component to the REPORT command, which even though it wasn't required I left in anyway.
7
+
8
+ ## INSTALLING
9
+
10
+ git clone git://github.com/robert2d/rea-robot.git
11
+ cd rea-robot
12
+ gem install bundler
13
+ bundle install
14
+
15
+ ## USE
16
+
17
+ ### Interactive:
18
+
19
+ $ ./robot.rb
20
+ PLACE 1,2,EAST
21
+ MOVE
22
+ MOVE
23
+ LEFT
24
+ MOVE
25
+ REPORT
26
+
27
+ ### Passing a file:
28
+
29
+ $ ./robot.rb test_data.txt
30
+
31
+ ## TESTS
32
+
33
+ $ rspec
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ require 'bundler'
2
+ require 'rubocop/rake_task'
3
+ require 'rspec/core'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RuboCop::RakeTask.new do |t|
7
+ t.formatters = %w(progress json)
8
+ t.options = %w(--out coverage/rubocop.json)
9
+ end
10
+
11
+ RSpec::Core::RakeTask.new(:spec) do |spec|
12
+ spec.pattern = FileList['spec/**/*_spec.rb']
13
+ end
14
+
15
+ task default: [:spec, :rubocop]
data/bin/robot ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ require 'robot'
3
+
4
+ # rubocop:disable Lint/AssignmentInCondition
5
+ while command = gets
6
+ Robot::CommandCentre.instance.parse_and_execute(command)
7
+ end
8
+ # rubocop:enable Lint/AssignmentInCondition
data/instructions.txt ADDED
@@ -0,0 +1,61 @@
1
+ -----------
2
+ Toy Robot Simulator
3
+
4
+ Description:
5
+ . The application is a simulation of a toy robot moving on a square tabletop, of dimensions 5 units x 5 units.
6
+ . There are no other obstructions on the table surface.
7
+ . The robot is free to roam around the surface of the table, but must be prevented from falling to destruction. Any movement
8
+ that would result in the robot falling from the table must be prevented, however further valid movement commands must still
9
+ be allowed.
10
+
11
+
12
+ . Create an application that can read in commands of the following form -
13
+ PLACE X,Y,F
14
+ MOVE
15
+ LEFT
16
+ RIGHT
17
+ REPORT
18
+
19
+ . PLACE will put the toy robot on the table in position X,Y and facing NORTH, SOUTH, EAST or WEST.
20
+ . The origin (0,0) can be considered to be the SOUTH WEST most corner.
21
+ . 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.
22
+ . MOVE will move the toy robot one unit forward in the direction it is currently facing.
23
+ . LEFT and RIGHT will rotate the robot 90 degrees in the specified direction without changing the position of the robot.
24
+ . REPORT will announce the X,Y and F of the robot. This can be in any form, but standard output is sufficient.
25
+
26
+ . A robot that is not on the table can choose the ignore the MOVE, LEFT, RIGHT and REPORT commands.
27
+ . Input can be from a file, or from standard input, as the developer chooses.
28
+ . Provide test data to exercise the application.
29
+
30
+
31
+ Constraints:
32
+ The toy robot must not fall off the table during movement. This also includes the initial placement of the toy robot.
33
+ Any move that would cause the robot to fall must be ignored.
34
+
35
+ Example Input and Output:
36
+ a)
37
+ PLACE 0,0,NORTH
38
+ MOVE
39
+ REPORT
40
+ Output: 0,1,NORTH
41
+
42
+ b)
43
+ PLACE 0,0,NORTH
44
+ LEFT
45
+ REPORT
46
+ Output: 0,0,WEST
47
+
48
+ c)
49
+ PLACE 1,2,EAST
50
+ MOVE
51
+ MOVE
52
+ LEFT
53
+ MOVE
54
+ REPORT
55
+ Output: 3,3,NORTH
56
+
57
+
58
+ Deliverables:
59
+ The Ruby source files, the test data and any test code.
60
+ It is not required to provide any graphical output showing the movement of the toy robot.
61
+ -------
data/lib/robot.rb ADDED
@@ -0,0 +1,95 @@
1
+ require 'singleton'
2
+ require 'robot/table'
3
+ require 'robot/matrix'
4
+ require 'robot/command_centre'
5
+
6
+ class Robot
7
+ include Singleton
8
+
9
+ MOVEMENT_DIRECTIONS = {
10
+ north: { x: 0, y: 1 },
11
+ east: { x: 1, y: 0 },
12
+ south: { x: 0, y: -1 },
13
+ west: { x: -1, y: 0 }
14
+ }
15
+
16
+ attr_reader :table, :direction
17
+
18
+ def initialize
19
+ @table = Table.instance
20
+ end
21
+
22
+ ##
23
+ # trys to move in current direction one place
24
+ def move
25
+ postition = new_position
26
+ return if table.place(postition[:x], postition[:y])
27
+ puts 'Cannot move off the table'
28
+ CommandCentre.instance.say('Does not compute, I will fall to my death')
29
+ end
30
+
31
+ ##
32
+ # rotates compass faces and uses current face as index
33
+ # to retrieve new direction
34
+ def right
35
+ rotate(:right)
36
+ end
37
+
38
+ def left
39
+ rotate(:left)
40
+ end
41
+
42
+ ##
43
+ # outputs the x,y,position of the robot also includes a position matrix
44
+ def report
45
+ puts to_s
46
+ puts Matrix.new(table.position, direction).to_s
47
+ say_position
48
+ end
49
+
50
+ def to_s
51
+ "#{table.position[:x]}, #{table.position[:y]} #{direction.upcase}"
52
+ end
53
+
54
+ def place(args)
55
+ return unless args
56
+ x, y, face = args.split(',')
57
+ return unless x && y && face
58
+ return unless table.place(x, y)
59
+ self.direction = face.downcase.to_sym
60
+ end
61
+
62
+ private
63
+
64
+ def say_position
65
+ CommandCentre.instance.say("My position is currently #{table.position[:x]}/
66
+ by #{table.position[:y]} and facing #{direction}")
67
+ end
68
+
69
+ ##
70
+ # fetches new coordinates and adds them to existing coords
71
+ def new_position
72
+ table.position.merge(move_coordinates) { |_key, memo, value| memo + value }
73
+ end
74
+
75
+ def move_coordinates
76
+ MOVEMENT_DIRECTIONS[direction]
77
+ end
78
+
79
+ def direction=(face)
80
+ @direction = face if MOVEMENT_DIRECTIONS.keys.include?(face)
81
+ end
82
+
83
+ def rotate(direction)
84
+ index = direction == :left ? -1 : 1
85
+ self.direction = faces.rotate(index)[face_index]
86
+ end
87
+
88
+ def faces
89
+ MOVEMENT_DIRECTIONS.keys
90
+ end
91
+
92
+ def face_index
93
+ faces.index(direction)
94
+ end
95
+ end
@@ -0,0 +1,46 @@
1
+ require 'singleton'
2
+
3
+ class Robot
4
+ class CommandCentre
5
+ include Singleton
6
+
7
+ COMMANDS = %w(PLACE MOVE LEFT RIGHT REPORT)
8
+
9
+ def parse_and_execute(command_string)
10
+ command = Command.new(command_string.strip.upcase)
11
+ return unless command.validate
12
+ command.execute
13
+ end
14
+
15
+ ##
16
+ # feature just for fun only for mac users
17
+ def say(message)
18
+ IO.popen(
19
+ "say -v Ralph '#{message}'"
20
+ ) if RbConfig::CONFIG['host_os'].match(/darwin|mac os/)
21
+ end
22
+
23
+ class Command
24
+ attr_reader :command, :args, :robot
25
+
26
+ def initialize(command_string)
27
+ @command, @args = command_string.split(' ')
28
+ @robot = Robot.instance
29
+ end
30
+
31
+ def execute
32
+ return robot.place(args) if place?
33
+ return robot.send(:"#{command.downcase}") if robot.table.placed?
34
+ CommandCentre.instance.say('Does not compute, You must place me first')
35
+ end
36
+
37
+ def place?
38
+ command == 'PLACE'
39
+ end
40
+
41
+ def validate
42
+ COMMANDS.include?(command)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,53 @@
1
+ class Robot
2
+ class Matrix
3
+ attr_reader :position, :direction
4
+ attr_accessor :matrix
5
+
6
+ DIRECTION_ARROWS = {
7
+ north: '^',
8
+ east: '>',
9
+ south: 'v',
10
+ west: '<'
11
+ }
12
+
13
+ def initialize(position, direction)
14
+ @position = position
15
+ @direction = direction
16
+ @matrix = generate_matrix
17
+ end
18
+
19
+ ##
20
+ # puts an arrow pointing in the direction of the current x,y of the robot
21
+ # on a matrix and returns a string of the matrix
22
+ def to_s
23
+ set_current_position!
24
+ matrix.reverse.map { |row| row.join(' ') }.join("\n")
25
+ end
26
+
27
+ private
28
+
29
+ ##
30
+ # sets the current position x,y coords to a directional arrow
31
+ def set_current_position!
32
+ matrix[
33
+ position[:y]
34
+ ][
35
+ position[:x]
36
+ ] = DIRECTION_ARROWS[direction]
37
+ end
38
+
39
+ ##
40
+ # constructs a matrix of positions
41
+ # Output:
42
+ # [
43
+ # ["X", "X", "X", "X", "X"],
44
+ # ["X", "X", "X", "X", "X"],
45
+ # ["X", "X", "X", "X", "X"],
46
+ # ["X", "X", "X", "X", "X"],
47
+ # ["X", "X", "X", "X", "X"]
48
+ # ]
49
+ def generate_matrix
50
+ [].tap { |a| 25.times { a << 'X' } }.each_slice(5).to_a
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,29 @@
1
+ require 'singleton'
2
+
3
+ class Robot
4
+ class Table
5
+ include Singleton
6
+
7
+ attr_accessor :position
8
+ attr_reader :width, :height
9
+
10
+ def initialize
11
+ @width = 4
12
+ @height = 4
13
+ end
14
+
15
+ def place(x, y)
16
+ position = { x: x.to_i, y: y.to_i }
17
+ self.position = position if valid_position?(position)
18
+ end
19
+
20
+ def placed?
21
+ !position.nil?
22
+ end
23
+
24
+ def valid_position?(position)
25
+ position[:x] <= width && position[:x] >= 0 && \
26
+ position[:y] <= height && position[:y] >= 0
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,3 @@
1
+ class Robot
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'robot/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'robert2d-robot'
8
+ spec.version = Robot::VERSION
9
+ spec.authors = ['Dave Robertson']
10
+ spec.email = ['dave.andrew.robertson.nz@gmail.com']
11
+ spec.summary = 'Toy Robot CLI'
12
+ spec.homepage = ''
13
+ spec.license = 'MIT'
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(spec)/})
18
+ spec.require_paths = ['lib']
19
+
20
+ spec.add_development_dependency 'bundler', '~>1.10.5'
21
+ spec.add_development_dependency 'rake', '~>10.4.2'
22
+ spec.add_development_dependency 'rspec', '~>3.3.0'
23
+ spec.add_development_dependency 'guard', '~>2.12.8'
24
+ spec.add_development_dependency 'guard-rspec', '~>4.6.2'
25
+ spec.add_development_dependency 'simplecov', '~>0.10.0'
26
+ spec.add_development_dependency 'rubocop', '~>0.32.1'
27
+ spec.add_development_dependency 'codeclimate-test-reporter', '~>0.2.0'
28
+ end
@@ -0,0 +1,90 @@
1
+ describe Robot::CommandCentre do
2
+ let(:command) { Robot::CommandCentre::Command }
3
+ let(:commands) { Robot::CommandCentre::COMMANDS }
4
+
5
+ subject { Singleton.__init__(described_class).instance }
6
+
7
+ before do
8
+ allow(subject).to receive(:puts)
9
+ end
10
+
11
+ describe '#parse_and_execute' do
12
+ context 'invalid command' do
13
+ let(:command) { double('Command') }
14
+
15
+ it 'returns without executing' do
16
+ expect(described_class::Command).to receive(:new).and_return(command)
17
+ expect(command).to receive(:validate)
18
+ expect(command).not_to receive(:execute)
19
+ subject.parse_and_execute('test')
20
+ end
21
+ end
22
+
23
+ context 'valid command' do
24
+ it 'validates and executes command' do
25
+ expect_any_instance_of(command).to receive(:execute)
26
+ subject.parse_and_execute(commands.sample)
27
+ end
28
+ end
29
+ end
30
+
31
+ describe Robot::CommandCentre::Command do
32
+ describe '#new' do
33
+ subject { command }
34
+
35
+ it 'constucts valid Command' do
36
+ expect(subject.new('test')).to respond_to(:command, :args, :robot)
37
+ end
38
+ end
39
+
40
+ describe '#validate' do
41
+ context 'is invalid' do
42
+ subject { command.new('test') }
43
+
44
+ it 'returns false' do
45
+ expect(subject.validate).not_to be
46
+ end
47
+ end
48
+
49
+ context 'is valid' do
50
+ subject { command.new(commands.sample) }
51
+
52
+ it 'returns false' do
53
+ expect(subject.validate).to be
54
+ end
55
+ end
56
+ end
57
+
58
+ describe '#execute' do
59
+ context 'with PLACE command' do
60
+ subject { command.new('PLACE 1,2,north') }
61
+
62
+ it 'delegates to robot passing args' do
63
+ expect(subject.robot).to receive(:place).with('1,2,north')
64
+ subject.execute
65
+ end
66
+ end
67
+
68
+ context 'with all other commands' do
69
+ subject { command.new('move') }
70
+
71
+ context 'when not placed' do
72
+ it 'delegates to robot' do
73
+ expect(Robot::CommandCentre.instance).to receive(:say)
74
+ expect(subject.robot.table).to receive(:placed?)
75
+ expect(subject.robot).not_to receive(:move)
76
+ subject.execute
77
+ end
78
+ end
79
+
80
+ context 'when placed' do
81
+ it 'delegates to robot' do
82
+ expect(subject.robot.table).to receive(:placed?).and_return(true)
83
+ expect(subject.robot).to receive(:move)
84
+ subject.execute
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,18 @@
1
+ describe Robot::Matrix do
2
+ subject { described_class.new({ x: 1, y: 1 }, :north) }
3
+
4
+ describe '#to_s' do
5
+ let(:expected) do
6
+ <<-TEXT.gsub(/^\s+/, '')
7
+ X X X X X
8
+ X X X X X
9
+ X X X X X
10
+ X ^ X X X
11
+ X X X X X
12
+ TEXT
13
+ end
14
+ it 'provides correct matrix for given inputs' do
15
+ expect(subject.to_s).to eq(expected.strip)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,54 @@
1
+ describe Robot::Table do
2
+ # ensure fresh singleton for each spec
3
+ subject(:table) { Singleton.__init__(Robot::Table).instance }
4
+
5
+ describe '#place' do
6
+ context 'with valid position' do
7
+ it 'sets position on table' do
8
+ table.place('1', '2')
9
+ expect(table.placed?).to be
10
+ expect(table.position).to eq(x: 1, y: 2)
11
+ end
12
+ end
13
+ context 'with invalid position' do
14
+ it 'does not set position' do
15
+ table.place('-1', '7')
16
+ expect(table.placed?).not_to be
17
+ expect(table.position).to be_nil
18
+ end
19
+ end
20
+ end
21
+
22
+ describe '#placed?' do
23
+ it 'is false when not placed yet' do
24
+ expect(table.placed?).not_to be
25
+ end
26
+ it 'is true when has position' do
27
+ table.place('1', '2')
28
+ expect(table.placed?).to be
29
+ end
30
+ end
31
+
32
+ describe '#valid_position?' do
33
+ context 'with invalid position coordinates' do
34
+ let(:position) do
35
+ {
36
+ x: -1,
37
+ y: 10
38
+ }
39
+ end
40
+ it 'returns false' do
41
+ expect(table.valid_position?(position)).not_to be
42
+ end
43
+ end
44
+ context 'with all valid position coordinates' do
45
+ it 'returns true' do
46
+ 5.times do |x|
47
+ 5.times do |y|
48
+ expect(table.valid_position?(x: x, y: y)).to be
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,151 @@
1
+ describe Robot do
2
+ let(:position) do
3
+ {
4
+ x: 1,
5
+ y: 2
6
+ }
7
+ end
8
+
9
+ # ensure fresh singleton for each spec
10
+ subject(:robot) { Singleton.__init__(Robot).instance }
11
+
12
+ before do
13
+ allow(robot).to receive(:puts)
14
+ end
15
+
16
+ it 'has a version' do
17
+ expect(Robot::VERSION).to be
18
+ end
19
+
20
+ describe 'relationship to table' do
21
+ it 'should create relationship to table' do
22
+ expect(robot).to respond_to(:table)
23
+ expect(robot.table).to be_an_instance_of(Robot::Table)
24
+ end
25
+ end
26
+
27
+ describe '#move' do
28
+ context 'with valid placement on table' do
29
+ it 'returns valid placement' do
30
+ expect(robot).to receive(:new_position).and_return(position)
31
+ expect(robot.table).to receive(:place).and_call_original
32
+ expect(Robot::CommandCentre.instance).not_to receive(:say)
33
+ robot.move
34
+ end
35
+ end
36
+
37
+ context 'invalid placement on table' do
38
+ it 'alerts user of failed move' do
39
+ expect(robot).to receive(:new_position).and_return(position)
40
+ expect(robot.table).to receive(:place)
41
+ expect(Robot::CommandCentre.instance).to receive(:say)
42
+ robot.move
43
+ end
44
+ end
45
+ end
46
+
47
+ describe '#new_position' do
48
+ it 'fetches coordinates for move' do
49
+ expect(robot.table).to receive(:position).and_return(position)
50
+ expect(robot).to receive(:move_coordinates).and_return(position)
51
+ expect(robot.send(:new_position)).to eq(x: 2, y: 4)
52
+ end
53
+ end
54
+
55
+ describe '#to_s' do
56
+ it 'provides position and direction in string' do
57
+ robot.send(:direction=, :north)
58
+ allow(robot.table).to receive(:position).and_return(position)
59
+ expect(robot.to_s).to eq('1, 2 NORTH')
60
+ end
61
+ end
62
+
63
+ describe '#move_coordinates' do
64
+ it 'returns coords from current face' do
65
+ robot.send(:direction=, :north)
66
+ expect(robot.send(:move_coordinates)).to eq(x: 0, y: 1)
67
+ end
68
+ end
69
+
70
+ describe '#right' do
71
+ it 'returns correct face clockwise' do
72
+ robot.send(:direction=, :north)
73
+ expect(robot).to receive(:direction=).with(:east)
74
+ expect(robot.right).to eq(:east)
75
+ end
76
+ end
77
+
78
+ describe '#left' do
79
+ it 'returns correct face anti-clockwise' do
80
+ robot.send(:direction=, :west)
81
+ expect(robot).to receive(:direction=).with(:south)
82
+ expect(robot.left).to eq(:south)
83
+ end
84
+ end
85
+
86
+ describe '#report' do
87
+ let(:table) { double(Robot::Table, position: { x: 1, y: 2 }) }
88
+
89
+ it 'outputs text and calls say' do
90
+ expect(robot).to receive(:direction).exactly(3).times.and_return(:north)
91
+ allow(robot).to receive(:table).and_return(table)
92
+ expect(Robot::CommandCentre.instance).to receive(:say)
93
+ robot.report
94
+ end
95
+ end
96
+
97
+ describe '#place' do
98
+ context 'with invalid args' do
99
+ it 'does nothing with nil args' do
100
+ expect(robot.table).not_to receive(:place)
101
+ robot.place(nil)
102
+ end
103
+
104
+ it 'does nothing with blank string' do
105
+ expect(robot.table).not_to receive(:place)
106
+ robot.place(' ')
107
+ end
108
+
109
+ it 'does nothing with malformed args' do
110
+ expect(robot.table).not_to receive(:place)
111
+ robot.place('1,2north')
112
+ robot.place('north')
113
+ end
114
+ end
115
+
116
+ context 'with valid args' do
117
+ context 'and valid placement' do
118
+ it 'sets direction' do
119
+ expect(robot.table).to receive(:place).with(
120
+ '1', '2'
121
+ ).and_call_original
122
+ expect(robot).to receive(:direction=).with(:north)
123
+ robot.place('1,2,north')
124
+ end
125
+ end
126
+
127
+ context 'and invalid placement' do
128
+ it 'does not set direction when place fails' do
129
+ expect(robot.table).to receive(:place).with('1', '2')
130
+ expect(robot).not_to receive(:direction=)
131
+ robot.place('1,2,north')
132
+ end
133
+ end
134
+ end
135
+ end
136
+
137
+ describe '#direction=' do
138
+ context 'direction is invalid' do
139
+ it 'does not save position' do
140
+ robot.send(:direction=, :up)
141
+ expect(robot.direction).not_to be
142
+ end
143
+ end
144
+ context 'direction is valid' do
145
+ it 'does save to singleton' do
146
+ robot.send(:direction=, :west)
147
+ expect(robot.direction).to eq(:west)
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,15 @@
1
+ require 'rubygems'
2
+ require 'json'
3
+ require 'simplecov'
4
+ require 'codeclimate-test-reporter'
5
+ CodeClimate::TestReporter.start
6
+
7
+ SimpleCov.start
8
+
9
+ require 'robot'
10
+
11
+ RSpec.configure do |config|
12
+ config.disable_monkey_patching!
13
+ config.expose_dsl_globally = true
14
+ config.order = :random
15
+ end
data/test_data.txt ADDED
@@ -0,0 +1,20 @@
1
+ PLACE 1,2,EAST
2
+ MOVE
3
+ MOVE
4
+ LEFT
5
+ MOVE
6
+ RIGHT
7
+ MOVE
8
+ LEFT
9
+ LEFT
10
+ MOVE
11
+ MOVE
12
+ MOVE
13
+ MOVE
14
+ LEFT
15
+ MOVE
16
+ MOVE
17
+ MOVE
18
+ RIGHT
19
+ RIGHT
20
+ REPORT
metadata ADDED
@@ -0,0 +1,186 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: robert2d-robot
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Dave Robertson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-07-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 1.10.5
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 1.10.5
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 10.4.2
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: 10.4.2
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: 3.3.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 3.3.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: guard
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 2.12.8
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: 2.12.8
69
+ - !ruby/object:Gem::Dependency
70
+ name: guard-rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: 4.6.2
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: 4.6.2
83
+ - !ruby/object:Gem::Dependency
84
+ name: simplecov
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: 0.10.0
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: 0.10.0
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ~>
102
+ - !ruby/object:Gem::Version
103
+ version: 0.32.1
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ~>
109
+ - !ruby/object:Gem::Version
110
+ version: 0.32.1
111
+ - !ruby/object:Gem::Dependency
112
+ name: codeclimate-test-reporter
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ~>
116
+ - !ruby/object:Gem::Version
117
+ version: 0.2.0
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ~>
123
+ - !ruby/object:Gem::Version
124
+ version: 0.2.0
125
+ description:
126
+ email:
127
+ - dave.andrew.robertson.nz@gmail.com
128
+ executables:
129
+ - robot
130
+ extensions: []
131
+ extra_rdoc_files: []
132
+ files:
133
+ - .gitignore
134
+ - .rspec
135
+ - .rubocop.yml
136
+ - .ruby-version
137
+ - .travis.yml
138
+ - Gemfile
139
+ - Gemfile.lock
140
+ - Guardfile
141
+ - README.md
142
+ - Rakefile
143
+ - bin/robot
144
+ - instructions.txt
145
+ - lib/robot.rb
146
+ - lib/robot/command_centre.rb
147
+ - lib/robot/matrix.rb
148
+ - lib/robot/table.rb
149
+ - lib/robot/version.rb
150
+ - robert2d-robot.gemspec
151
+ - spec/lib/robot/command_centre_spec.rb
152
+ - spec/lib/robot/matrix_spec.rb
153
+ - spec/lib/robot/table_spec.rb
154
+ - spec/lib/robot_spec.rb
155
+ - spec/spec_helper.rb
156
+ - test_data.txt
157
+ homepage: ''
158
+ licenses:
159
+ - MIT
160
+ metadata: {}
161
+ post_install_message:
162
+ rdoc_options: []
163
+ require_paths:
164
+ - lib
165
+ required_ruby_version: !ruby/object:Gem::Requirement
166
+ requirements:
167
+ - - ! '>='
168
+ - !ruby/object:Gem::Version
169
+ version: '0'
170
+ required_rubygems_version: !ruby/object:Gem::Requirement
171
+ requirements:
172
+ - - ! '>='
173
+ - !ruby/object:Gem::Version
174
+ version: '0'
175
+ requirements: []
176
+ rubyforge_project:
177
+ rubygems_version: 2.4.5
178
+ signing_key:
179
+ specification_version: 4
180
+ summary: Toy Robot CLI
181
+ test_files:
182
+ - spec/lib/robot/command_centre_spec.rb
183
+ - spec/lib/robot/matrix_spec.rb
184
+ - spec/lib/robot/table_spec.rb
185
+ - spec/lib/robot_spec.rb
186
+ - spec/spec_helper.rb