colstrom-rtanque 0.1.3
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 +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
|