toyrobot 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +34 -0
- data/CONTRIBUTING.md +28 -0
- data/LICENSE +24 -0
- data/README.md +148 -0
- data/Rakefile +21 -0
- data/bin/toyrobot +5 -0
- data/data/example_input_a.txt +3 -0
- data/data/example_input_b.txt +3 -0
- data/data/example_input_c.txt +6 -0
- data/lib/toy_robot/application.rb +38 -0
- data/lib/toy_robot/board.rb +16 -0
- data/lib/toy_robot/command/base.rb +5 -0
- data/lib/toy_robot/command/parser/base.rb +58 -0
- data/lib/toy_robot/command/parser/place.rb +37 -0
- data/lib/toy_robot/command/parser.rb +20 -0
- data/lib/toy_robot/command/token.rb +11 -0
- data/lib/toy_robot/factory.rb +83 -0
- data/lib/toy_robot/placement.rb +34 -0
- data/lib/toy_robot/pose.rb +79 -0
- data/lib/toy_robot/robot.rb +41 -0
- data/lib/toy_robot/robot_controller.rb +35 -0
- data/lib/toy_robot/view.rb +22 -0
- data/lib/toy_robot.rb +1 -0
- data/test/integration/test_application.rb +24 -0
- data/test/integration/test_factory.rb +18 -0
- data/test/integration/test_toy_robot.rb +9 -0
- data/test/test_board.rb +31 -0
- data/test/test_command.rb +22 -0
- data/test/test_command_matcher.rb +32 -0
- data/test/test_command_matcher_interface.rb +31 -0
- data/test/test_command_matcher_left.rb +20 -0
- data/test/test_command_matcher_move.rb +21 -0
- data/test/test_command_matcher_place.rb +56 -0
- data/test/test_command_matcher_report.rb +21 -0
- data/test/test_command_matcher_right.rb +20 -0
- data/test/test_placement.rb +94 -0
- data/test/test_pose.rb +146 -0
- data/test/test_reporter_interface.rb +7 -0
- data/test/test_robot.rb +70 -0
- data/test/test_robot_controller.rb +62 -0
- data/test/test_view.rb +32 -0
- data/toyrobot.gemspec +20 -0
- metadata +139 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 38160974bafdf9946e5e665c58b1bf00dccaf40e
|
4
|
+
data.tar.gz: b9a29c8e8df15252a2453517cccda9057818502c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 05978ae19f8a401719dc7f825127c36575efe3c116bcd8ae5613aad01e74687869e8ca0f889ce79c2976bf122bd2196993f730d1479054299a734f2c86b582c7
|
7
|
+
data.tar.gz: d24163a31a50ad221ebf1c00ca844d9a3bf08fcbb7cdb3a9f68b27f9070f0d6378c5bc3b5aa6eac3b43dad7119eae2a8dbef28c279199e25f6c3d785af13918a
|
data/.gitignore
ADDED
@@ -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
|
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
## How to contribute
|
2
|
+
|
3
|
+
Fork, then clone the repo:
|
4
|
+
|
5
|
+
git clone git@github.com:your-username/toy-robot-simulator.git
|
6
|
+
|
7
|
+
Check you ruby and development dependancies versions:
|
8
|
+
|
9
|
+
$ ruby -v
|
10
|
+
|
11
|
+
$ gem list
|
12
|
+
|
13
|
+
Make sure the tests pass:
|
14
|
+
|
15
|
+
rake test:all
|
16
|
+
|
17
|
+
Make your change. Add tests for your change. Make the tests pass:
|
18
|
+
|
19
|
+
rake test:all
|
20
|
+
|
21
|
+
Push to your fork and submit a pull request. Some things that are equal parts necesary/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).
|
28
|
+
|
data/LICENSE
ADDED
@@ -0,0 +1,24 @@
|
|
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>
|
data/README.md
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
toy-robot
|
2
|
+
===================
|
3
|
+
|
4
|
+
A simulation of a toy robot moving on a square tabletop.
|
5
|
+
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
via RubyGems:
|
10
|
+
|
11
|
+
$ gem install toy_robot
|
12
|
+
|
13
|
+
|
14
|
+
## Usage
|
15
|
+
|
16
|
+
Start the simulator in interactive mode:
|
17
|
+
|
18
|
+
$ toy_robot
|
19
|
+
|
20
|
+
Or pipe it a file with commands:
|
21
|
+
|
22
|
+
$ toy_robot < path/to/file
|
23
|
+
|
24
|
+
An example file with commands:
|
25
|
+
|
26
|
+
PLACE 1,2,EAST
|
27
|
+
MOVE
|
28
|
+
MOVE
|
29
|
+
LEFT
|
30
|
+
MOVE
|
31
|
+
REPORT
|
32
|
+
|
33
|
+
To which the simulator will respond:
|
34
|
+
|
35
|
+
3,3,NORTH
|
36
|
+
|
37
|
+
### Simulation Commands
|
38
|
+
|
39
|
+
Simulation commands are case-sensitive.
|
40
|
+
|
41
|
+
#### PLACE
|
42
|
+
|
43
|
+
Puts the robot on the table in position X,Y and facing `NORTH`, `SOUTH`, `EAST` or `WEST`. The origin (0,0) is considered to be the SOUTH WEST most corner. Usage:
|
44
|
+
|
45
|
+
PLACE 2,1,WEST
|
46
|
+
|
47
|
+
#### MOVE
|
48
|
+
|
49
|
+
Move the robot one unit forward in the direction it is currently facing. Usage:
|
50
|
+
|
51
|
+
MOVE
|
52
|
+
|
53
|
+
#### LEFT
|
54
|
+
|
55
|
+
Rotate the robot 90 degrees counter-clockwise. It does not affect its position. Usage:
|
56
|
+
|
57
|
+
LEFT
|
58
|
+
|
59
|
+
#### RIGHT
|
60
|
+
|
61
|
+
Rotates the robot 90 degrees clockwise. It does not affect its position. Usage:
|
62
|
+
|
63
|
+
RIGHT
|
64
|
+
|
65
|
+
#### REPORT
|
66
|
+
|
67
|
+
Announce the X,Y and F of the robot. Usage:
|
68
|
+
|
69
|
+
REPORT
|
70
|
+
|
71
|
+
Response:
|
72
|
+
|
73
|
+
X,Y,FACING
|
74
|
+
|
75
|
+
### Simulation Constraints
|
76
|
+
|
77
|
+
* The toy robot is moving on a square tabletop of dimensions 5 units x 5 units.
|
78
|
+
|
79
|
+
* There are no other obstructions on the table surface.
|
80
|
+
|
81
|
+
* The robot is free to roam around the surface of the table, but is prevented from falling to destruction.
|
82
|
+
|
83
|
+
* Any movement that would result in the robot falling from the table is prevented, however further valid movement commands are still allowed.
|
84
|
+
|
85
|
+
* 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 discards all commands in the sequence until a valid PLACE command has been executed.
|
86
|
+
|
87
|
+
* A robot that is not on the table ignores the MOVE, LEFT, RIGHT and REPORT commands.
|
88
|
+
|
89
|
+
* The application does not provide any graphical output showing the movement of the toy robot.
|
90
|
+
|
91
|
+
|
92
|
+
## Dependancies
|
93
|
+
|
94
|
+
ruby version ~> 2.1.0p0
|
95
|
+
|
96
|
+
To check your version run:
|
97
|
+
|
98
|
+
$ ruby -v
|
99
|
+
|
100
|
+
To learn how to install ruby visit [ruby-lang.org/en/installation/](https://www.ruby-lang.org/en/installation/)
|
101
|
+
|
102
|
+
|
103
|
+
## Troubleshooting
|
104
|
+
|
105
|
+
### Development environment
|
106
|
+
|
107
|
+
* OSX 10.8.5, ruby 2.1.2p95
|
108
|
+
|
109
|
+
Development dependancies:
|
110
|
+
|
111
|
+
rake ~> 10.3
|
112
|
+
minitest ~> 4.7.5
|
113
|
+
|
114
|
+
To install them along the gem:
|
115
|
+
|
116
|
+
$ gem install --dev toy_robot
|
117
|
+
|
118
|
+
### Compatible environments
|
119
|
+
|
120
|
+
* Ubuntu 14.04 x64, ruby 2.1.0p0
|
121
|
+
|
122
|
+
* Ubuntu 12.04 x32, ruby 2.1.0p0
|
123
|
+
|
124
|
+
### Incompatible enviroments
|
125
|
+
|
126
|
+
* ruby < 2.1.0
|
127
|
+
|
128
|
+
### Tests
|
129
|
+
|
130
|
+
To run unit tests:
|
131
|
+
|
132
|
+
$ rake test
|
133
|
+
|
134
|
+
To run integration tests:
|
135
|
+
|
136
|
+
$ rake test:integration
|
137
|
+
|
138
|
+
To run all tests:
|
139
|
+
|
140
|
+
$ rake test:all
|
141
|
+
|
142
|
+
## Discussion
|
143
|
+
|
144
|
+
This piece of software is over-engineered. Over engineering does not feel good.
|
145
|
+
|
146
|
+
## Contributing
|
147
|
+
|
148
|
+
View [CONTRIBUTING.md](https://github.com/matiasanaya/toy-robot-simulator/blob/master/CONTRIBUTING.md).
|
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rake/testtask'
|
2
|
+
|
3
|
+
Rake::TestTask.new do |t|
|
4
|
+
t.libs << "test"
|
5
|
+
t.test_files = FileList['test/test*.rb']
|
6
|
+
t.verbose = true
|
7
|
+
end
|
8
|
+
|
9
|
+
Rake::TestTask.new do |t|
|
10
|
+
t.libs << "test"
|
11
|
+
t.name = "test:integration"
|
12
|
+
t.test_files = FileList['test/integration/test*.rb']
|
13
|
+
t.verbose = true
|
14
|
+
end
|
15
|
+
|
16
|
+
Rake::TestTask.new do |t|
|
17
|
+
t.libs << "test"
|
18
|
+
t.name = "test:all"
|
19
|
+
t.test_files = FileList['test/**/test*.rb']
|
20
|
+
t.verbose = true
|
21
|
+
end
|
data/bin/toyrobot
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require_relative 'factory'
|
2
|
+
require_relative 'command/parser'
|
3
|
+
|
4
|
+
module ToyRobot
|
5
|
+
class Application
|
6
|
+
def initialize(argv = [], args = {})
|
7
|
+
@input = args[:input] || $stdin
|
8
|
+
@parser = Command::Parser
|
9
|
+
@controller = Factory.create(:controller)
|
10
|
+
end
|
11
|
+
|
12
|
+
def run(options = nil)
|
13
|
+
loop do
|
14
|
+
raw_input = input.gets
|
15
|
+
break unless raw_input
|
16
|
+
raw_input.chomp!
|
17
|
+
debug(raw_input)
|
18
|
+
command = parser.parse(raw_input)
|
19
|
+
controller.input(command) if command
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
attr_reader :input, :parser, :controller
|
26
|
+
|
27
|
+
def debug(raw_input)
|
28
|
+
case raw_input
|
29
|
+
when /\AR(EPORT_DEBUG)*\z/
|
30
|
+
puts controller.instance_variable_get(:@robot).inspect
|
31
|
+
when /\AE(XIT)*\z/
|
32
|
+
puts 'Have a nice day, bye :)'
|
33
|
+
puts '...'
|
34
|
+
exit(false)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module ToyRobot
|
2
|
+
class Board
|
3
|
+
def initialize(x_size, y_size)
|
4
|
+
@x_size = x_size
|
5
|
+
@y_size = y_size
|
6
|
+
end
|
7
|
+
|
8
|
+
def valid_pose?(pose)
|
9
|
+
pose && (0..x_size).include?(pose.x) && (0..y_size).include?(pose.y)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
attr_reader :x_size, :y_size
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require_relative '../token'
|
2
|
+
require_relative '../base'
|
3
|
+
|
4
|
+
module ToyRobot
|
5
|
+
module Command
|
6
|
+
module Parser
|
7
|
+
class Base
|
8
|
+
def initialize(args)
|
9
|
+
@regex = args[:regex]
|
10
|
+
@token = args[:token]
|
11
|
+
@args_extractor = args[:args_extractor]
|
12
|
+
end
|
13
|
+
|
14
|
+
def build_with_match(string)
|
15
|
+
build_command(string) if match?(string)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
attr_reader :regex, :token, :args_extractor
|
21
|
+
|
22
|
+
def match?(string)
|
23
|
+
string =~ regex
|
24
|
+
end
|
25
|
+
|
26
|
+
def extract_args(string)
|
27
|
+
if args_extractor
|
28
|
+
args_extractor.call(string)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def build_command(string)
|
33
|
+
Command::Base.new(token, extract_args(string)) if match?(string)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
Move = Base.new(
|
38
|
+
token: Command::Token::MOVE,
|
39
|
+
regex: /\AMOVE\z/
|
40
|
+
)
|
41
|
+
|
42
|
+
Right = Base.new(
|
43
|
+
token: Command::Token::RIGHT,
|
44
|
+
regex: /\ARIGHT\z/
|
45
|
+
)
|
46
|
+
|
47
|
+
Left = Base.new(
|
48
|
+
token: Command::Token::LEFT,
|
49
|
+
regex: /\ALEFT\z/
|
50
|
+
)
|
51
|
+
|
52
|
+
Report = Base.new(
|
53
|
+
token: Command::Token::REPORT,
|
54
|
+
regex: /\AREPORT\z/
|
55
|
+
)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require_relative 'base'
|
2
|
+
require_relative '../../pose'
|
3
|
+
|
4
|
+
module ToyRobot
|
5
|
+
module Command
|
6
|
+
module Parser
|
7
|
+
constantize_orientation = lambda do |string|
|
8
|
+
case string
|
9
|
+
when 'EAST'
|
10
|
+
Pose::Orientation::EAST
|
11
|
+
when 'NORTH'
|
12
|
+
Pose::Orientation::NORTH
|
13
|
+
when 'WEST'
|
14
|
+
Pose::Orientation::WEST
|
15
|
+
when 'SOUTH'
|
16
|
+
Pose::Orientation::SOUTH
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
place_args_extractor = lambda do |raw_command|
|
21
|
+
x, y, f = raw_command.split.pop.split(',')
|
22
|
+
|
23
|
+
Pose.new(
|
24
|
+
x: x.to_i,
|
25
|
+
y: y.to_i,
|
26
|
+
orientation: constantize_orientation.call(f)
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
Place = Base.new(
|
31
|
+
token: Command::Token::PLACE,
|
32
|
+
regex: /\APLACE \d+,\d+,(NORTH|SOUTH|EAST|WEST)\z/,
|
33
|
+
args_extractor: place_args_extractor
|
34
|
+
)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require_relative 'parser/base'
|
2
|
+
require_relative 'parser/place'
|
3
|
+
|
4
|
+
module ToyRobot
|
5
|
+
module Command
|
6
|
+
module Parser
|
7
|
+
def self.parse(string)
|
8
|
+
all.each do |matcher|
|
9
|
+
command = matcher.build_with_match(string)
|
10
|
+
return command if command
|
11
|
+
end
|
12
|
+
nil
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.all
|
16
|
+
constants(false).reject! { |const| const == :Base }.map { |const| const_get(const) }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require_relative 'robot_controller'
|
2
|
+
require_relative 'robot'
|
3
|
+
require_relative 'placement'
|
4
|
+
require_relative 'board'
|
5
|
+
require_relative 'pose'
|
6
|
+
require_relative 'view'
|
7
|
+
|
8
|
+
module ToyRobot
|
9
|
+
module Factory
|
10
|
+
module_function
|
11
|
+
|
12
|
+
def create(identifier, opts = {})
|
13
|
+
case identifier
|
14
|
+
when :controller
|
15
|
+
RobotControllerFactory.create(opts)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
module RobotControllerFactory
|
20
|
+
module_function
|
21
|
+
|
22
|
+
def create(opts = {})
|
23
|
+
robot = opts[:robot] || RobotFactory.create
|
24
|
+
view = opts[:view] || ViewFactory.create(robot: robot)
|
25
|
+
|
26
|
+
ToyRobot::RobotController.new(
|
27
|
+
robot: robot,
|
28
|
+
view: view
|
29
|
+
)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
module ViewFactory
|
34
|
+
module_function
|
35
|
+
|
36
|
+
def create(opts = {})
|
37
|
+
robot = opts[:robot] || RobotFactory.create
|
38
|
+
output = opts[:output] || $stdout
|
39
|
+
|
40
|
+
ToyRobot::View.new(
|
41
|
+
robot: robot,
|
42
|
+
output: output
|
43
|
+
)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
module RobotFactory
|
48
|
+
module_function
|
49
|
+
|
50
|
+
def create(opts = {})
|
51
|
+
placement = opts[:placement] || PlacementFactory.create
|
52
|
+
|
53
|
+
ToyRobot::Robot.new(
|
54
|
+
placement: placement
|
55
|
+
)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
module PlacementFactory
|
60
|
+
module_function
|
61
|
+
|
62
|
+
def create(opts = {})
|
63
|
+
board = opts[:board] || BoardFactory.create
|
64
|
+
pose = opts[:pose] || ToyRobot::Pose.new
|
65
|
+
ToyRobot::Placement.new(
|
66
|
+
board: board,
|
67
|
+
pose: pose
|
68
|
+
)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
module BoardFactory
|
73
|
+
module_function
|
74
|
+
|
75
|
+
def create(opts = {})
|
76
|
+
x = opts[:x] || 5
|
77
|
+
y = opts[:y] || 5
|
78
|
+
|
79
|
+
ToyRobot::Board.new(x, y)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module ToyRobot
|
4
|
+
class Placement
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
def_delegator :pose, :report
|
8
|
+
def_delegator :pose, :rotate!, :rotate
|
9
|
+
|
10
|
+
def initialize(args)
|
11
|
+
@board = args[:board]
|
12
|
+
@pose = args[:pose]
|
13
|
+
end
|
14
|
+
|
15
|
+
def on_board?
|
16
|
+
board && board.valid_pose?(pose)
|
17
|
+
end
|
18
|
+
|
19
|
+
def update(a_new_pose)
|
20
|
+
if board.valid_pose?(a_new_pose)
|
21
|
+
self.pose = a_new_pose
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def advance
|
26
|
+
update(pose.adjacent)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
attr_reader :board
|
32
|
+
attr_accessor :pose
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module ToyRobot
|
2
|
+
class Pose
|
3
|
+
module Orientation
|
4
|
+
EAST = :east
|
5
|
+
NORTH = :north
|
6
|
+
WEST = :west
|
7
|
+
SOUTH = :south
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_accessor :x, :y, :orientation
|
11
|
+
|
12
|
+
def initialize(args = {})
|
13
|
+
@x = args[:x]
|
14
|
+
@y = args[:y]
|
15
|
+
@orientation = args[:orientation]
|
16
|
+
end
|
17
|
+
|
18
|
+
def adjacent
|
19
|
+
dup.send(:adjacent!)
|
20
|
+
end
|
21
|
+
|
22
|
+
def rotate!(degrees)
|
23
|
+
step = (degrees % 90) + (degrees/degrees.abs)
|
24
|
+
self.orientation = step_orientation(step)
|
25
|
+
end
|
26
|
+
|
27
|
+
def report
|
28
|
+
{
|
29
|
+
x: x,
|
30
|
+
y: y,
|
31
|
+
orientation: orientation
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def step_orientation(by = 1)
|
38
|
+
orientations = [
|
39
|
+
Pose::Orientation::NORTH,
|
40
|
+
Pose::Orientation::EAST,
|
41
|
+
Pose::Orientation::SOUTH,
|
42
|
+
Pose::Orientation::WEST
|
43
|
+
]
|
44
|
+
|
45
|
+
orientations[(orientations.index(orientation) + by) % 4]
|
46
|
+
end
|
47
|
+
|
48
|
+
def adjacent!
|
49
|
+
case orientation
|
50
|
+
when Pose::Orientation::EAST
|
51
|
+
increment!(:x)
|
52
|
+
when Pose::Orientation::NORTH
|
53
|
+
increment!(:y)
|
54
|
+
when Pose::Orientation::WEST
|
55
|
+
decrement!(:x)
|
56
|
+
when Pose::Orientation::SOUTH
|
57
|
+
decrement!(:y)
|
58
|
+
end
|
59
|
+
self
|
60
|
+
end
|
61
|
+
|
62
|
+
def increment!(coordinate, by = 1)
|
63
|
+
update!(coordinate, by)
|
64
|
+
end
|
65
|
+
|
66
|
+
def decrement!(coordinate, by = -1)
|
67
|
+
update!(coordinate, by)
|
68
|
+
end
|
69
|
+
|
70
|
+
def update!(coordinate, by)
|
71
|
+
case coordinate
|
72
|
+
when :x
|
73
|
+
self.x = x + by
|
74
|
+
when :y
|
75
|
+
self.y = y + by
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|