ruby_arena 0.0.2

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.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -0
  3. data/.travis.yml +7 -0
  4. data/Gemfile +3 -0
  5. data/Gemfile.lock +31 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +118 -0
  8. data/Rakefile +5 -0
  9. data/bin/ruby_arena +7 -0
  10. data/lib/ruby_arena/ai.rb +68 -0
  11. data/lib/ruby_arena/arena.rb +75 -0
  12. data/lib/ruby_arena/bullet.rb +53 -0
  13. data/lib/ruby_arena/bullet_renderer.rb +53 -0
  14. data/lib/ruby_arena/class_loader.rb +20 -0
  15. data/lib/ruby_arena/command_parser.rb +41 -0
  16. data/lib/ruby_arena/game.rb +74 -0
  17. data/lib/ruby_arena/gui.rb +62 -0
  18. data/lib/ruby_arena/level.rb +42 -0
  19. data/lib/ruby_arena/level0.rb +6 -0
  20. data/lib/ruby_arena/level1.rb +9 -0
  21. data/lib/ruby_arena/movable.rb +18 -0
  22. data/lib/ruby_arena/option_parser.rb +29 -0
  23. data/lib/ruby_arena/robot.rb +196 -0
  24. data/lib/ruby_arena/robot_renderer.rb +180 -0
  25. data/lib/ruby_arena/value_sanitizer.rb +11 -0
  26. data/lib/ruby_arena/version.rb +3 -0
  27. data/lib/ruby_arena.rb +23 -0
  28. data/ruby_arena.gemspec +25 -0
  29. data/screenshot.png +0 -0
  30. data/spec/ai_spec.rb +11 -0
  31. data/spec/ai_test.rb +11 -0
  32. data/spec/arena_spec.rb +79 -0
  33. data/spec/bullet_renderer_spec.rb +4 -0
  34. data/spec/bullet_spec.rb +36 -0
  35. data/spec/class_loader_spec.rb +14 -0
  36. data/spec/command_parser_spec.rb +72 -0
  37. data/spec/fake_gui.rb +28 -0
  38. data/spec/game_spec.rb +4 -0
  39. data/spec/gui_spec.rb +50 -0
  40. data/spec/level_spec.rb +29 -0
  41. data/spec/movable_tests.rb +48 -0
  42. data/spec/option_parser_spec.rb +49 -0
  43. data/spec/robot_renderer_spec.rb +4 -0
  44. data/spec/robot_spec.rb +294 -0
  45. data/spec/spec_helper.rb +8 -0
  46. data/spec/value_sanitizer_spec.rb +21 -0
  47. metadata +163 -0
