loco_bot 1.0.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 +7 -0
- data/.gitignore +14 -0
- data/.rspec +3 -0
- data/.travis.yml +6 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +393 -0
- data/Rakefile +9 -0
- data/bin/loco_bot_cli +10 -0
- data/lib/loco_bot/cli/command/base.rb +28 -0
- data/lib/loco_bot/cli/command/hodor.rb +14 -0
- data/lib/loco_bot/cli/command/left.rb +14 -0
- data/lib/loco_bot/cli/command/move.rb +14 -0
- data/lib/loco_bot/cli/command/place.rb +17 -0
- data/lib/loco_bot/cli/command/report.rb +18 -0
- data/lib/loco_bot/cli/command/right.rb +14 -0
- data/lib/loco_bot/cli/command.rb +34 -0
- data/lib/loco_bot/cli.rb +67 -0
- data/lib/loco_bot/robot/direction/east.rb +37 -0
- data/lib/loco_bot/robot/direction/north.rb +37 -0
- data/lib/loco_bot/robot/direction/south.rb +37 -0
- data/lib/loco_bot/robot/direction/west.rb +37 -0
- data/lib/loco_bot/robot/direction.rb +27 -0
- data/lib/loco_bot/robot.rb +102 -0
- data/lib/loco_bot/table.rb +94 -0
- data/lib/loco_bot/version.rb +4 -0
- data/lib/loco_bot.rb +8 -0
- data/loco_bot.gemspec +26 -0
- data/spec/loco_bot/cli/command/hodor_spec.rb +14 -0
- data/spec/loco_bot/cli/command/left_spec.rb +14 -0
- data/spec/loco_bot/cli/command/move_spec.rb +14 -0
- data/spec/loco_bot/cli/command/place_spec.rb +14 -0
- data/spec/loco_bot/cli/command/report_spec.rb +22 -0
- data/spec/loco_bot/cli/command/right_spec.rb +14 -0
- data/spec/loco_bot/cli/command_spec.rb +53 -0
- data/spec/loco_bot/cli_spec.rb +37 -0
- data/spec/loco_bot/robot/direction/east_spec.rb +25 -0
- data/spec/loco_bot/robot/direction/north_spec.rb +25 -0
- data/spec/loco_bot/robot/direction/south_spec.rb +25 -0
- data/spec/loco_bot/robot/direction/west_spec.rb +25 -0
- data/spec/loco_bot/robot/direction_spec.rb +35 -0
- data/spec/loco_bot/robot_spec.rb +247 -0
- data/spec/loco_bot/table_spec.rb +197 -0
- data/spec/spec_helper.rb +31 -0
- data/spec/support/test_1.txt +3 -0
- data/spec/support/test_2.txt +3 -0
- data/spec/support/test_3.txt +6 -0
- data/spec/support/test_4.txt +28 -0
- metadata +183 -0
data/lib/loco_bot/cli.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'loco_bot/cli/command'
|
2
|
+
|
3
|
+
module LocoBot
|
4
|
+
# Provides a 'command line'-like interface to interact with the API,
|
5
|
+
# in a limited way: only one Robot and Table instances are available.
|
6
|
+
class CLI
|
7
|
+
# The regexp used to parse input.
|
8
|
+
INPUT_SPLIT_REGEXP = / |,|\s/
|
9
|
+
|
10
|
+
# @!attribute [r] table
|
11
|
+
# @return [Robot] the Robot
|
12
|
+
attr_reader :robot
|
13
|
+
|
14
|
+
# @!attribute [r] table
|
15
|
+
# @return [Table] the Table
|
16
|
+
attr_reader :table
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
@robot = Robot.new
|
20
|
+
@table = Table.new
|
21
|
+
end
|
22
|
+
|
23
|
+
# Reads input for commands.
|
24
|
+
# @param input [String] the input
|
25
|
+
# @note Available commands are:
|
26
|
+
# - PLACE X,Y,F: put the robot on the table in position X,Y and facing NORTH, SOUTH, EAST or WEST.
|
27
|
+
# - MOVE: move the robot one unit forward in the direction it is currently facing.
|
28
|
+
# - LEFT and RIGHT rotate the robot 90 degrees in the specified direction without changing its position.
|
29
|
+
# - REPORT announces the X,Y and F of the robot.
|
30
|
+
# - HODOR outputs a friendly greating.
|
31
|
+
# @return [void]
|
32
|
+
def input!(input)
|
33
|
+
parsed_data = parse_input(input)
|
34
|
+
|
35
|
+
return if parsed_data[:command].nil?
|
36
|
+
|
37
|
+
command_class = Command.class_from_name(parsed_data[:command])
|
38
|
+
|
39
|
+
if command_class.nil?
|
40
|
+
puts "#{parsed_data[:command].upcase}: not a known command. Valid commands are #{CLI::Command.list.join(', ')}."
|
41
|
+
else
|
42
|
+
execute_command(command_class, parsed_data[:arguments])
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def execute_command(command_class, arguments)
|
49
|
+
begin
|
50
|
+
command_class.new(robot, table).execute(*arguments)
|
51
|
+
rescue ArgumentError => exception
|
52
|
+
puts "#{command_class.label}: #{exception.message}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def parse_input(input)
|
57
|
+
return {} if input.nil?
|
58
|
+
|
59
|
+
split_data = input.split(INPUT_SPLIT_REGEXP)
|
60
|
+
split_data.reject!(&:empty?)
|
61
|
+
|
62
|
+
return {} if split_data.empty?
|
63
|
+
|
64
|
+
{ command: split_data[0], arguments: split_data.drop(1) }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module LocoBot
|
2
|
+
class Robot
|
3
|
+
module Direction
|
4
|
+
# East direction.
|
5
|
+
module East
|
6
|
+
|
7
|
+
# Returns the Direction at the left of this one.
|
8
|
+
# @return [Direction::North]
|
9
|
+
def self.left
|
10
|
+
Direction::North
|
11
|
+
end
|
12
|
+
|
13
|
+
# Returns the Direction at the right of this one.
|
14
|
+
# @return [Direction::South]
|
15
|
+
def self.right
|
16
|
+
Direction::South
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns the Direction's label.
|
20
|
+
# @return [String] the Direction's label
|
21
|
+
def self.label
|
22
|
+
'EAST'
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns the given coordinates in a Hash, modified to reflect the Direction.
|
26
|
+
# @param x [Integer] the x-axis coordinate
|
27
|
+
# @param y [Integer] the y-axis coordinate
|
28
|
+
# @return [Hash] the modified x and y coordinates
|
29
|
+
# @example
|
30
|
+
# Direction::East.coordinates(1, 2) # => {x: 2, y: 2}
|
31
|
+
def self.coordinates(x, y)
|
32
|
+
{ x: x + 1, y: y }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module LocoBot
|
2
|
+
class Robot
|
3
|
+
module Direction
|
4
|
+
# North direction.
|
5
|
+
module North
|
6
|
+
|
7
|
+
# Returns the Direction at the left of this one.
|
8
|
+
# @return [Direction::West]
|
9
|
+
def self.left
|
10
|
+
Direction::West
|
11
|
+
end
|
12
|
+
|
13
|
+
# Returns the Direction at the right of this one.
|
14
|
+
# @return [Direction::East]
|
15
|
+
def self.right
|
16
|
+
Direction::East
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns the Direction's label.
|
20
|
+
# @return [String] the Direction's label
|
21
|
+
def self.label
|
22
|
+
'NORTH'
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns the given coordinates in a Hash, modified to reflect the Direction.
|
26
|
+
# @param x [Integer] the x-axis coordinate
|
27
|
+
# @param y [Integer] the y-axis coordinate
|
28
|
+
# @return [Hash] the modified x and y coordinates
|
29
|
+
# @example
|
30
|
+
# Direction::North.coordinates(1, 2) # => {x: 1, y: 3}
|
31
|
+
def self.coordinates(x, y)
|
32
|
+
{ x: x, y: y + 1 }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module LocoBot
|
2
|
+
class Robot
|
3
|
+
module Direction
|
4
|
+
# South direction.
|
5
|
+
module South
|
6
|
+
|
7
|
+
# Returns the Direction at the left of this one.
|
8
|
+
# @return [Direction::East]
|
9
|
+
def self.left
|
10
|
+
Direction::East
|
11
|
+
end
|
12
|
+
|
13
|
+
# Returns the Direction at the right of this one.
|
14
|
+
# @return [Direction::West]
|
15
|
+
def self.right
|
16
|
+
Direction::West
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns the Direction's label.
|
20
|
+
# @return [String] the Direction's label
|
21
|
+
def self.label
|
22
|
+
'SOUTH'
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns the given coordinates in a Hash, modified to reflect the Direction.
|
26
|
+
# @param x [Integer] the x-axis coordinate
|
27
|
+
# @param y [Integer] the y-axis coordinate
|
28
|
+
# @return [Hash] the modified x and y coordinates
|
29
|
+
# @example
|
30
|
+
# Direction::South.coordinates(1, 2) # => {x: 1, y: 1}
|
31
|
+
def self.coordinates(x, y)
|
32
|
+
{ x: x, y: y - 1 }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module LocoBot
|
2
|
+
class Robot
|
3
|
+
module Direction
|
4
|
+
# West direction.
|
5
|
+
module West
|
6
|
+
|
7
|
+
# Returns the Direction at the left of this one.
|
8
|
+
# @return [Direction::South]
|
9
|
+
def self.left
|
10
|
+
Direction::South
|
11
|
+
end
|
12
|
+
|
13
|
+
# Returns the Direction at the right of this one.
|
14
|
+
# @return [Direction::North]
|
15
|
+
def self.right
|
16
|
+
Direction::North
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns the Direction's label.
|
20
|
+
# @return [String] the Direction's label
|
21
|
+
def self.label
|
22
|
+
'WEST'
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns the given coordinates in a Hash, modified to reflect the Direction.
|
26
|
+
# @param x [Integer] the x-axis coordinate
|
27
|
+
# @param y [Integer] the y-axis coordinate
|
28
|
+
# @return [Hash] the modified x and y coordinates
|
29
|
+
# @example
|
30
|
+
# Direction::West.coordinates(1, 2) # => {x: 0, y: 2}
|
31
|
+
def self.coordinates(x, y)
|
32
|
+
{ x: x - 1, y: y }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'loco_bot/robot/direction/east'
|
2
|
+
require 'loco_bot/robot/direction/north'
|
3
|
+
require 'loco_bot/robot/direction/south'
|
4
|
+
require 'loco_bot/robot/direction/west'
|
5
|
+
|
6
|
+
module LocoBot
|
7
|
+
class Robot
|
8
|
+
# Top-level namespace for the possible facing directions.
|
9
|
+
module Direction
|
10
|
+
|
11
|
+
# Returns the list of available Direction as Strings.
|
12
|
+
# @return [Array<String>]
|
13
|
+
def self.list
|
14
|
+
constants.map(&:to_s)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns a Direction class that as the same name as the given name.
|
18
|
+
# @param name [String] the Direction name
|
19
|
+
# @return [Command] the Direction
|
20
|
+
def self.from_name(name)
|
21
|
+
if const_defined?(name.capitalize, false)
|
22
|
+
const_get(name.capitalize)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'loco_bot/robot/direction'
|
2
|
+
|
3
|
+
module LocoBot
|
4
|
+
# Representation of a robot, placeable on tables.
|
5
|
+
class Robot
|
6
|
+
|
7
|
+
# @!attribute [rw] table
|
8
|
+
# @return [Table] the Table the Robot is currently placed on
|
9
|
+
attr_accessor :table
|
10
|
+
|
11
|
+
# @!attribute [rw] x
|
12
|
+
# @return [Integer] the x-axis coordinate the Robot is currently at on the Table
|
13
|
+
attr_accessor :x
|
14
|
+
|
15
|
+
# @!attribute [rw] y
|
16
|
+
# @return [Integer] the y-axis coordinate the Robot is currently at on the Table
|
17
|
+
attr_accessor :y
|
18
|
+
|
19
|
+
# @!attribute [rw] direction
|
20
|
+
# @return [Direction] the Direction the Robot is currently facing on the Table
|
21
|
+
attr_accessor :direction
|
22
|
+
|
23
|
+
# Places the Robot on the given Table at the given coordinates, facing the given Direction.
|
24
|
+
# If the Robot was previously on another Table, it is removed from it before being placed on the given.
|
25
|
+
# @param table [Table] the Table to place the Robot at
|
26
|
+
# @param x [Integer] the x-axis coordinate
|
27
|
+
# @param y [Integer] the y-axis coordinate
|
28
|
+
# @param direction [Direction] the facing Direction
|
29
|
+
# @return [Boolean] true if placing was successful, false otherwise.
|
30
|
+
def place(table, x, y, direction)
|
31
|
+
table.place_robot(self, x, y, direction)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Removes the Robot its current Table. Position attributes of the Robot are set to nil.
|
35
|
+
# @return [Boolean] true if removing was successful, false otherwise.
|
36
|
+
def remove
|
37
|
+
return false if table.nil?
|
38
|
+
|
39
|
+
table.remove_robot(self)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Moves the robot one unit forward in the direction it is currently facing.
|
43
|
+
# @return [Boolean] true if moving was successful, false otherwise.
|
44
|
+
def move
|
45
|
+
return false if table.nil? or !table.position_valid?(next_position[:x], next_position[:y])
|
46
|
+
|
47
|
+
@x = next_position[:x]
|
48
|
+
@y = next_position[:y]
|
49
|
+
|
50
|
+
true
|
51
|
+
end
|
52
|
+
|
53
|
+
# Rotates the robot 90 degrees to the left without changing its position.
|
54
|
+
# @return [Boolean] true if turning was successful, false otherwise.
|
55
|
+
def turn_left
|
56
|
+
return false if table.nil?
|
57
|
+
|
58
|
+
@direction = direction.left
|
59
|
+
|
60
|
+
true
|
61
|
+
end
|
62
|
+
|
63
|
+
# Rotates the robot 90 degrees to the right without changing its position.
|
64
|
+
# @return [Boolean] true if turning was successful, false otherwise.
|
65
|
+
def turn_right
|
66
|
+
return false if table.nil?
|
67
|
+
|
68
|
+
@direction = direction.right
|
69
|
+
|
70
|
+
true
|
71
|
+
end
|
72
|
+
|
73
|
+
# Returns a Hash containing the x, y and direction of the robot.
|
74
|
+
# @return [Hash] the x, y and direction of the Robot.
|
75
|
+
# @example
|
76
|
+
# robot.place(1, 2, Direction::West)
|
77
|
+
# robot.report # => {x: 1, y: 2, direction: LocoBot::Robot::Direction::West}
|
78
|
+
def report
|
79
|
+
return {} if table.nil?
|
80
|
+
|
81
|
+
{x: x, y: y, direction: direction}
|
82
|
+
end
|
83
|
+
|
84
|
+
# Outputs a friendly greating.
|
85
|
+
# @return [void]
|
86
|
+
def hodor!
|
87
|
+
puts 'HODOR HODOR !'
|
88
|
+
end
|
89
|
+
|
90
|
+
# Returns a Hash containing the next x and y coordinates of the Robot if it moves facing its current direction.
|
91
|
+
# The Hash will be empty if the Robot has not been placed.
|
92
|
+
# @return [Hash] the next x and y coordinates of the Robot.
|
93
|
+
# @example
|
94
|
+
# robot.place(1, 2, Direction::West)
|
95
|
+
# robot.next_direction # => {x: 0, y: 2}
|
96
|
+
def next_position
|
97
|
+
return {} if table.nil?
|
98
|
+
|
99
|
+
direction.coordinates(x, y)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module LocoBot
|
2
|
+
# Representation of a table on which robots can be placed.
|
3
|
+
class Table
|
4
|
+
|
5
|
+
# @!attribute [r] width
|
6
|
+
# @return [Fixnum] width of the Table
|
7
|
+
# Determines the farthest accessible point on the Table's x-axis.
|
8
|
+
attr_reader :width
|
9
|
+
|
10
|
+
# @!attribute [r] height
|
11
|
+
# @return [Fixnum] height of the table
|
12
|
+
# Determines the farthest accessible point on the Table's y-axis.
|
13
|
+
attr_reader :height
|
14
|
+
|
15
|
+
# @!attribute [r] robots
|
16
|
+
# @return [Array<Robot>] a collection of [Robot]
|
17
|
+
# The collection of Robots currently placed on the Table.
|
18
|
+
attr_reader :robots
|
19
|
+
|
20
|
+
# @param width [Integer] The width of the Table
|
21
|
+
# @param height [Integer] The height of the Table
|
22
|
+
def initialize(width = 5, height = 5)
|
23
|
+
@width = width
|
24
|
+
@height = height
|
25
|
+
@robots = []
|
26
|
+
|
27
|
+
validate_dimensions
|
28
|
+
end
|
29
|
+
|
30
|
+
# Places the given Robot on the Table at the given coordinates, facing the given Direction.
|
31
|
+
# If the Robot was previously on another Table, it is removed from it before being placed on the current.
|
32
|
+
# @param robot [Robot] the Robot to place on the Table
|
33
|
+
# @param x [Integer] the x-axis coordinate
|
34
|
+
# @param y [Integer] the y-axis coordinate
|
35
|
+
# @param direction [Direction] the facing Direction
|
36
|
+
# @return [Boolean] true if placing was successful, false otherwise.
|
37
|
+
def place_robot(robot, x, y, direction)
|
38
|
+
return false unless position_valid?(x, y)
|
39
|
+
|
40
|
+
robot.table.remove_robot(robot) if robot.table
|
41
|
+
|
42
|
+
robot.table = self
|
43
|
+
robot.direction = direction
|
44
|
+
robot.x = x
|
45
|
+
robot.y = y
|
46
|
+
|
47
|
+
@robots.push(robot) unless robots.include?(robot)
|
48
|
+
|
49
|
+
true
|
50
|
+
end
|
51
|
+
|
52
|
+
# Removes the given Robot from the Table. Position attributes of the Robot are set to nil
|
53
|
+
# @param robot [Robot] the Robot to remove from the Table
|
54
|
+
# @return [Boolean] true if removing was successful, false otherwise.
|
55
|
+
def remove_robot(robot)
|
56
|
+
return false unless robots.include?(robot)
|
57
|
+
|
58
|
+
robot.table = nil
|
59
|
+
robot.x = nil
|
60
|
+
robot.y = nil
|
61
|
+
robot.direction = nil
|
62
|
+
|
63
|
+
@robots.delete(robot)
|
64
|
+
|
65
|
+
true
|
66
|
+
end
|
67
|
+
|
68
|
+
# Determines whether a Robot can be placed at the given coordinates.
|
69
|
+
# @param x [Integer] the x-axis coordinate
|
70
|
+
# @param y [Integer] the y-axis coordinate
|
71
|
+
# @return [Boolean] true if a Robot can be placed at the given coordinates, false otherwise.
|
72
|
+
def position_valid?(x, y)
|
73
|
+
position_within_bounds?(x, y) and position_free?(x, y)
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def position_within_bounds?(x, y)
|
79
|
+
x >= 0 && y >= 0 && x < width && y < height
|
80
|
+
end
|
81
|
+
|
82
|
+
def position_free?(x, y)
|
83
|
+
robots.none? {|robot| robot.x == x && robot.y == y }
|
84
|
+
end
|
85
|
+
|
86
|
+
def validate_dimensions
|
87
|
+
errors = []
|
88
|
+
errors << "#{width} is not a valid width: it must be > 0" unless width > 0
|
89
|
+
errors << "#{height} is not a valid height: it must be > 0" unless height > 0
|
90
|
+
|
91
|
+
raise ArgumentError.new(errors.join(', ')) unless errors.empty?
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
data/lib/loco_bot.rb
ADDED
data/loco_bot.gemspec
ADDED
@@ -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 'loco_bot/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'loco_bot'
|
8
|
+
spec.version = LocoBot::VERSION
|
9
|
+
spec.authors = ['Rafaël Gonzalez']
|
10
|
+
spec.email = ['github@rafaelgonzalez.me']
|
11
|
+
spec.summary = %q{Ruby gem of crazy robots and benevolent tables that keep watching over them.}
|
12
|
+
spec.description = %q{Issue commands to control the robots and the tables will keep you from making them fall, you monster!}
|
13
|
+
spec.homepage = 'https://github.com/rafaelgonzalez/loco_bot'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
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_development_dependency 'bundler', '~> 1.6'
|
22
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
23
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
24
|
+
spec.add_development_dependency 'simplecov', '~> 0.9'
|
25
|
+
spec.add_development_dependency 'codeclimate-test-reporter', '~> 0.4.0'
|
26
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
RSpec.describe LocoBot::CLI::Command::Hodor do
|
2
|
+
let(:robot) { LocoBot::Robot.new }
|
3
|
+
let(:table) { LocoBot::Table.new }
|
4
|
+
|
5
|
+
subject { described_class.new(robot, table) }
|
6
|
+
|
7
|
+
describe '#execute' do
|
8
|
+
it 'calls #hodor! on robot' do
|
9
|
+
expect(subject.robot).to receive(:hodor!).with(no_args).once
|
10
|
+
|
11
|
+
subject.execute
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
RSpec.describe LocoBot::CLI::Command::Left do
|
2
|
+
let(:robot) { LocoBot::Robot.new }
|
3
|
+
let(:table) { LocoBot::Table.new }
|
4
|
+
|
5
|
+
subject { described_class.new(robot, table) }
|
6
|
+
|
7
|
+
describe '#execute' do
|
8
|
+
it 'calls #turn_left on robot' do
|
9
|
+
expect(subject.robot).to receive(:turn_left).with(no_args).once
|
10
|
+
|
11
|
+
subject.execute
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
RSpec.describe LocoBot::CLI::Command::Move do
|
2
|
+
let(:robot) { LocoBot::Robot.new }
|
3
|
+
let(:table) { LocoBot::Table.new }
|
4
|
+
|
5
|
+
subject { described_class.new(robot, table) }
|
6
|
+
|
7
|
+
describe '#execute' do
|
8
|
+
it 'calls #move on robot' do
|
9
|
+
expect(subject.robot).to receive(:move).with(no_args).once
|
10
|
+
|
11
|
+
subject.execute
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
RSpec.describe LocoBot::CLI::Command::Place do
|
2
|
+
let(:robot) { LocoBot::Robot.new }
|
3
|
+
let(:table) { LocoBot::Table.new }
|
4
|
+
|
5
|
+
subject { described_class.new(robot, table) }
|
6
|
+
|
7
|
+
describe '#execute' do
|
8
|
+
it 'calls #place on robot' do
|
9
|
+
expect(subject.robot).to receive(:place).with(table, 3, 2, LocoBot::Robot::Direction::West).once
|
10
|
+
|
11
|
+
subject.execute('3', '2', 'west')
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
RSpec.describe LocoBot::CLI::Command::Report do
|
2
|
+
let(:robot) { LocoBot::Robot.new }
|
3
|
+
let(:table) { LocoBot::Table.new }
|
4
|
+
|
5
|
+
subject { described_class.new(robot, table) }
|
6
|
+
|
7
|
+
describe '#execute' do
|
8
|
+
context 'before placing robot' do
|
9
|
+
it 'does not output a string' do
|
10
|
+
expect { subject.execute }.not_to output.to_stdout
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
context 'after placing robot' do
|
15
|
+
before { subject.robot.place(table, 4 ,2 , LocoBot::Robot::Direction::East) }
|
16
|
+
|
17
|
+
it 'outputs a string' do
|
18
|
+
expect { subject.execute }.to output("4,2,EAST\n").to_stdout
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
RSpec.describe LocoBot::CLI::Command::Right do
|
2
|
+
let(:robot) { LocoBot::Robot.new }
|
3
|
+
let(:table) { LocoBot::Table.new }
|
4
|
+
|
5
|
+
subject { described_class.new(robot, table) }
|
6
|
+
|
7
|
+
describe '#execute' do
|
8
|
+
it 'calls #turn_right on robot' do
|
9
|
+
expect(subject.robot).to receive(:turn_right).with(no_args).once
|
10
|
+
|
11
|
+
subject.execute
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
RSpec.describe LocoBot::CLI::Command do
|
2
|
+
describe '.list' do
|
3
|
+
subject { described_class.list }
|
4
|
+
|
5
|
+
it { is_expected.to eql ['HODOR', 'LEFT', 'MOVE', 'PLACE', 'REPORT', 'RIGHT'] }
|
6
|
+
end
|
7
|
+
|
8
|
+
describe '.class_from_name' do
|
9
|
+
subject { described_class.class_from_name(direction) }
|
10
|
+
|
11
|
+
context 'passing :base' do
|
12
|
+
let(:direction) { :base }
|
13
|
+
|
14
|
+
it { is_expected.to eql nil }
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'passing :hodor' do
|
18
|
+
let(:direction) { :hodor }
|
19
|
+
|
20
|
+
it { is_expected.to eql LocoBot::CLI::Command::Hodor }
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'passing :left' do
|
24
|
+
let(:direction) { :left }
|
25
|
+
|
26
|
+
it { is_expected.to eql LocoBot::CLI::Command::Left }
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'passing :move' do
|
30
|
+
let(:direction) { :move }
|
31
|
+
|
32
|
+
it { is_expected.to eql LocoBot::CLI::Command::Move }
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'passing :place' do
|
36
|
+
let(:direction) { :place }
|
37
|
+
|
38
|
+
it { is_expected.to eql LocoBot::CLI::Command::Place }
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'passing :report' do
|
42
|
+
let(:direction) { :report }
|
43
|
+
|
44
|
+
it { is_expected.to eql LocoBot::CLI::Command::Report }
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'passing :right' do
|
48
|
+
let(:direction) { :right }
|
49
|
+
|
50
|
+
it { is_expected.to eql LocoBot::CLI::Command::Right }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
RSpec.describe LocoBot::CLI do
|
2
|
+
describe '#initialize' do
|
3
|
+
subject { described_class.new }
|
4
|
+
|
5
|
+
it { expect(subject.robot).to be_an_instance_of(LocoBot::Robot) }
|
6
|
+
it { expect(subject.table).to be_an_instance_of(LocoBot::Table) }
|
7
|
+
end
|
8
|
+
|
9
|
+
describe '#input!' do
|
10
|
+
let(:input) { "#{command} #{arguments}" }
|
11
|
+
|
12
|
+
context 'using a valid command' do
|
13
|
+
let(:command) { :place }
|
14
|
+
|
15
|
+
context 'with a valid number of arguments' do
|
16
|
+
let(:arguments) { '1, 3, SOUTH' }
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'with an invalid number of arguments' do
|
20
|
+
let(:arguments) { '1, 2' }
|
21
|
+
|
22
|
+
it 'outputs a custom ArgumentError message' do
|
23
|
+
expect { subject.input!(input) }.to output("PLACE: wrong number of arguments (2 for 3)\n").to_stdout
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'using an invalid command' do
|
29
|
+
let(:command) { :yadayada }
|
30
|
+
let(:arguments) { '' }
|
31
|
+
|
32
|
+
it 'does not raise an error' do
|
33
|
+
expect { subject.input!(input) }.to output("YADAYADA: not a known command. Valid commands are HODOR, LEFT, MOVE, PLACE, REPORT, RIGHT.\n").to_stdout
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|