rtanque 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.
- 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
|