colstrom-rtanque 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +10 -0
- data/.yardopts +4 -0
- data/Gemfile +4 -0
- data/Gemfile.ci +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +168 -0
- data/Rakefile +1 -0
- data/bin/rtanque +117 -0
- data/lib/rtanque.rb +31 -0
- data/lib/rtanque/arena.rb +8 -0
- data/lib/rtanque/bot.rb +117 -0
- data/lib/rtanque/bot/brain.rb +50 -0
- data/lib/rtanque/bot/brain_helper.rb +23 -0
- data/lib/rtanque/bot/command.rb +23 -0
- data/lib/rtanque/bot/radar.rb +54 -0
- data/lib/rtanque/bot/sensors.rb +37 -0
- data/lib/rtanque/bot/turret.rb +14 -0
- data/lib/rtanque/configuration.rb +47 -0
- data/lib/rtanque/explosion.rb +23 -0
- data/lib/rtanque/gui.rb +25 -0
- data/lib/rtanque/gui/bot.rb +71 -0
- data/lib/rtanque/gui/bot/health_color_calculator.rb +37 -0
- data/lib/rtanque/gui/draw_group.rb +30 -0
- data/lib/rtanque/gui/explosion.rb +25 -0
- data/lib/rtanque/gui/shell.rb +31 -0
- data/lib/rtanque/gui/window.rb +64 -0
- data/lib/rtanque/heading.rb +162 -0
- data/lib/rtanque/match.rb +73 -0
- data/lib/rtanque/match/tick_group.rb +50 -0
- data/lib/rtanque/movable.rb +51 -0
- data/lib/rtanque/normalized_attr.rb +69 -0
- data/lib/rtanque/point.rb +140 -0
- data/lib/rtanque/runner.rb +88 -0
- data/lib/rtanque/shell.rb +44 -0
- data/lib/rtanque/version.rb +3 -0
- data/resources/images/body.png +0 -0
- data/resources/images/bullet.png +0 -0
- data/resources/images/explosions/explosion2-1.png +0 -0
- data/resources/images/explosions/explosion2-10.png +0 -0
- data/resources/images/explosions/explosion2-11.png +0 -0
- data/resources/images/explosions/explosion2-12.png +0 -0
- data/resources/images/explosions/explosion2-13.png +0 -0
- data/resources/images/explosions/explosion2-14.png +0 -0
- data/resources/images/explosions/explosion2-15.png +0 -0
- data/resources/images/explosions/explosion2-16.png +0 -0
- data/resources/images/explosions/explosion2-17.png +0 -0
- data/resources/images/explosions/explosion2-18.png +0 -0
- data/resources/images/explosions/explosion2-19.png +0 -0
- data/resources/images/explosions/explosion2-2.png +0 -0
- data/resources/images/explosions/explosion2-20.png +0 -0
- data/resources/images/explosions/explosion2-21.png +0 -0
- data/resources/images/explosions/explosion2-22.png +0 -0
- data/resources/images/explosions/explosion2-23.png +0 -0
- data/resources/images/explosions/explosion2-24.png +0 -0
- data/resources/images/explosions/explosion2-25.png +0 -0
- data/resources/images/explosions/explosion2-26.png +0 -0
- data/resources/images/explosions/explosion2-27.png +0 -0
- data/resources/images/explosions/explosion2-28.png +0 -0
- data/resources/images/explosions/explosion2-29.png +0 -0
- data/resources/images/explosions/explosion2-3.png +0 -0
- data/resources/images/explosions/explosion2-30.png +0 -0
- data/resources/images/explosions/explosion2-31.png +0 -0
- data/resources/images/explosions/explosion2-32.png +0 -0
- data/resources/images/explosions/explosion2-33.png +0 -0
- data/resources/images/explosions/explosion2-34.png +0 -0
- data/resources/images/explosions/explosion2-35.png +0 -0
- data/resources/images/explosions/explosion2-36.png +0 -0
- data/resources/images/explosions/explosion2-37.png +0 -0
- data/resources/images/explosions/explosion2-38.png +0 -0
- data/resources/images/explosions/explosion2-39.png +0 -0
- data/resources/images/explosions/explosion2-4.png +0 -0
- data/resources/images/explosions/explosion2-40.png +0 -0
- data/resources/images/explosions/explosion2-41.png +0 -0
- data/resources/images/explosions/explosion2-42.png +0 -0
- data/resources/images/explosions/explosion2-43.png +0 -0
- data/resources/images/explosions/explosion2-44.png +0 -0
- data/resources/images/explosions/explosion2-45.png +0 -0
- data/resources/images/explosions/explosion2-46.png +0 -0
- data/resources/images/explosions/explosion2-47.png +0 -0
- data/resources/images/explosions/explosion2-48.png +0 -0
- data/resources/images/explosions/explosion2-49.png +0 -0
- data/resources/images/explosions/explosion2-5.png +0 -0
- data/resources/images/explosions/explosion2-50.png +0 -0
- data/resources/images/explosions/explosion2-51.png +0 -0
- data/resources/images/explosions/explosion2-52.png +0 -0
- data/resources/images/explosions/explosion2-53.png +0 -0
- data/resources/images/explosions/explosion2-54.png +0 -0
- data/resources/images/explosions/explosion2-55.png +0 -0
- data/resources/images/explosions/explosion2-56.png +0 -0
- data/resources/images/explosions/explosion2-57.png +0 -0
- data/resources/images/explosions/explosion2-58.png +0 -0
- data/resources/images/explosions/explosion2-59.png +0 -0
- data/resources/images/explosions/explosion2-6.png +0 -0
- data/resources/images/explosions/explosion2-60.png +0 -0
- data/resources/images/explosions/explosion2-61.png +0 -0
- data/resources/images/explosions/explosion2-62.png +0 -0
- data/resources/images/explosions/explosion2-63.png +0 -0
- data/resources/images/explosions/explosion2-64.png +0 -0
- data/resources/images/explosions/explosion2-65.png +0 -0
- data/resources/images/explosions/explosion2-66.png +0 -0
- data/resources/images/explosions/explosion2-67.png +0 -0
- data/resources/images/explosions/explosion2-68.png +0 -0
- data/resources/images/explosions/explosion2-69.png +0 -0
- data/resources/images/explosions/explosion2-7.png +0 -0
- data/resources/images/explosions/explosion2-70.png +0 -0
- data/resources/images/explosions/explosion2-71.png +0 -0
- data/resources/images/explosions/explosion2-8.png +0 -0
- data/resources/images/explosions/explosion2-9.png +0 -0
- data/resources/images/grass.png +0 -0
- data/resources/images/radar.png +0 -0
- data/resources/images/turret.png +0 -0
- data/rtanque.gemspec +34 -0
- data/sample_bots/camper.rb +79 -0
- data/sample_bots/keyboard.rb +50 -0
- data/sample_bots/seek_and_destroy.rb +53 -0
- data/screenshots/battle_1.png +0 -0
- data/screenshots/battle_2.png +0 -0
- data/spec/rtanque/bot_spec.rb +239 -0
- data/spec/rtanque/heading_spec.rb +279 -0
- data/spec/rtanque/match_spec.rb +46 -0
- data/spec/rtanque/normalized_attr_spec.rb +90 -0
- data/spec/rtanque/point_spec.rb +196 -0
- data/spec/rtanque/radar_spec.rb +87 -0
- data/spec/rtanque/shell_spec.rb +35 -0
- data/spec/rtanque_spec.rb +34 -0
- data/spec/spec_helper.rb +51 -0
- data/templates/bot.erb +17 -0
- metadata +315 -0
@@ -0,0 +1,279 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe RTanque::Heading do
|
4
|
+
NINETY = Math::PI / 2.0
|
5
|
+
|
6
|
+
describe '#delta' do
|
7
|
+
before do
|
8
|
+
@instance = described_class.new(0)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'receives floats' do
|
12
|
+
other = 0.0
|
13
|
+
expect(@instance.delta(other)).to eql 0.0
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'receives headings' do
|
17
|
+
other = described_class.new(0.0)
|
18
|
+
expect(@instance.delta(other)).to eql 0.0
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'gives 0 when provided same a == b' do
|
22
|
+
expect(@instance.delta(@instance)).to eq 0
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'outputs positive when a < b' do
|
26
|
+
expect(@instance.delta(@instance + 1)).to be > 0
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'outputs negative when b < a' do
|
30
|
+
expect(@instance.delta(@instance - 1)).to be < 0
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'correct output when difference < 180 deg' do
|
34
|
+
expect(@instance.delta(NINETY)).to eq NINETY
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'correct output when difference > 180 deg' do
|
38
|
+
expect(@instance.delta(NINETY * 3)).to eq -NINETY
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'correctly handles degress > 360' do
|
42
|
+
expect(@instance.delta(NINETY * 5)).to eq NINETY
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'correctly handles differences > 180 in which a < b' do
|
46
|
+
expect(@instance.delta(NINETY * 3)).to eq -NINETY
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'correctly handles differences > 180 in which a > b' do
|
50
|
+
expect(@instance.delta(-(NINETY * 3))).to eq NINETY
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'correctly handles when a and b are on both sides of 180' do
|
54
|
+
@instance = described_class.new(RTanque::Heading::S - RTanque::Heading::ONE_DEGREE)
|
55
|
+
delta = (@instance.delta(RTanque::Heading::S + RTanque::Heading::ONE_DEGREE)).round(5)
|
56
|
+
expected = (RTanque::Heading::ONE_DEGREE * 2).round(5)
|
57
|
+
expect(delta).to eq(expected)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe '.new_from_degrees' do
|
62
|
+
it 'receives positive degrees' do
|
63
|
+
expect(described_class.new_from_degrees(90).to_f).to eql NINETY
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'receives degrees large than 360' do
|
67
|
+
expect(described_class.new_from_degrees(90 + 360).to_f).to eql NINETY
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'receives negative degrees' do
|
71
|
+
expect(described_class.new_from_degrees(-90).to_f).to eql NINETY * 3
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'receives negative degrees less than -360' do
|
75
|
+
expect(described_class.new_from_degrees(-90 - 360).to_f).to eql NINETY * 3
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe '.delta_between_points' do
|
80
|
+
let(:from_point) { RTanque::Point.new(10, 10, @arena) }
|
81
|
+
let(:from_heading) { described_class.new(0) }
|
82
|
+
it 'is correct when 0 delta' do
|
83
|
+
to_point = RTanque::Point.new(10, 20, @arena)
|
84
|
+
expect(described_class.delta_between_points(from_point, from_heading, to_point)).to eq 0
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'is correct when delta is negative' do
|
88
|
+
to_point = RTanque::Point.new(9, 11)
|
89
|
+
expect(described_class.delta_between_points(from_point, from_heading, to_point)).to eq -(NINETY / 2)
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'is correct when delta is positive' do
|
93
|
+
to_point = RTanque::Point.new(11, 11)
|
94
|
+
expect(described_class.delta_between_points(from_point, from_heading, to_point)).to eq(NINETY / 2)
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'is correct when delta is max' do
|
98
|
+
to_point = RTanque::Point.new(10, 9)
|
99
|
+
expect(described_class.delta_between_points(from_point, from_heading, to_point)).to eq(NINETY * 2)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe '#initialize' do
|
104
|
+
it 'receives inits and sets radians to float' do
|
105
|
+
expect(described_class.new(0).radians).to eql 0.0
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'receives negative floats' do
|
109
|
+
expect(described_class.new(-NINETY).radians).to eql NINETY * 3
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'creates frozen object' do
|
113
|
+
expect(described_class.new(0).frozen?).to be_true
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
describe '#clone' do
|
118
|
+
it 'returns a Heading' do
|
119
|
+
expect(described_class.new.clone).to be_instance_of described_class
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'returns a new object' do
|
123
|
+
original = described_class.new
|
124
|
+
copy = original.clone
|
125
|
+
expect(original.object_id).not_to eq copy.object_id
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'returns a new object which does not affect old' do
|
129
|
+
original = described_class.new(0.0)
|
130
|
+
copy = original.clone
|
131
|
+
expect(copy).not_to equal original
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe '#==' do
|
136
|
+
it 'works like Numeric, ignoring type' do
|
137
|
+
a = described_class.new(1.0)
|
138
|
+
expect(a == 1).to be_true
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'correctly compares two equal headings' do
|
142
|
+
a = described_class.new(1.0)
|
143
|
+
b = described_class.new(1.0)
|
144
|
+
expect(a == b).to be_true
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'correctly compares two different headings' do
|
148
|
+
a = described_class.new(0)
|
149
|
+
b = described_class.new(1.0)
|
150
|
+
expect(a == b).to be_false
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'compares to a numeric on LHS' do
|
154
|
+
a = described_class.new(Math::PI)
|
155
|
+
expect(Math::PI == a).to be_true
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
describe '#eql?' do
|
160
|
+
it 'compares types like Numeric, comparing type' do
|
161
|
+
a = described_class.new(1.0)
|
162
|
+
expect(a.eql?(1)).to be_false
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'correctly compares two equal headings' do
|
166
|
+
a = described_class.new(1.0)
|
167
|
+
b = described_class.new(1.0)
|
168
|
+
expect(a.eql?(b)).to be_true
|
169
|
+
end
|
170
|
+
|
171
|
+
it 'correctly compares two different headings' do
|
172
|
+
a = described_class.new(0)
|
173
|
+
b = described_class.new(1.0)
|
174
|
+
expect(a.eql?(b)).to be_false
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
describe '#<=>' do
|
179
|
+
it 'receives headings' do
|
180
|
+
a = described_class.new
|
181
|
+
b = described_class.new(NINETY)
|
182
|
+
expect(a <=> b).to eq -1
|
183
|
+
end
|
184
|
+
|
185
|
+
it 'receives floats' do
|
186
|
+
a = described_class.new
|
187
|
+
expect(a <=> NINETY).to eq -1
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
describe '#+' do
|
192
|
+
it 'returns new instance' do
|
193
|
+
a = described_class.new(0.0)
|
194
|
+
result = a + NINETY
|
195
|
+
expect(result).not_to eql a
|
196
|
+
expect(result).not_to equal a
|
197
|
+
expect(result).to be_kind_of described_class
|
198
|
+
end
|
199
|
+
|
200
|
+
it 'leaves receiver unchanged' do
|
201
|
+
a = described_class.new(1.0)
|
202
|
+
a + NINETY
|
203
|
+
expect(a).to eql described_class.new(1.0)
|
204
|
+
end
|
205
|
+
|
206
|
+
it 'correctly adds' do
|
207
|
+
a = described_class.new(0.0) + NINETY
|
208
|
+
expect(a).to eq NINETY
|
209
|
+
end
|
210
|
+
|
211
|
+
it 'correctly adds negative numbers' do
|
212
|
+
a = described_class.new(0.0) + -NINETY
|
213
|
+
expect(a).to eq(NINETY * 3)
|
214
|
+
end
|
215
|
+
|
216
|
+
it 'correctly adds other headings' do
|
217
|
+
a = described_class.new(NINETY * 2)
|
218
|
+
b = described_class.new(NINETY)
|
219
|
+
expect(a + b).to eq(NINETY * 3)
|
220
|
+
end
|
221
|
+
|
222
|
+
it 'respects radian module ring' do
|
223
|
+
a = described_class.new(NINETY * 3)
|
224
|
+
b = described_class.new(NINETY * 3)
|
225
|
+
expect(a + b).to eq NINETY * 2
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
describe '#*' do
|
230
|
+
it 'returns new instance' do
|
231
|
+
a = described_class.new(NINETY)
|
232
|
+
result = a * NINETY
|
233
|
+
expect(result).not_to eql a
|
234
|
+
expect(result).not_to equal a
|
235
|
+
expect(result).to be_kind_of described_class
|
236
|
+
end
|
237
|
+
|
238
|
+
it 'correctly multiplies' do
|
239
|
+
a = described_class.new(NINETY)
|
240
|
+
expect(a * 2.0).to eq NINETY * 2.0
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
describe '#/' do
|
245
|
+
it 'returns new instance' do
|
246
|
+
a = described_class.new(NINETY)
|
247
|
+
result = a / NINETY
|
248
|
+
expect(result).not_to eql a
|
249
|
+
expect(result).not_to equal a
|
250
|
+
expect(result).to be_kind_of described_class
|
251
|
+
end
|
252
|
+
|
253
|
+
it 'correctly divides' do
|
254
|
+
a = described_class.new(NINETY * 2.0)
|
255
|
+
expect(a / 2.0).to eq NINETY
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
describe '#-@' do
|
260
|
+
it 'creates a new instance' do
|
261
|
+
a = described_class.new(1.0)
|
262
|
+
b = -a
|
263
|
+
expect(b).not_to equal a
|
264
|
+
end
|
265
|
+
|
266
|
+
it 'correctly negates itself' do
|
267
|
+
expect(-described_class.new(NINETY)).to eq(NINETY * 3)
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
describe '#to_degrees' do
|
272
|
+
it 'returns float' do
|
273
|
+
a = described_class.new
|
274
|
+
expect(a.to_degrees).to eq 0.0
|
275
|
+
b = described_class.new(NINETY)
|
276
|
+
expect(b.to_degrees).to eq 90.0
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe RTanque::Match do
|
4
|
+
before do
|
5
|
+
@instance = described_class.new(@arena)
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'should start with 0 ticks' do
|
9
|
+
expect(@instance.ticks).to eql 0
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should allow adding bots' do
|
13
|
+
bot = double('bot')
|
14
|
+
@instance.add_bots(bot)
|
15
|
+
expect(@instance.bots.include?(bot)).to be_true
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '#finished?' do
|
19
|
+
it 'should be true if one or less bots left' do
|
20
|
+
expect(@instance.finished?).to be_true
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should be false if two or more bots left' do
|
24
|
+
bot1 = double('bot', :name => "bot1")
|
25
|
+
bot2 = double('bot', :name => "bot2")
|
26
|
+
@instance.add_bots(bot1, bot2)
|
27
|
+
expect(@instance.finished?).to be_false
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should be true if two or more bots left w/ same name' do
|
31
|
+
@instance.teams = true
|
32
|
+
bot1 = double('bot', :name => "bot1")
|
33
|
+
bot2 = double('bot', :name => "bot1")
|
34
|
+
@instance.add_bots(bot1, bot2)
|
35
|
+
expect(@instance.finished?).to be_true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '#tick' do
|
40
|
+
it 'should increment tick count' do
|
41
|
+
@instance.tick
|
42
|
+
expect(@instance.ticks).to eq 1
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
INFINITY = 1.0 / 0.0
|
4
|
+
|
5
|
+
describe RTanque::NormalizedAttr do
|
6
|
+
context 'integer range, no max delta' do
|
7
|
+
let(:instance) do
|
8
|
+
described_class::AttrContainer.new(1..5)
|
9
|
+
end
|
10
|
+
let(:attached_instance) do
|
11
|
+
mock('some_object')
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'has -infinity as max_delta' do
|
15
|
+
expect(instance.max_delta(attached_instance)).to eq INFINITY
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'has correct delta' do
|
19
|
+
expect(instance.delta(nil, 100)).to eq 0
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'does not allow more than range' do
|
23
|
+
expect(instance.normalize(attached_instance, nil, 100)).to eq 5
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'does not allow less than range' do
|
27
|
+
expect(instance.normalize(attached_instance, nil, -100)).to eq 1
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'integer range, max delta' do
|
32
|
+
let(:instance) do
|
33
|
+
described_class::AttrContainer.new(0..5, lambda{ |attached| attached.max_delta })
|
34
|
+
end
|
35
|
+
let(:attached_instance) do
|
36
|
+
mock('some_object', :max_delta => 0.1)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should call block for max_delta' do
|
40
|
+
attached = mock('some_object')
|
41
|
+
attached.should_receive(:max_delta).and_return(0.1)
|
42
|
+
expect(instance.max_delta(attached)).to eq 0.1
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'does not allow a change greater than given delta' do
|
46
|
+
expect(instance.normalize(attached_instance, 1, 2)).to eq 1.1
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'does not allow a change less than given delta' do
|
50
|
+
expect(instance.normalize(attached_instance, 2, 1)).to eq 1.9
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
#context 'heading range' do
|
55
|
+
# let(:normalized) do
|
56
|
+
# described_class::AttrContainer.new(0..RTanque::Heading::FULL_ANGLE, RTanque::Heading::ONE_DEGREE)
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# it 'should respect step' do
|
60
|
+
# expect(normalized.normalize(Math::PI, 0)).to eq RTanque::Heading::ONE_DEGREE
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
# it 'should allow full 360 turn' do
|
64
|
+
# previous = 0
|
65
|
+
# 360.times do
|
66
|
+
# new_value = normalized.normalize(previous + Math::PI, previous)
|
67
|
+
# expect(new_value).to eq(RTanque::Heading.new(previous) + RTanque::Heading::ONE_DEGREE)
|
68
|
+
# previous = new_value
|
69
|
+
# end
|
70
|
+
# end
|
71
|
+
#end
|
72
|
+
#
|
73
|
+
#context 'mixin' do
|
74
|
+
# class Tester
|
75
|
+
# extend RTanque::NormalizedAttr
|
76
|
+
# attr_accessor :speed
|
77
|
+
# attr_normalized :speed, 0..5
|
78
|
+
# end
|
79
|
+
#
|
80
|
+
# it 'defines normalize method' do
|
81
|
+
# expect(Tester.instance_methods.grep(/normalized_speed/)).to have(1).match
|
82
|
+
# end
|
83
|
+
#
|
84
|
+
# it 'normalize method behaves correctly' do
|
85
|
+
# i = Tester.new
|
86
|
+
# expect(i.normalized_speed(100)).to eq 5
|
87
|
+
# end
|
88
|
+
#
|
89
|
+
#end
|
90
|
+
end
|
@@ -0,0 +1,196 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe RTanque::Point do
|
4
|
+
context '.distance' do
|
5
|
+
it 'gives 0 when points are equal' do
|
6
|
+
a = described_class.new(0, 0, @arena)
|
7
|
+
b = described_class.new(0, 0, @arena)
|
8
|
+
expect(described_class.distance(a,b)).to eq 0
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'always returns positive number' do
|
12
|
+
a = described_class.new(0, 0, @arena)
|
13
|
+
b = described_class.new(10, 0, @arena)
|
14
|
+
expect(described_class.distance(a, b)).to eq 10
|
15
|
+
expect(described_class.distance(b, a)).to eq 10
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context '#initialize' do
|
20
|
+
it 'should be frozen' do
|
21
|
+
a = described_class.new(10, 10, @arena)
|
22
|
+
expect(a.frozen?).to be_true
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should not allow modification' do
|
26
|
+
a = described_class.new(10, 10, @arena)
|
27
|
+
expect { a.x = 1 }.to raise_exception(RUBY_VERSION >= '1.9' ? RuntimeError : TypeError)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context '#heading' do
|
32
|
+
before do
|
33
|
+
@instance = described_class.new(5, 5, @arena)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'return correct heading when at 0deg' do
|
37
|
+
other = described_class.new(5, 10, @arena)
|
38
|
+
expect(@instance.heading(other)).to eq RTanque::Heading::N
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'return correct heading when at 45deg' do
|
42
|
+
other = described_class.new(10, 10, @arena)
|
43
|
+
expect(@instance.heading(other)).to eq RTanque::Heading::NE
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'return correct heading when at 90deg' do
|
47
|
+
other = described_class.new(10, 5, @arena)
|
48
|
+
expect(@instance.heading(other)).to eq RTanque::Heading::E
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'return correct heading when at 135deg' do
|
52
|
+
other = described_class.new(10, 0, @arena)
|
53
|
+
expect(@instance.heading(other)).to eq RTanque::Heading::SE
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'return correct heading when at 180deg' do
|
57
|
+
other = described_class.new(5, 0, @arena)
|
58
|
+
expect(@instance.heading(other)).to eq RTanque::Heading::S
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'return correct heading when at 225deg' do
|
62
|
+
other = described_class.new(0, 0, @arena)
|
63
|
+
expect(@instance.heading(other)).to eq RTanque::Heading::SW
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'return correct heading when at 270deg' do
|
67
|
+
other = described_class.new(0, 5, @arena)
|
68
|
+
expect(@instance.heading(other)).to eq RTanque::Heading::W
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'return correct heading when at 315deg' do
|
72
|
+
other = described_class.new(0, 10, @arena)
|
73
|
+
expect(@instance.heading(other)).to eq RTanque::Heading::NW
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'returns correct heading when points are the same' do
|
77
|
+
other = @instance.clone
|
78
|
+
expect(@instance.heading(other)).to eq RTanque::Heading::N
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context '#==' do
|
83
|
+
it 'is true when x & y are equal' do
|
84
|
+
a = described_class.new(1, 1, @arena)
|
85
|
+
b = described_class.new(1, 1, @arena)
|
86
|
+
expect(a == b).to be_true
|
87
|
+
expect(b == a).to be_true
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'is not true when x & y are equal' do
|
91
|
+
a = described_class.new(2, 1, @arena)
|
92
|
+
b = described_class.new(1, 1, @arena)
|
93
|
+
expect(a == b).to be_false
|
94
|
+
expect(b == a).to be_false
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context '#move' do
|
99
|
+
before do
|
100
|
+
@init_x, @init_y = 0.0, 0.0
|
101
|
+
@point = RTanque::Point.new(@init_x, @init_y, @arena)
|
102
|
+
@heading = RTanque::Heading.new(RTanque::Heading::NORTH)
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'should not move if no speed' do
|
106
|
+
new_point = @point.move(@heading, 0)
|
107
|
+
expect(new_point.x).to eq @init_x
|
108
|
+
expect(new_point.y).to eq @init_y
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'should move on y axis' do
|
112
|
+
new_point = @point.move(@heading, 1)
|
113
|
+
expect(new_point.x).to eq @init_x
|
114
|
+
expect(new_point.y).to eq @init_y + 1
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'should move on x axis' do
|
118
|
+
@heading = RTanque::Heading.new(RTanque::Heading::EAST)
|
119
|
+
new_point = @point.move(@heading, 1)
|
120
|
+
expect(new_point.x).to eq @init_x + 1
|
121
|
+
expect(new_point.y).to eq @init_y
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'should move on y axis twice' do
|
125
|
+
@heading = RTanque::Heading.new(RTanque::Heading::EAST)
|
126
|
+
new_point = @point.move(@heading, 1).move(@heading, 1)
|
127
|
+
expect(new_point.x).to eq @init_x + 2
|
128
|
+
expect(new_point.y).to eq @init_y
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'should move on x and y axis' do
|
132
|
+
@heading = RTanque::Heading.new(RTanque::Heading::NORTH_EAST)
|
133
|
+
new_point = @point.move(@heading, Math.hypot(1,1) * 2)
|
134
|
+
expect(new_point.x).to eq @init_x + 2
|
135
|
+
expect(new_point.y).to eq @init_y + 2
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'should move on x and y axis forwards and backwards' do
|
139
|
+
@heading = RTanque::Heading.new(RTanque::Heading::NORTH_EAST)
|
140
|
+
new_point = @point.move(@heading, Math.hypot(1,1) * 2)
|
141
|
+
expect(new_point.x).to eq @init_x + 2
|
142
|
+
expect(new_point.y).to eq @init_y + 2
|
143
|
+
new_point = @point.move(@heading, -Math.hypot(1,1) * 2)
|
144
|
+
expect(new_point.x).to eq @init_x
|
145
|
+
expect(new_point.y).to eq @init_y
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'should stay within arena when bound' do
|
149
|
+
@heading = RTanque::Heading.new(RTanque::Heading::NORTH_EAST)
|
150
|
+
new_point = @point.move(@heading, Math.hypot(1,1) * 1000)
|
151
|
+
expect(new_point.x).to eq @arena.width
|
152
|
+
expect(new_point.y).to eq @arena.height
|
153
|
+
|
154
|
+
@heading = RTanque::Heading.new(RTanque::Heading::NORTH_EAST)
|
155
|
+
new_point = @point.move(@heading, Math.hypot(1,1) * -1000)
|
156
|
+
expect(new_point.x).to eq @init_x
|
157
|
+
expect(new_point.y).to eq @init_y
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
describe '#within_radius?' do
|
162
|
+
before do
|
163
|
+
@a = described_class.new(5, 5, @arena)
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'correctly detects equal points' do
|
167
|
+
@b = @a.clone
|
168
|
+
expect(@a.within_radius?(@b, 1)).to be_true
|
169
|
+
end
|
170
|
+
|
171
|
+
it 'correctly detects point 1 to left' do
|
172
|
+
@b = described_class.new(4, 5, @arena)
|
173
|
+
expect(@a.within_radius?(@b, 1)).to be_true
|
174
|
+
end
|
175
|
+
|
176
|
+
it 'correctly detects point 1 to right' do
|
177
|
+
@b = described_class.new(6, 5, @arena)
|
178
|
+
expect(@a.within_radius?(@b, 1)).to be_true
|
179
|
+
end
|
180
|
+
|
181
|
+
it 'correctly detects point 1 above' do
|
182
|
+
@b = described_class.new(5, 6, @arena)
|
183
|
+
expect(@a.within_radius?(@b, 1)).to be_true
|
184
|
+
end
|
185
|
+
|
186
|
+
it 'correctly detects point 1 below' do
|
187
|
+
@b = described_class.new(5, 4, @arena)
|
188
|
+
expect(@a.within_radius?(@b, 1)).to be_true
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'correctly detects point 1 NE' do
|
192
|
+
@b = described_class.new(6, 6, @arena)
|
193
|
+
expect(@a.within_radius?(@b, 1)).to be_false
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|