rtanque 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +22 -0
- data/.rspec +2 -0
- data/.rvmrc +34 -0
- data/.travis.yml +11 -0
- data/.yardopts +4 -0
- data/Gemfile +4 -0
- data/Gemfile.ci +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +120 -0
- data/Rakefile +1 -0
- data/bin/rtanque +108 -0
- data/lib/rtanque.rb +40 -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 +33 -0
- data/lib/rtanque/bot/turret.rb +14 -0
- data/lib/rtanque/configuration.rb +46 -0
- data/lib/rtanque/explosion.rb +23 -0
- data/lib/rtanque/gui.rb +24 -0
- data/lib/rtanque/gui/bot.rb +42 -0
- data/lib/rtanque/gui/draw_group.rb +30 -0
- data/lib/rtanque/gui/explosion.rb +25 -0
- data/lib/rtanque/gui/shell.rb +20 -0
- data/lib/rtanque/gui/window.rb +51 -0
- data/lib/rtanque/heading.rb +172 -0
- data/lib/rtanque/match.rb +67 -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 +40 -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 +33 -0
- data/sample_bots/camper.rb +79 -0
- data/sample_bots/keyboard.rb +50 -0
- data/sample_bots/seek_and_destroy.rb +51 -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 +36 -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 +6 -0
- data/spec/spec_helper.rb +51 -0
- data/templates/bot.erb +11 -0
- metadata +310 -0
@@ -0,0 +1,36 @@
|
|
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
|
+
@instance.add_bots(:bot, :bot2)
|
25
|
+
expect(@instance.finished?).to be_false
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '#tick' do
|
30
|
+
it 'should increment tick count' do
|
31
|
+
@instance.tick
|
32
|
+
expect(@instance.ticks).to eq 1
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
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
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe RTanque::Bot::Radar do
|
4
|
+
def reflection(heading, distance, name)
|
5
|
+
described_class::Reflection.new(RTanque::Heading.new(heading), distance, name)
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:radar) { described_class.new(mockbot(10, 10), RTanque::Heading.new(0)) }
|
9
|
+
|
10
|
+
context '#scan' do
|
11
|
+
it 'should return an array' do
|
12
|
+
radar.scan([])
|
13
|
+
expect(radar.to_a).to be_an_instance_of Array
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should return an empty array' do
|
17
|
+
radar.scan([])
|
18
|
+
expect(radar.empty?).to be_true
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should 1 Reflection' do
|
22
|
+
radar.scan([mockbot(10, 20)])
|
23
|
+
expect(radar).to have(1).reflections
|
24
|
+
expect(radar.first).to be_an_instance_of described_class::Reflection
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'reflection should be correct' do
|
28
|
+
radar.scan([mockbot(10, 20, 'otherbot')])
|
29
|
+
expect(radar.first).to eq reflection(0, 10.0, 'otherbot')
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should clear self' do
|
33
|
+
radar.scan([mockbot(10, 20, 'otherbot')])
|
34
|
+
radar.scan([])
|
35
|
+
expect(radar.empty?).to be_true
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'reflections should be correct' do
|
39
|
+
bots = [mockbot(10, 20, 'below'), mockbot(10, 50, 'right')]
|
40
|
+
radar.scan(bots)
|
41
|
+
expect(radar.to_a).to include(
|
42
|
+
reflection(RTanque::Heading::N, 10.0, 'below'),
|
43
|
+
reflection(RTanque::Heading::N, 40.0, 'right')
|
44
|
+
)
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should detect bot in same position' do
|
48
|
+
bots = [mockbot(10, 10, 'ontop')]
|
49
|
+
radar.scan(bots)
|
50
|
+
expect(radar.to_a).to include(
|
51
|
+
reflection(RTanque::Heading::N, 0.0, 'ontop')
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'should detect point on max vision range' do
|
56
|
+
x = 10 + Math.sin(RTanque::Bot::Radar::VISION_RANGE.last) * 10
|
57
|
+
y = 10 + Math.cos(RTanque::Bot::Radar::VISION_RANGE.last) * 10
|
58
|
+
bots = [mockbot(x, y, 'border')]
|
59
|
+
radar.scan(bots)
|
60
|
+
expect(radar.to_a).to have(1).reflections
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'should detect point on min vision range' do
|
64
|
+
x = 10 + Math.sin(RTanque::Bot::Radar::VISION_RANGE.first) * 10
|
65
|
+
y = 10 + Math.cos(RTanque::Bot::Radar::VISION_RANGE.first) * 10
|
66
|
+
bots = [mockbot(x, y, 'border')]
|
67
|
+
radar.scan(bots)
|
68
|
+
expect(radar.to_a).to have(1).reflections
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should not detect point outside max vision range' do
|
72
|
+
x = 10 + Math.sin(RTanque::Bot::Radar::VISION_RANGE.last) * 10
|
73
|
+
y = 10 + Math.cos(RTanque::Bot::Radar::VISION_RANGE.last) * 10
|
74
|
+
bots = [mockbot(x + 0.01, y, 'border')]
|
75
|
+
radar.scan(bots)
|
76
|
+
expect(radar.to_a).to have(0).reflections
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'should not detect point outside max vision range' do
|
80
|
+
x = 10 + Math.sin(RTanque::Bot::Radar::VISION_RANGE.first) * 10
|
81
|
+
y = 10 + Math.cos(RTanque::Bot::Radar::VISION_RANGE.first) * 10
|
82
|
+
bots = [mockbot(x, y - 0.01, 'border')]
|
83
|
+
radar.scan(bots)
|
84
|
+
expect(radar.to_a).to have(0).reflections
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe RTanque::Shell do
|
4
|
+
let(:bot) { mockbot(10, 10, 'shooter') }
|
5
|
+
let(:shell) { described_class.new(bot, RTanque::Point.new(10, 10, @arena), RTanque::Heading.new(0), 1) }
|
6
|
+
|
7
|
+
context '#hits' do
|
8
|
+
it 'should yield no bots' do
|
9
|
+
bots = []
|
10
|
+
expect{ |p| shell.hits(bots, &p) }.not_to yield_control
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should yield hit bot' do
|
14
|
+
bots = [mockbot(10, 10, 'deadbot')]
|
15
|
+
expect { |p| shell.hits(bots, &p) }.to yield_with_args(bot, bots[0])
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'shell should be dead after hit' do
|
19
|
+
bots = [mockbot(10, 10, 'deadbot')]
|
20
|
+
expect(shell.dead?).to be_false
|
21
|
+
shell.hits(bots)
|
22
|
+
expect(shell.dead?).to be_true
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should hit shell on bot radius' do
|
26
|
+
bots = [mockbot(10 + RTanque::Bot::RADIUS, 10, 'deadbot')]
|
27
|
+
expect { |p| shell.hits(bots, &p) }.to yield_with_args(bot, bots[0])
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should not hit shell outside bot radius' do
|
31
|
+
bots = [mockbot(10 + RTanque::Bot::RADIUS + 0.01, 10, 'deadbot')]
|
32
|
+
expect { |p| shell.hits(bots, &p) }.not_to yield_with_args(bot, bots[0])
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|