robert2d-robot 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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