@@ -0,0 +1,72 @@
1
+ require 'spec_helper'
2
+
3
+ describe RubyArena:: CommandParser do
4
+ describe '#turn' do
5
+ it 'sets turn to given value' do
6
+ command_parser.turn(10)
7
+
8
+ expect(command_parser.actions[:turn]).to be 10
9
+ end
10
+ end
11
+
12
+ describe '#turn_gun' do
13
+ it 'sets turn_gun action to given value' do
14
+ command_parser.turn_gun(20)
15
+
16
+ expect(command_parser.actions[:turn_gun]).to be 20
17
+ end
18
+ end
19
+
20
+ describe '#turn_radar' do
21
+ it 'sets turn_radar action to given value' do
22
+ command_parser.turn_radar(20)
23
+
24
+ expect(command_parser.actions[:turn_radar]).to be 20
25
+ end
26
+ end
27
+
28
+ describe '#accelerate' do
29
+ it 'accelerates' do
30
+ command_parser.accelerate
31
+
32
+ expect(command_parser.actions[:accelerate]).to be true
33
+ end
34
+ end
35
+
36
+ describe '#decelerate' do
37
+ it 'decelerates' do
38
+ command_parser.decelerate
39
+
40
+ expect(command_parser.actions[:decelerate]).to be true
41
+ end
42
+ end
43
+
44
+ describe '#fire' do
45
+ it 'fires a bullet' do
46
+ command_parser.fire
47
+
48
+ expect(command_parser.actions[:fire]).to be true
49
+ end
50
+ end
51
+
52
+ describe '#change_radar_view' do
53
+ it 'sets radar view angle' do
54
+ command_parser.radar_view_angle(10)
55
+
56
+ expect(command_parser.actions[:radar_view_angle]).to be 10
57
+ end
58
+ end
59
+
60
+ describe '#reset_actions' do
61
+ it 'reset actions hash' do
62
+ command_parser.turn(10)
63
+ command_parser.reset_actions
64
+
65
+ expect(command_parser.actions).to eq({})
66
+ end
67
+ end
68
+
69
+ def command_parser
70
+ @_command_parser ||= RubyArena::CommandParser.new
71
+ end
72
+ end
data/spec/fake_gui.rb ADDED
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ class FakeGosuWindow
4
+ def stub(*args)
5
+ true
6
+ end
7
+
8
+ alias initialize stub
9
+ alias draw_quad stub
10
+ alias draw_triangle stub
11
+ alias rotate stub
12
+ end
13
+
14
+ GosuWindowBackup = Gosu::Window
15
+ Gosu.send(:remove_const, :Window)
16
+ Gosu::Window = FakeGosuWindow
17
+
18
+ GuiBackup = RubyArena::Gui
19
+ RubyArena.send(:remove_const, :Gui)
20
+
21
+ load 'ruby_arena/gui.rb' # reload gui.rb after stubbing
22
+
23
+ FakeGui = RubyArena::Gui # catch the stubbed gui
24
+
25
+ RubyArena::send(:remove_const, :Gui)
26
+ RubyArena::Gui = GuiBackup # restore things
27
+ Gosu.send(:remove_const, :Window)
28
+ Gosu::Window = GosuWindowBackup
data/spec/game_spec.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'spec_helper'
2
+
3
+ describe RubyArena:: Game do
4
+ end
data/spec/gui_spec.rb ADDED
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+
3
+ describe RubyArena:: Gui do
4
+ let(:robot) do
5
+ double('robot',
6
+ heading: 0,
7
+ radar_heading: 0,
8
+ gun_heading: 0,
9
+ x: 0,
10
+ y: 0,
11
+ size: 10,
12
+ radar_view_angle: 10,
13
+ radar_range: 100 )
14
+ end
15
+ let(:bullet) { double('bullet', heading: 0, x: 0, y: 0, size: 2) }
16
+ let(:arena) { double('arena', width: 800, height: 600, robots: [robot], bullets: [bullet]) }
17
+ let(:gui) { FakeGui.new(arena) }
18
+
19
+ describe 'public interface' do
20
+ it { expect(gui).to respond_to(:draw) }
21
+ end
22
+
23
+ describe '#update' do
24
+ it 'notifies arena' do
25
+ expect(arena).to receive(:update)
26
+
27
+ gui.update
28
+ end
29
+ end
30
+
31
+ describe '#draw' do
32
+ it 'notifies each robot renderer' do
33
+ robot_renderer = double('robot_renderer')
34
+ allow(RubyArena::RobotRenderer).to receive(:new) { robot_renderer }
35
+
36
+ expect(robot_renderer).to receive(:draw)
37
+
38
+ gui.draw
39
+ end
40
+
41
+ it 'notifies each bullet renderer' do
42
+ bullet_renderer = double('bullet_renderer')
43
+ allow(RubyArena::BulletRenderer).to receive(:new) { bullet_renderer }
44
+
45
+ expect(bullet_renderer).to receive(:draw)
46
+
47
+ gui.draw
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe RubyArena::Level do
4
+ before do
5
+ allow_any_instance_of(IO).to receive(:puts).and_return nil
6
+ end
7
+
8
+ describe '#initialize' do
9
+ it 'exits when level is not defined' do
10
+ expect { RubyArena::Level.new(:undefined_level) }.to raise_error SystemExit
11
+ end
12
+ end
13
+
14
+ describe '#options_for_cpu_robots' do
15
+ it 'returns options for cpu robots' do
16
+ level = RubyArena::Level.new(0)
17
+
18
+ expect(level.options_for_cpu_robots.first).to eq({ ai: RubyArena::Level0, x: 600, y: 300 })
19
+ end
20
+ end
21
+
22
+ describe '#options_for_user_robot' do
23
+ it 'returns options for user robot' do
24
+ level = RubyArena::Level.new(0)
25
+
26
+ expect(level.options_for_user_robot).to eq({ x: 200, y: 300 })
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,48 @@
1
+ shared_examples 'movable' do
2
+ describe 'public interface' do
3
+ it_responds_to(:x)
4
+ it_responds_to(:y)
5
+ it_responds_to(:speed)
6
+ it_responds_to(:heading)
7
+ end
8
+
9
+ describe '#move' do
10
+ it 'changes y coordinate by -1 when speed is 1 and heading is 0' do
11
+ object = object(x: 0, y: 0, heading: 0, speed: 1)
12
+
13
+ object.move
14
+
15
+ expect(object.y).to eq(-1)
16
+ end
17
+
18
+ it 'changes y coordinate by 1 when speed is 1 and heading is 180' do
19
+ object = object(x: 0, y: 0, heading: 180, speed: 1)
20
+
21
+ object.move
22
+
23
+ expect(object.y).to eq(1)
24
+ end
25
+
26
+ it 'changes x coordinate by 1 when speed is 1 and heading is 90' do
27
+ object = object(x: 0, y: 0, heading: 90, speed: 1)
28
+
29
+ object.move
30
+
31
+ expect(object.x).to eq(1)
32
+ end
33
+
34
+ it 'changes x coordinate by -1 when speed is 1 and heading is 270' do
35
+ object = object(x: 0, y: 0, heading: 270, speed: 1)
36
+
37
+ object.move
38
+
39
+ expect(object.x).to eq(-1)
40
+ end
41
+ end
42
+ end
43
+
44
+ def it_responds_to(method)
45
+ it "responds to ##{method}" do
46
+ expect(object).to respond_to(method)
47
+ end
48
+ end
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+
3
+ describe OptionParser do
4
+ describe '.parse' do
5
+ it 'returns {level: 1} when ["-l", "1"] is given' do
6
+ options = OptionParser.parse(['-l', '1'])
7
+
8
+ expect(options).to eq({level: 1})
9
+ end
10
+
11
+ it 'returns {level: 1} when ["--level", "1"] is given' do
12
+ options = OptionParser.parse(['--level', '1'])
13
+
14
+ expect(options).to eq({level: 1})
15
+ end
16
+
17
+ it 'prints help when no arguments are given' do
18
+ begin
19
+ output = capture_stdout { OptionParser.parse([]) }
20
+ expect(output).to include "Usage:"
21
+ rescue SystemExit
22
+ end
23
+ end
24
+
25
+ it 'prints help when -h is given' do
26
+ begin
27
+ output = capture_stdout { OptionParser.parse(['-h']) }
28
+ expect(output).to include "Usage:"
29
+ rescue SystemExit
30
+ end
31
+ end
32
+
33
+ it 'exits when -h is given' do
34
+ expect do
35
+ capture_stdout { OptionParser.parse(['-h']) }
36
+ end.to raise_error SystemExit
37
+ end
38
+ end
39
+ end
40
+
41
+ def capture_stdout &block
42
+ old_stdout = $stdout
43
+ fake_stdout = StringIO.new
44
+ $stdout = fake_stdout
45
+ block.call
46
+ fake_stdout.string
47
+ ensure
48
+ $stdout = old_stdout
49
+ end
@@ -0,0 +1,4 @@
1
+ require 'spec_helper'
2
+
3
+ describe RubyArena:: RobotRenderer do
4
+ end
@@ -0,0 +1,294 @@
1
+ require 'spec_helper'
2
+
3
+ describe RubyArena:: Robot do
4
+ let(:klass) { Robot }
5
+
6
+ it_behaves_like 'movable'
7
+
8
+ describe '#tick' do
9
+ it 'notifies AI' do
10
+ expect(robot.ai).to receive(:tick)
11
+
12
+ robot.tick
13
+ end
14
+
15
+ it 'decreases gun heat by 0.1 if it is more than 0' do
16
+ robot = robot(gun_heat: 1)
17
+ expect(robot.ai).to receive(:tick)
18
+
19
+ robot.tick
20
+
21
+ expect(robot.gun_heat).to eq(0.9)
22
+ end
23
+
24
+ it "doesn't decrease gun heat below 0" do
25
+ robot = robot(gun_heat: 0)
26
+ expect(robot.ai).to receive(:tick)
27
+
28
+ robot.tick
29
+
30
+ expect(robot.gun_heat).to be 0
31
+ end
32
+
33
+ end
34
+
35
+ describe '#update' do
36
+ it 'calls #execute_actions and #move on robot' do
37
+ expect(robot.command_parser).to receive(:actions)
38
+ expect(robot.command_parser).to receive(:reset_actions)
39
+ expect(robot).to receive(:execute_actions)
40
+ expect(robot).to receive(:move)
41
+
42
+ robot.update
43
+ end
44
+ end
45
+
46
+ describe '#accelerate' do
47
+ it 'increases speed by 1' do
48
+ robot.accelerate
49
+
50
+ expect(robot.speed).to be 1
51
+ end
52
+
53
+ it 'increases speed max to MAX_SPEED' do
54
+ 10.times { robot.accelerate }
55
+
56
+ expect(robot.speed).to be 8
57
+ end
58
+ end
59
+
60
+ describe '#decelerate' do
61
+ it 'decreases speed by 1' do
62
+ robot.decelerate
63
+
64
+ expect(robot.speed).to be(-1)
65
+ end
66
+
67
+ it 'decreases speed max to -MAX_SPEED' do
68
+ 10.times { robot.decelerate }
69
+
70
+ expect(robot.speed).to be(-8)
71
+ end
72
+ end
73
+
74
+ describe '#turn' do
75
+ it 'changes heading by given value' do
76
+ robot = robot(heading: 0)
77
+
78
+ robot.turn(5)
79
+
80
+ expect(robot.heading).to be 5
81
+ end
82
+
83
+ it 'changes heading maximum by MAX_TURN_ANGLE' do
84
+ robot = robot(heading: 0)
85
+
86
+ robot.turn(100)
87
+
88
+ expect(robot.heading).to be RubyArena::Robot::MAX_TURN_ANGLE
89
+ end
90
+
91
+ it 'changes heading maximum by MAX_TURN_ANGLE for negative values' do
92
+ robot = robot(heading: 100)
93
+
94
+ robot.turn(-100)
95
+
96
+ expect(robot.heading).to be(100-RubyArena::Robot::MAX_TURN_ANGLE)
97
+ end
98
+
99
+ it 'changes gun heading by given value' do
100
+ robot = robot(heading: 0)
101
+
102
+ robot.turn(5)
103
+
104
+ expect(robot.gun_heading).to be 5
105
+ end
106
+
107
+ it 'changes radar heading by given value' do
108
+ robot = robot(heading: 0)
109
+
110
+ robot.turn(5)
111
+
112
+ expect(robot.radar_heading).to be 5
113
+ end
114
+ end
115
+
116
+ describe '#turn_gun' do
117
+ it 'changes gun heading by given value' do
118
+ robot = robot(heading: 0)
119
+
120
+ robot.turn_gun(10)
121
+
122
+ expect(robot.gun_heading).to be 10
123
+ end
124
+
125
+ it 'changes gun heading maximum by MAX_TURN_GUN_ANGLE' do
126
+ robot = robot(gun_heading: 0)
127
+
128
+ robot.turn_gun(100)
129
+
130
+ expect(robot.gun_heading).to be RubyArena::Robot::MAX_TURN_GUN_ANGLE
131
+ end
132
+
133
+ it 'changes gun heading maximum by MAX_TURN_GUN_ANGLE for negative values' do
134
+ robot = robot(gun_heading: 100)
135
+
136
+ robot.turn_gun(-100)
137
+
138
+ expect(robot.gun_heading).to be(100-RubyArena::Robot::MAX_TURN_GUN_ANGLE)
139
+ end
140
+
141
+ it 'changes radar heading by given value' do
142
+ robot = robot(heading: 0)
143
+
144
+ robot.turn_gun(30)
145
+
146
+ expect(robot.radar_heading).to be 30
147
+ end
148
+ end
149
+
150
+ describe '#turn_radar' do
151
+ it 'changes radar heading by given value' do
152
+ robot = robot(heading: 0)
153
+
154
+ robot.turn_radar(10)
155
+
156
+ expect(robot.radar_heading).to be 10
157
+ end
158
+
159
+ it 'changes radar heading maximum by MAX_TURN_RADAR_ANGLE' do
160
+ robot = robot(radar_heading: 0)
161
+
162
+ robot.turn_radar(100)
163
+
164
+ expect(robot.radar_heading).to be RubyArena::Robot::MAX_TURN_RADAR_ANGLE
165
+ end
166
+
167
+ it 'changes radar heading maximum by MAX_TURN_RADAR_ANGLE for negative values' do
168
+ robot = robot(radar_heading: 100)
169
+
170
+ robot.turn_radar(-100)
171
+
172
+ expect(robot.radar_heading).to be(100-RubyArena::Robot::MAX_TURN_RADAR_ANGLE)
173
+ end
174
+ end
175
+
176
+ describe '#fire' do
177
+ it 'adds new bullet to arena' do
178
+ expect(arena).to receive(:add_bullet)
179
+
180
+ robot.fire
181
+ end
182
+
183
+ it 'increases gun heat' do
184
+ robot.fire
185
+
186
+ expect(robot.gun_heat).to be 3
187
+ end
188
+
189
+ it "doesn't fire if gun heat is more than 0" do
190
+ robot = robot(gun_heat: 3)
191
+
192
+ expect(arena).not_to receive(:add_bullet)
193
+
194
+ robot.fire
195
+ end
196
+ end
197
+
198
+ describe '#execute_actions' do
199
+ it 'turns robot if turn action is set' do
200
+ expect(robot).to receive(:turn).with(10)
201
+
202
+ robot.execute_actions({ turn: 10 })
203
+ end
204
+
205
+ it 'turns gun if turn_gun action is set' do
206
+ expect(robot).to receive(:turn_gun).with(20)
207
+
208
+ robot.execute_actions({ turn_gun: 20 })
209
+ end
210
+
211
+ it 'turns radar if turn_radar action is set' do
212
+ expect(robot).to receive(:turn_radar).with(30)
213
+
214
+ robot.execute_actions({ turn_radar: 30 })
215
+ end
216
+
217
+ it 'accelerate if accelerate action is set' do
218
+ expect(robot).to receive(:accelerate)
219
+
220
+ robot.execute_actions({ accelerate: true })
221
+ end
222
+
223
+ it 'decelerate if decelerate action is set' do
224
+ expect(robot).to receive(:decelerate)
225
+
226
+ robot.execute_actions({ decelerate: true })
227
+ end
228
+
229
+ it 'fires if fire action is set' do
230
+ expect(robot).to receive(:fire)
231
+
232
+ robot.execute_actions({ fire: true })
233
+ end
234
+
235
+ it 'changes radar view angle if radar view angle action is set' do
236
+ robot = robot(radar_view_angle: 10)
237
+
238
+ robot.execute_actions({ radar_view_angle: 5 })
239
+
240
+ expect(robot.radar_view_angle).to be 5
241
+ end
242
+ end
243
+
244
+ describe '#hit' do
245
+ it 'decreases energy by bullet energy' do
246
+ robot = robot(energy: 100)
247
+ bullet = double('bullet', energy: 10)
248
+
249
+ robot.hit(bullet)
250
+
251
+ expect(robot.energy).to be 90
252
+ end
253
+ end
254
+
255
+ describe '#dead?' do
256
+ it 'returns false if energy is more than 0' do
257
+ expect(robot.dead?).to be false
258
+ end
259
+
260
+ it 'returns true if energy is less than 0' do
261
+ robot = robot(energy: -1)
262
+
263
+ expect(robot.dead?).to be true
264
+ end
265
+ end
266
+
267
+ describe '#scan' do
268
+ it 'returns distance from all robots in radar view and range' do
269
+ robot_in_radar_view = double('robot_in_radar_view', x: 200, y: 100)
270
+ robot_not_in_radar_view = double('robot_not_in_radar_view', x: 0, y: 0)
271
+ arena = double('arena', robots: [robot_in_radar_view, robot_not_in_radar_view])
272
+ robot = robot(x: 100, y: 100, heading: 90, arena: arena)
273
+
274
+ expect(robot.scan).to eq [100]
275
+ end
276
+ end
277
+
278
+ def object(args = {})
279
+ robot(args)
280
+ end
281
+
282
+ def robot(args = {})
283
+ defaults = {
284
+ ai: RSpec::Mocks::Mock,
285
+ arena: arena
286
+ }
287
+
288
+ @_robot ||= RubyArena::Robot.new(defaults.merge(args))
289
+ end
290
+
291
+ def arena
292
+ @_arena ||= double('arena', robots: [], width: 100, height: 100, add_bullet: nil)
293
+ end
294
+ end
@@ -0,0 +1,8 @@
1
+ require 'ruby_arena'
2
+
3
+ require 'ai_test'
4
+ require 'fake_gui'
5
+ require 'movable_tests'
6
+
7
+ RSpec.configure do |config|
8
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe RubyArena::ValueSanitizer do
4
+ describe '.sanitize' do
5
+ it 'returns 1 when value is 1 and max change is 10' do
6
+ expect(RubyArena::ValueSanitizer.sanitize(1, 10)).to be 1
7
+ end
8
+
9
+ it 'returns 10 when value is 100 and max change is 10' do
10
+ expect(RubyArena::ValueSanitizer.sanitize(100, 10)).to be 10
11
+ end
12
+
13
+ it 'returns -10 when value is -10 and max change is 10' do
14
+ expect(RubyArena::ValueSanitizer.sanitize(-10, 10)).to be(-10)
15
+ end
16
+
17
+ it 'returns -10 when value is -100 and max change is 10' do
18
+ expect(RubyArena::ValueSanitizer.sanitize(-100, 10)).to be(-10)
19
+ end
20
+ end
21
+ end