robodog 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1c903f6841736d12811c90affeee2c238b4cbf20
4
+ data.tar.gz: 291ddf2774970bf08f543ddbbc3114f9609884a8
5
+ SHA512:
6
+ metadata.gz: 371c4f9d545f5fabe6f583c03fe02840250385f28ddecbf2234dd0af0fe36138230fecc39c877b78c0aabb21460a5e22f0017dc4ed924b25d583cd283af655b4
7
+ data.tar.gz: f8f6b7a7be9ce41fb3a77d44acce0d523c48aba2042fe6e5f998fb9814337643a58793e67e4079ad17cfd6a7bd4dc4147eb4763798c083fa4a634ab49ed651bf
@@ -0,0 +1,34 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /test/tmp/
9
+ /test/version_tmp/
10
+ /tmp/
11
+
12
+ ## Specific to RubyMotion:
13
+ .dat*
14
+ .repl_history
15
+ build/
16
+
17
+ ## Documentation cache and generated files:
18
+ /.yardoc/
19
+ /_yardoc/
20
+ /doc/
21
+ /rdoc/
22
+
23
+ ## Environment normalisation:
24
+ /.bundle/
25
+ /lib/bundler/man/
26
+
27
+ # for a library or gem, you might want to ignore these files since the code is
28
+ # intended to run in multiple environments; otherwise, check them in:
29
+ # Gemfile.lock
30
+ # .ruby-version
31
+ # .ruby-gemset
32
+
33
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
34
+ .rvmrc
@@ -0,0 +1,27 @@
1
+ ## How to contribute
2
+
3
+ Fork, then clone the repo:
4
+
5
+ git clone git@github.com:your-username/robo-dog.git
6
+
7
+ Check you ruby and development dependencies versions:
8
+
9
+ $ ruby -v
10
+
11
+ $ gem list
12
+
13
+ Make sure the tests pass:
14
+
15
+ rspec
16
+
17
+ Make your change. Add tests for your change. Make the tests pass:
18
+
19
+ rspec
20
+
21
+ Push to your fork and submit a pull request. Some things that are equal parts necessary/epic:
22
+
23
+ * Write tests.
24
+
25
+ * Follow a sensible style guide. My pick is [this one](https://github.com/bbatsov/ruby-style-guide).
26
+
27
+ * Write a [good commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
data/LICENSE ADDED
@@ -0,0 +1,25 @@
1
+ This is free and unencumbered software released into the public domain.
2
+
3
+ Anyone is free to copy, modify, publish, use, compile, sell, or
4
+ distribute this software, either in source code form or as a compiled
5
+ binary, for any purpose, commercial or non-commercial, and by any
6
+ means.
7
+
8
+ In jurisdictions that recognize copyright laws, the author or authors
9
+ of this software dedicate any and all copyright interest in the
10
+ software to the public domain. We make this dedication for the benefit
11
+ of the public at large and to the detriment of our heirs and
12
+ successors. We intend this dedication to be an overt act of
13
+ relinquishment in perpetuity of all present and future rights to this
14
+ software under copyright law.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ For more information, please refer to <http://unlicense.org>
25
+
@@ -0,0 +1,302 @@
1
+ robo-dog
2
+ =========
3
+
4
+ Fredwina the Farmer's robotic sheep dog simulator.
5
+
6
+ ## Installation
7
+
8
+ via RubyGems:
9
+
10
+ $ gem install robodog
11
+
12
+
13
+ ## Usage
14
+
15
+ Pipe in a file with simulation parameters:
16
+
17
+ $ robodog < path/to/file
18
+
19
+ An example file:
20
+
21
+ 5 5
22
+ 1 2 N
23
+ LMLMLMLMM
24
+ 3 3 E
25
+ MMRMMRMRRM
26
+
27
+ To which the application prints:
28
+
29
+ 1 3 N
30
+ 5 1 E
31
+
32
+ More example files can be found at `data/`
33
+
34
+ ## Dependencies
35
+
36
+ ruby version ~> 2.1.0p0
37
+
38
+ To check your version run:
39
+
40
+ $ ruby -v
41
+
42
+ To learn how to install ruby visit [ruby-lang.org/en/installation/](https://www.ruby-lang.org/en/installation/)
43
+
44
+ ## Troubleshooting
45
+
46
+ ### Development environment
47
+
48
+ * OSX 10.8.5, ruby 2.1.2p95
49
+
50
+ Development dependencies:
51
+
52
+ rspec ~> 3.1
53
+
54
+ To install them along the gem:
55
+
56
+ $ gem install --dev robodog
57
+
58
+ ### Compatible environments
59
+
60
+ * Ubuntu 12.04 x32, ruby 2.1.0p0
61
+
62
+ ### Incompatible environments
63
+
64
+ * ruby < 2.1.0
65
+
66
+ ### Tests
67
+
68
+ To run the specs:
69
+
70
+ $ rspec
71
+
72
+ To run just the integration tests:
73
+
74
+ $ rspec spec/features
75
+
76
+ ## Overview
77
+
78
+ The application is designed to read simulation parameters from `$stdin` and print the output to `$stdout`.
79
+
80
+ ### Input Format
81
+
82
+ The application expects input containing a description of the paddock on which the robotic sheep dogs are to move, and a description of each robotic sheep dogs. The description of each robotic sheep dog has two parts in itself. It must contain a starting position and a sequence of commands to execute.
83
+
84
+ #### Paddock
85
+
86
+ The paddock description must contain two integers separated by a space, as follows:
87
+
88
+ <x_size> <y_size>
89
+
90
+ These two integers represent the dimensions of a square paddock.
91
+
92
+ Example:
93
+
94
+ 5 5
95
+
96
+ Only one paddock description is allowed per input.
97
+
98
+ #### Robot
99
+
100
+ The robot part of the input is expected to have a starting position, and a sequence of commands it will execute. There is no limit as to the amount of robots accepted.
101
+
102
+ ##### Position
103
+
104
+ This piece of the input is expected to contain two space-separated integers, plus a space-separated cardinal orientation represented by the strings `N`, `E`, `S` and `W`. The general structure is as follows:
105
+
106
+ <x_coordinate> <y_coordinate> <cardinal_orientation>
107
+
108
+ * The spaces are validated by the application and thus required.
109
+ * The `<cardinal_orientation>` is case sensitive.
110
+
111
+ Example:
112
+
113
+ 1 2 N
114
+
115
+ ##### Commands
116
+
117
+ The commands represent a sequence of instructions that the robot will follow in order. The valid commands are `M`, `R` and `L`:
118
+
119
+ M # stands for Move and will make the robot advance one position in the direction its facing
120
+
121
+ R # stands for Right and will make the robot rotate its orientation 90 degrees clockwise
122
+
123
+ L # stands for Left and will make the robot rotate its orientation 90 degrees counter-clockwise
124
+
125
+ * The commands should be concatenated into a single string
126
+ * The commands are case sensitive
127
+ * Non-valid commands will make the application fail.
128
+
129
+ Example:
130
+
131
+ LMRLMRLMRLR
132
+
133
+ ### Output Format
134
+
135
+ The simulation's final state will be printed as a results of the application running. The state is represented by the end position of each of the robots. A line is printed per robot and this line follows the same format as the input position line `<x_coordinate> <y_coordinate> <cardinal_orientation>`.
136
+
137
+ Example:
138
+
139
+ 1 3 N
140
+ 5 1 E
141
+
142
+ ### Constraints
143
+
144
+ Sheepdogs are not permitted to bump into each other or run each other over. The program fails at runtime if this happens. Specifically the program will fail if:
145
+
146
+ * Two robots are placed on the same starting position.
147
+ * A robot moves into another robot's position.
148
+ * A robot tries to move beyond the paddock's limit.
149
+
150
+ In case the simulation fails, the following message will be printed:
151
+
152
+ Invalid coordinates. This means two robots collided or a robot hit the border of the paddock. (RuntimeError)
153
+
154
+ ## Design
155
+
156
+ The application flows in the following way:
157
+
158
+ #### 1 Launch
159
+
160
+ $ robodog < data/valid_example_input_a.txt
161
+
162
+ ##### 1.1 bin/robodog
163
+
164
+ The `robodog` in the command is a ruby executable in your load path. This executable contains the following lines:
165
+
166
+ ```ruby
167
+ require 'robo_dog'
168
+ RoboDog::Application.build.run
169
+ ```
170
+
171
+ The first line requires the file `./lib/robo_dog.rb` which loads the library. The second line creates and instance of the application and runs it.
172
+
173
+ ##### 1.2 lib/robo_dog/application.rb
174
+
175
+ The `#run` method starts by delegating the input parsing to the `parser` object. The `input` by default is `$stdin`.
176
+
177
+ ```ruby
178
+ def run(mode = nil)
179
+ simulation_attrs = parser.parse(input)
180
+ simulation = simulation_class.new(simulation_attrs)
181
+
182
+ simulation.run(mode)
183
+ puts format(simulation.report)
184
+ end
185
+ ```
186
+
187
+ The parser line is:
188
+
189
+ ```ruby
190
+ simulation_attrs = parser.parse(input)
191
+ ```
192
+
193
+ ##### 1.2.1 lib/robo_dog/parser.rb
194
+
195
+ ```ruby
196
+ def parse(input)
197
+ paddock = @paddock_parser.call(input)
198
+ robots = []
199
+ loop do
200
+ robot_attrs = @robot_extractor.call(input)
201
+ break unless robot_attrs
202
+ robots << @robot_factory.build(robot_attrs)
203
+ end
204
+ {
205
+ paddock: paddock,
206
+ robots: robots
207
+ }
208
+ end
209
+ ```
210
+
211
+ The parser knows how to extract the paddock and robot attributes for the simulation. It actually delegates the building of these objects to the `Paddock` and `Robot` class themselves, but extracts the string from the input stream. The first delegation occurs at:
212
+
213
+ ```ruby
214
+ robots << @robot_factory.build(robot_attrs)
215
+ ```
216
+
217
+ The `@robot_factory` object is actually the `Robot` class which knows how to build a instance of itself from the `robot_attrs`.
218
+
219
+ After all the parsing is done, this function returns a hash with the correct objects in `:paddock` and `:robots` (an Array of robots)
220
+
221
+ ##### 1.2.2 lib/robo_dog/simulation.rb
222
+
223
+ Next in application.rb is:
224
+
225
+ ```ruby
226
+ simulation.run(mode)
227
+ ```
228
+
229
+ The `#run` message to `Simulation` is responsible for coordinating the robots on the paddock. It has two run modes: `:sequential` and `:turns`.
230
+
231
+ ```ruby
232
+ def run(mode = :sequential)
233
+ mode ||= :sequential
234
+ warm_up
235
+ case mode
236
+ when :sequential
237
+ robots.each { |r| r.execute(:all) }
238
+ when :turns
239
+ run_in_turns
240
+ end
241
+ end
242
+ ```
243
+
244
+ * In `:sequential` mode each robot will execute all of its commands at once.
245
+ * In `:turns` mode robots will take turns executing one command at a time.
246
+
247
+ This method messages each `Robot` to `#execute`. This guide will not cover how `#execute` is implemented. Just bear in mind that robots can move and rotate, while these movements need to be coordinated to avoid robots running over each other.
248
+
249
+ ##### 1.2 lib/robo_dog/application.rb
250
+
251
+ Once the simulation has run, all that remains is to print the output. This output is formated by the `Application` class and then printed to `$stdout`.
252
+
253
+ ```ruby
254
+ puts format(simulation.report)
255
+ ```
256
+
257
+ #### 2 Exit
258
+
259
+ After the output is printed, the application exits.
260
+
261
+ ## Discussion
262
+
263
+ My main design decisions / concerns in no specific order are the following.
264
+
265
+ #### Placing
266
+
267
+ There is no specification as to the order of the placing of the robots on the paddock. This application assumes that all robots are placed first, failing if they are placed on the same coordinates, and then the commands are executed according to the selected run mode.
268
+
269
+ #### Run Mode
270
+
271
+ The application has `:sequential` and `:turns` (beta, no command-line support) mode. Should the robots run the commands in turns or all at once?
272
+
273
+ This is a relevant question to ask since for the same input file, one run mode might fail while the other one might be successful. There are a couple of examples of these circumstance in `spec/features/robo_dog_spec.rb`
274
+
275
+ This issue addresses specifically the case when you order a robot to chase another one. If run sequentially, the first robot will run over the second one. If run in turns, the intended outcome will happen.
276
+
277
+ #### Failing
278
+
279
+ While the original spec specifies that the application should fail if robots run over each other, it does not specify how or when. In this implementation the application will fail when robots run the following line:
280
+
281
+ ```ruby
282
+ self.pose = adj_pose if coordinators.all? { |c| c.valid_coordinates?(adj_pose.coordinates) }
283
+ ```
284
+
285
+ since `#valid_coordinates?` is:
286
+
287
+ ```ruby
288
+ def valid_coordinates?(coordinates)
289
+ coordinates &&
290
+ paddock.valid_coordinates?(coordinates) &&
291
+ robots.all? { |r| r.coordinates != coordinates } ||
292
+ fail_appropriately
293
+ end
294
+ ```
295
+
296
+ My concern about this decision is that `#valid_coordinates?` failing is not clear enough, this method should return either `true` or `false`. The alternative here is to simply advance the pose, and let know the observer (aka the `Simulation`) of what happened, and the application should fail there.
297
+
298
+ In the end the existing approach seemed more sensible because it stops the application in a pre-fail state which can be inspected, and is more flexible in case a decision is made in the future by which robots should just ignore commands that would send them into a invalid coordinate.
299
+
300
+ ## Contributing
301
+
302
+ View [CONTRIBUTING.md](https://github.com/matiasanaya/robo-dog/blob/master/CONTRIBUTING.md)
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'robo_dog'
4
+
5
+ RoboDog::Application.build.run
@@ -0,0 +1,5 @@
1
+ 5 5
2
+ 0 0 E
3
+ M
4
+ 1 0 N
5
+ L
@@ -0,0 +1,5 @@
1
+ 5 5
2
+ 1 2 N
3
+ LMLMLMLMM
4
+ 3 3 E
5
+ MMRMMRMRRM
@@ -0,0 +1 @@
1
+ require_relative 'robo_dog/application'
@@ -0,0 +1,37 @@
1
+ require_relative 'parser'
2
+ require_relative 'simulation'
3
+
4
+ module RoboDog
5
+ class Application
6
+ def self.build(input = nil)
7
+ new(input: input, parser: Parser, simulation_class: Simulation)
8
+ end
9
+
10
+ def initialize(args = {})
11
+ @input = args[:input] || $stdin
12
+ @parser = args[:parser]
13
+ @simulation_class = args[:simulation_class]
14
+ end
15
+
16
+ def run(mode = nil)
17
+ simulation_attrs = parser.parse(input)
18
+ simulation = simulation_class.new(simulation_attrs)
19
+
20
+ simulation.run(mode)
21
+ puts format(simulation.report)
22
+ end
23
+
24
+ private
25
+
26
+ attr_reader :input, :parser, :simulation_class
27
+
28
+ def format(report)
29
+ str = ''
30
+ report[:robots].each do |r|
31
+ str << "#{r[:x]} #{r[:y]} #{r[:orientation]}"
32
+ str << "\n"
33
+ end
34
+ str
35
+ end
36
+ end
37
+ end