toyrobot 0.0.1
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 +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
|