toybot 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.ruby-version +1 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/README.md +117 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/bin/toy-robot +11 -0
- data/lib/toybot.rb +7 -0
- data/lib/toybot/bot.rb +173 -0
- data/lib/toybot/direction.rb +25 -0
- data/lib/toybot/tabletop.rb +19 -0
- data/lib/toybot/version.rb +3 -0
- data/toybot.gemspec +30 -0
- metadata +100 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 49c78135d65da889f15226744edb5f9112e35f21
|
4
|
+
data.tar.gz: 219588e93302a02dabfb75e147e7481f6d26a1dc
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4871e41bcbf4d5399648144d05c5ae4fe90227aac8baef2f0554bd0a9284548c1ca39cf746d5457b87dfa53e0c5168255b10278c1c3124afab37f83c70a5ce8a
|
7
|
+
data.tar.gz: 38f92f0f1f4e05e7fd3a71d74c395ccd07efb815066cce60c376addcfbe7acc441e111843831e2bed0f363018c9e193e55e1cecb508002d3c49fcfe2d039aabf
|
data/.gitignore
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.2.2
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
# toy-robot
|
2
|
+
|
3
|
+
### Description
|
4
|
+
|
5
|
+
toy-robot is a simulator of a toy robot that moves on a tabletop.
|
6
|
+
|
7
|
+
### Requirements
|
8
|
+
|
9
|
+
- You need [RubyGems](https://rubygems.org/pages/download).
|
10
|
+
- You need [Bundler](http://bundler.io/)
|
11
|
+
|
12
|
+
To install Bundler from rubygems:
|
13
|
+
|
14
|
+
```bash
|
15
|
+
$ gem install bundler
|
16
|
+
```
|
17
|
+
|
18
|
+
### Install
|
19
|
+
|
20
|
+
```bash
|
21
|
+
$ bundle install
|
22
|
+
```
|
23
|
+
|
24
|
+
This will install the few required dependencies specified in `toybot.gemspec`
|
25
|
+
|
26
|
+
**Run tests**
|
27
|
+
|
28
|
+
You can check that everything works correctly by running the tests:
|
29
|
+
|
30
|
+
```bash
|
31
|
+
$ bundle exec rake
|
32
|
+
```
|
33
|
+
|
34
|
+
### Usage
|
35
|
+
|
36
|
+
**Start bot**
|
37
|
+
|
38
|
+
```bash
|
39
|
+
$ bundle exec bin/toy-robot
|
40
|
+
```
|
41
|
+
|
42
|
+
It is also possible to give a size to the tabletop like so:
|
43
|
+
|
44
|
+
```bash
|
45
|
+
$ bundle exec bin/toy-robot WIDTH HEIGHT
|
46
|
+
```
|
47
|
+
|
48
|
+
### Error handling
|
49
|
+
|
50
|
+
- Toybot is quite permissive with the input as long as it respects the number of arguments, and uses the comma separator.
|
51
|
+
- Toybot is not very smart, but he is trying. Any invalid input will freak him out and he will display a little notice.
|
52
|
+
|
53
|
+
### Implementation details
|
54
|
+
|
55
|
+
Toybot is made of several components:
|
56
|
+
|
57
|
+
1. Tabletop
|
58
|
+
|
59
|
+
The `Tabletop` class is a simple abstraction of a tabletop with a `width` and a `height`.
|
60
|
+
|
61
|
+
- By default, the size of the tabletop is infinite.
|
62
|
+
- The tabletop only has positive coordinates starting at x(0) and y(0).
|
63
|
+
- The tabletop cannot be smaller than 1 by 1.
|
64
|
+
|
65
|
+
**where**: `lib/toybot/tabletop.rb`
|
66
|
+
|
67
|
+
2. Direction
|
68
|
+
|
69
|
+
The `Direction` class abstract the notion of cardinal directions.
|
70
|
+
|
71
|
+
- It can be rotated clockwise or counter clockwise of 90deg.
|
72
|
+
|
73
|
+
**where**: `lib/toybot/direction.rb`
|
74
|
+
|
75
|
+
3. Bot
|
76
|
+
|
77
|
+
Finally, our little robot.
|
78
|
+
|
79
|
+
- The bot needs to be initialized with a tabletop object.
|
80
|
+
- It can be initialized and run in one time with:
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
Toybot::Bot.start(tabletop)
|
84
|
+
```
|
85
|
+
|
86
|
+
- It implements internally all the possible actions.
|
87
|
+
- Actions are called by passing a message to the bot object, deducted from the input.
|
88
|
+
|
89
|
+
**where**: `lib/toybot/bot.rb`
|
90
|
+
|
91
|
+
### Specifications
|
92
|
+
|
93
|
+
toy-robot is a simulator of a toy robot that moves on a tabletop.
|
94
|
+
|
95
|
+
toy-robot reads instructions from STDIN, executing them one at a time until EOF is reached. (On a Linux or OSX system, type C-d to generate an EOF character).
|
96
|
+
|
97
|
+
valid commands
|
98
|
+
|
99
|
+
PLACE X,Y,FACING
|
100
|
+
|
101
|
+
Put the toy robot on the table in position X,Y and facing NORTH, SOUTH, EAST or WEST. If the robot is already placed, issuing another valid PLACE command will place the robot in the newly specified location.
|
102
|
+
|
103
|
+
MOVE
|
104
|
+
|
105
|
+
Moves the toy robot one unit forward in the direction it is currently facing.
|
106
|
+
|
107
|
+
LEFT
|
108
|
+
|
109
|
+
Rotates the robot 90 degrees to the left (i.e. counter-clockwise) without changing the position of the robot.
|
110
|
+
|
111
|
+
RIGHT
|
112
|
+
|
113
|
+
Rotates the robot 90 degrees to the right (i.e. clockwise) without changing the position of the robot.
|
114
|
+
|
115
|
+
REPORT
|
116
|
+
|
117
|
+
Announces the X,Y and F of the robot.
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "toybot"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/bin/toy-robot
ADDED
data/lib/toybot.rb
ADDED
data/lib/toybot/bot.rb
ADDED
@@ -0,0 +1,173 @@
|
|
1
|
+
class Toybot::Bot
|
2
|
+
# The format of the output of the `#report` method
|
3
|
+
REPORT_FORMAT = "[^_^] *bip bop* -( My position is X:%d Y:%d facing %s! )"
|
4
|
+
ERROR_REPORTS = [
|
5
|
+
"[x_x] *tuuuuut* -( What do you mean? )",
|
6
|
+
"[T_T] *tuuuuut* -( I can't help you, but Siri might... )",
|
7
|
+
"[o_0] *tuuuuut* -( Bonjour, parlez vous francais? )",
|
8
|
+
]
|
9
|
+
USAGE = <<-EOS
|
10
|
+
|
11
|
+
Here's what you can do:
|
12
|
+
-----------------------
|
13
|
+
|
14
|
+
PLACE X, Y, F - Put the toy robot on the table in position X,Y
|
15
|
+
and facing NORTH, SOUTH, EAST or WEST. If the robot is already
|
16
|
+
placed, issuing another valid PLACE command will place the robot
|
17
|
+
in the newly specified location
|
18
|
+
|
19
|
+
example: PLACE 24, 42, SOUTH
|
20
|
+
|
21
|
+
MOVE
|
22
|
+
Moves the toy robot one unit forward in the direction it is currently
|
23
|
+
facing.
|
24
|
+
|
25
|
+
LEFT
|
26
|
+
Rotates the robot 90 degrees to the left (i.e. counter-clockwise) without
|
27
|
+
changing the position of the robot.
|
28
|
+
|
29
|
+
RIGHT
|
30
|
+
Rotates the robot 90 degrees to the right (i.e. clockwise) without changing
|
31
|
+
the position of the robot.
|
32
|
+
|
33
|
+
REPORT
|
34
|
+
Announces the X,Y and F of the robot.
|
35
|
+
|
36
|
+
EOS
|
37
|
+
|
38
|
+
# Whitelist of actions that the bot knows how to execute
|
39
|
+
ACTIONS = [:place, :move, :left, :right, :report]
|
40
|
+
|
41
|
+
# The bot entry point.
|
42
|
+
# It takes a `Toybot::Tabletop` as the first argument.
|
43
|
+
def self.start tabletop
|
44
|
+
self.new(tabletop).run
|
45
|
+
end
|
46
|
+
|
47
|
+
# Initialize a new `Bot` on a given `Tabletop`
|
48
|
+
# The bot has a default position of `{x: 0, y: 0, facing: :north}`
|
49
|
+
def initialize tabletop = Toybot::Tabletop.new
|
50
|
+
@tabletop = tabletop
|
51
|
+
@x, @y, @facing = 0, 0, Toybot::Direction.new(:north)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Run loop of the bot
|
55
|
+
# Wait for input from STDIN, parse, execute, repeat until EOF
|
56
|
+
def run
|
57
|
+
while (input = $stdin.gets) do
|
58
|
+
action, args = self.parse_input(input)
|
59
|
+
self.execute action, args unless action == :no_action
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns the action and an array of arguments.
|
64
|
+
# It does not perform any validation on the input.
|
65
|
+
def parse_input input
|
66
|
+
input = input.chomp
|
67
|
+
arr = input.split
|
68
|
+
action = arr.shift || :no_action
|
69
|
+
return action.downcase.to_sym, arr.join.split(',')
|
70
|
+
end
|
71
|
+
|
72
|
+
# It takes the action and the arguments as first and second arguments.
|
73
|
+
# 1. Validate that the action is whitelisted
|
74
|
+
# 2. Fire the corresponding method, with the arguments if needed.
|
75
|
+
def execute action, args
|
76
|
+
if ACTIONS.include? action
|
77
|
+
if args.any?
|
78
|
+
self.send action, *args
|
79
|
+
else
|
80
|
+
self.send action
|
81
|
+
end
|
82
|
+
else
|
83
|
+
self.report_error
|
84
|
+
end
|
85
|
+
rescue ArgumentError
|
86
|
+
self.report_error
|
87
|
+
end
|
88
|
+
|
89
|
+
############################################################
|
90
|
+
# Bot actions
|
91
|
+
#
|
92
|
+
# All bot actions return the bot position to facilitate
|
93
|
+
# testing.
|
94
|
+
############################################################
|
95
|
+
|
96
|
+
def place x, y, facing
|
97
|
+
@x = x.to_i
|
98
|
+
@y = y.to_i
|
99
|
+
@facing.value = facing.downcase.to_sym
|
100
|
+
self.position
|
101
|
+
end
|
102
|
+
|
103
|
+
def right
|
104
|
+
@facing.rotate_clockwise!
|
105
|
+
self.position
|
106
|
+
end
|
107
|
+
|
108
|
+
def left
|
109
|
+
@facing.rotate_counter_clockwise!
|
110
|
+
self.position
|
111
|
+
end
|
112
|
+
|
113
|
+
def move
|
114
|
+
case @facing.value
|
115
|
+
when :north
|
116
|
+
self.move_up
|
117
|
+
when :east
|
118
|
+
self.move_right
|
119
|
+
when :south
|
120
|
+
self.move_down
|
121
|
+
when :west
|
122
|
+
self.move_left
|
123
|
+
end
|
124
|
+
self.position
|
125
|
+
end
|
126
|
+
|
127
|
+
def report
|
128
|
+
puts self.to_s
|
129
|
+
self.position
|
130
|
+
end
|
131
|
+
|
132
|
+
############################################################
|
133
|
+
# Bot movement rules
|
134
|
+
############################################################
|
135
|
+
|
136
|
+
def move_down
|
137
|
+
@y -= @y > 0 ? 1 : 0
|
138
|
+
end
|
139
|
+
|
140
|
+
def move_up
|
141
|
+
@y += @y < @tabletop.height ? 1 : 0
|
142
|
+
end
|
143
|
+
|
144
|
+
def move_left
|
145
|
+
@x -= @x > 0 ? 1 : 0
|
146
|
+
end
|
147
|
+
|
148
|
+
def move_right
|
149
|
+
@x += @x < @tabletop.width ? 1 : 0
|
150
|
+
end
|
151
|
+
|
152
|
+
|
153
|
+
############################################################
|
154
|
+
# Bot reporting
|
155
|
+
############################################################
|
156
|
+
|
157
|
+
def position
|
158
|
+
{x: @x, y: @y, facing: @facing.value}
|
159
|
+
end
|
160
|
+
|
161
|
+
def to_s
|
162
|
+
REPORT_FORMAT % [@x, @y, @facing.to_s]
|
163
|
+
end
|
164
|
+
|
165
|
+
def report_error
|
166
|
+
puts
|
167
|
+
puts "ERROR REPORT ======================================================================"
|
168
|
+
puts ERROR_REPORTS.sample
|
169
|
+
puts USAGE
|
170
|
+
puts "==================================================================================="
|
171
|
+
puts
|
172
|
+
end
|
173
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class Toybot::Direction
|
2
|
+
attr_reader :value
|
3
|
+
|
4
|
+
DIRECTIONS = [:north, :east, :south, :west]
|
5
|
+
|
6
|
+
def initialize facing = :north
|
7
|
+
self.value = facing
|
8
|
+
end
|
9
|
+
|
10
|
+
def value= facing
|
11
|
+
@value = facing if DIRECTIONS.include? facing
|
12
|
+
end
|
13
|
+
|
14
|
+
def rotate_clockwise!
|
15
|
+
self.value = DIRECTIONS[DIRECTIONS.index(@value) + 1] || DIRECTIONS[0]
|
16
|
+
end
|
17
|
+
|
18
|
+
def rotate_counter_clockwise!
|
19
|
+
self.value = DIRECTIONS[DIRECTIONS.index(@value) - 1]
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
@value.to_s
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class Toybot::Tabletop
|
2
|
+
attr_accessor :width, :height
|
3
|
+
|
4
|
+
# Default values for tabletop x & y
|
5
|
+
# +1.0/0.0 => Infinity
|
6
|
+
DEFAULT_TABLETOP_WIDTH = +1.0/0.0
|
7
|
+
DEFAULT_TABLETOP_HEIGHT = +1.0/0.0
|
8
|
+
|
9
|
+
# A Tabletop has a width and height (Infinity by default).
|
10
|
+
# A Tabletop has a minimum size of 1 by 1.
|
11
|
+
def initialize width: DEFAULT_TABLETOP_WIDTH, height: DEFAULT_TABLETOP_HEIGHT
|
12
|
+
@width = width > 0 ? width : 1
|
13
|
+
@height = height > 0 ? height : 1
|
14
|
+
end
|
15
|
+
|
16
|
+
def size
|
17
|
+
{width: self.width, height: self.height}
|
18
|
+
end
|
19
|
+
end
|
data/toybot.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'toybot/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "toybot"
|
8
|
+
spec.version = Toybot::VERSION
|
9
|
+
spec.authors = ["Arnaud MESUREUR"]
|
10
|
+
spec.email = ["arnaud.mesureur@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{BIP BOP}
|
13
|
+
|
14
|
+
# Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
|
15
|
+
# delete this section to allow pushing this gem to any host.
|
16
|
+
# if spec.respond_to?(:metadata)
|
17
|
+
# spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
|
18
|
+
# else
|
19
|
+
# raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
|
20
|
+
# end
|
21
|
+
|
22
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
23
|
+
spec.bindir = "exe"
|
24
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
25
|
+
spec.require_paths = ["lib"]
|
26
|
+
|
27
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
28
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
29
|
+
spec.add_development_dependency "minitest"
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: toybot
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Arnaud MESUREUR
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-09-16 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'
|
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'
|
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.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description:
|
56
|
+
email:
|
57
|
+
- arnaud.mesureur@gmail.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- ".gitignore"
|
63
|
+
- ".ruby-version"
|
64
|
+
- ".travis.yml"
|
65
|
+
- Gemfile
|
66
|
+
- README.md
|
67
|
+
- Rakefile
|
68
|
+
- bin/console
|
69
|
+
- bin/setup
|
70
|
+
- bin/toy-robot
|
71
|
+
- lib/toybot.rb
|
72
|
+
- lib/toybot/bot.rb
|
73
|
+
- lib/toybot/direction.rb
|
74
|
+
- lib/toybot/tabletop.rb
|
75
|
+
- lib/toybot/version.rb
|
76
|
+
- toybot.gemspec
|
77
|
+
homepage:
|
78
|
+
licenses: []
|
79
|
+
metadata: {}
|
80
|
+
post_install_message:
|
81
|
+
rdoc_options: []
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
requirements: []
|
95
|
+
rubyforge_project:
|
96
|
+
rubygems_version: 2.4.5
|
97
|
+
signing_key:
|
98
|
+
specification_version: 4
|
99
|
+
summary: BIP BOP
|
100
|
+
test_files: []
|