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.
Files changed (133) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/.rspec +2 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +10 -0
  7. data/.yardopts +4 -0
  8. data/Gemfile +4 -0
  9. data/Gemfile.ci +6 -0
  10. data/LICENSE.txt +22 -0
  11. data/README.md +168 -0
  12. data/Rakefile +1 -0
  13. data/bin/rtanque +117 -0
  14. data/lib/rtanque.rb +31 -0
  15. data/lib/rtanque/arena.rb +8 -0
  16. data/lib/rtanque/bot.rb +117 -0
  17. data/lib/rtanque/bot/brain.rb +50 -0
  18. data/lib/rtanque/bot/brain_helper.rb +23 -0
  19. data/lib/rtanque/bot/command.rb +23 -0
  20. data/lib/rtanque/bot/radar.rb +54 -0
  21. data/lib/rtanque/bot/sensors.rb +37 -0
  22. data/lib/rtanque/bot/turret.rb +14 -0
  23. data/lib/rtanque/configuration.rb +47 -0
  24. data/lib/rtanque/explosion.rb +23 -0
  25. data/lib/rtanque/gui.rb +25 -0
  26. data/lib/rtanque/gui/bot.rb +71 -0
  27. data/lib/rtanque/gui/bot/health_color_calculator.rb +37 -0
  28. data/lib/rtanque/gui/draw_group.rb +30 -0
  29. data/lib/rtanque/gui/explosion.rb +25 -0
  30. data/lib/rtanque/gui/shell.rb +31 -0
  31. data/lib/rtanque/gui/window.rb +64 -0
  32. data/lib/rtanque/heading.rb +162 -0
  33. data/lib/rtanque/match.rb +73 -0
  34. data/lib/rtanque/match/tick_group.rb +50 -0
  35. data/lib/rtanque/movable.rb +51 -0
  36. data/lib/rtanque/normalized_attr.rb +69 -0
  37. data/lib/rtanque/point.rb +140 -0
  38. data/lib/rtanque/runner.rb +88 -0
  39. data/lib/rtanque/shell.rb +44 -0
  40. data/lib/rtanque/version.rb +3 -0
  41. data/resources/images/body.png +0 -0
  42. data/resources/images/bullet.png +0 -0
  43. data/resources/images/explosions/explosion2-1.png +0 -0
  44. data/resources/images/explosions/explosion2-10.png +0 -0
  45. data/resources/images/explosions/explosion2-11.png +0 -0
  46. data/resources/images/explosions/explosion2-12.png +0 -0
  47. data/resources/images/explosions/explosion2-13.png +0 -0
  48. data/resources/images/explosions/explosion2-14.png +0 -0
  49. data/resources/images/explosions/explosion2-15.png +0 -0
  50. data/resources/images/explosions/explosion2-16.png +0 -0
  51. data/resources/images/explosions/explosion2-17.png +0 -0
  52. data/resources/images/explosions/explosion2-18.png +0 -0
  53. data/resources/images/explosions/explosion2-19.png +0 -0
  54. data/resources/images/explosions/explosion2-2.png +0 -0
  55. data/resources/images/explosions/explosion2-20.png +0 -0
  56. data/resources/images/explosions/explosion2-21.png +0 -0
  57. data/resources/images/explosions/explosion2-22.png +0 -0
  58. data/resources/images/explosions/explosion2-23.png +0 -0
  59. data/resources/images/explosions/explosion2-24.png +0 -0
  60. data/resources/images/explosions/explosion2-25.png +0 -0
  61. data/resources/images/explosions/explosion2-26.png +0 -0
  62. data/resources/images/explosions/explosion2-27.png +0 -0
  63. data/resources/images/explosions/explosion2-28.png +0 -0
  64. data/resources/images/explosions/explosion2-29.png +0 -0
  65. data/resources/images/explosions/explosion2-3.png +0 -0
  66. data/resources/images/explosions/explosion2-30.png +0 -0
  67. data/resources/images/explosions/explosion2-31.png +0 -0
  68. data/resources/images/explosions/explosion2-32.png +0 -0
  69. data/resources/images/explosions/explosion2-33.png +0 -0
  70. data/resources/images/explosions/explosion2-34.png +0 -0
  71. data/resources/images/explosions/explosion2-35.png +0 -0
  72. data/resources/images/explosions/explosion2-36.png +0 -0
  73. data/resources/images/explosions/explosion2-37.png +0 -0
  74. data/resources/images/explosions/explosion2-38.png +0 -0
  75. data/resources/images/explosions/explosion2-39.png +0 -0
  76. data/resources/images/explosions/explosion2-4.png +0 -0
  77. data/resources/images/explosions/explosion2-40.png +0 -0
  78. data/resources/images/explosions/explosion2-41.png +0 -0
  79. data/resources/images/explosions/explosion2-42.png +0 -0
  80. data/resources/images/explosions/explosion2-43.png +0 -0
  81. data/resources/images/explosions/explosion2-44.png +0 -0
  82. data/resources/images/explosions/explosion2-45.png +0 -0
  83. data/resources/images/explosions/explosion2-46.png +0 -0
  84. data/resources/images/explosions/explosion2-47.png +0 -0
  85. data/resources/images/explosions/explosion2-48.png +0 -0
  86. data/resources/images/explosions/explosion2-49.png +0 -0
  87. data/resources/images/explosions/explosion2-5.png +0 -0
  88. data/resources/images/explosions/explosion2-50.png +0 -0
  89. data/resources/images/explosions/explosion2-51.png +0 -0
  90. data/resources/images/explosions/explosion2-52.png +0 -0
  91. data/resources/images/explosions/explosion2-53.png +0 -0
  92. data/resources/images/explosions/explosion2-54.png +0 -0
  93. data/resources/images/explosions/explosion2-55.png +0 -0
  94. data/resources/images/explosions/explosion2-56.png +0 -0
  95. data/resources/images/explosions/explosion2-57.png +0 -0
  96. data/resources/images/explosions/explosion2-58.png +0 -0
  97. data/resources/images/explosions/explosion2-59.png +0 -0
  98. data/resources/images/explosions/explosion2-6.png +0 -0
  99. data/resources/images/explosions/explosion2-60.png +0 -0
  100. data/resources/images/explosions/explosion2-61.png +0 -0
  101. data/resources/images/explosions/explosion2-62.png +0 -0
  102. data/resources/images/explosions/explosion2-63.png +0 -0
  103. data/resources/images/explosions/explosion2-64.png +0 -0
  104. data/resources/images/explosions/explosion2-65.png +0 -0
  105. data/resources/images/explosions/explosion2-66.png +0 -0
  106. data/resources/images/explosions/explosion2-67.png +0 -0
  107. data/resources/images/explosions/explosion2-68.png +0 -0
  108. data/resources/images/explosions/explosion2-69.png +0 -0
  109. data/resources/images/explosions/explosion2-7.png +0 -0
  110. data/resources/images/explosions/explosion2-70.png +0 -0
  111. data/resources/images/explosions/explosion2-71.png +0 -0
  112. data/resources/images/explosions/explosion2-8.png +0 -0
  113. data/resources/images/explosions/explosion2-9.png +0 -0
  114. data/resources/images/grass.png +0 -0
  115. data/resources/images/radar.png +0 -0
  116. data/resources/images/turret.png +0 -0
  117. data/rtanque.gemspec +34 -0
  118. data/sample_bots/camper.rb +79 -0
  119. data/sample_bots/keyboard.rb +50 -0
  120. data/sample_bots/seek_and_destroy.rb +53 -0
  121. data/screenshots/battle_1.png +0 -0
  122. data/screenshots/battle_2.png +0 -0
  123. data/spec/rtanque/bot_spec.rb +239 -0
  124. data/spec/rtanque/heading_spec.rb +279 -0
  125. data/spec/rtanque/match_spec.rb +46 -0
  126. data/spec/rtanque/normalized_attr_spec.rb +90 -0
  127. data/spec/rtanque/point_spec.rb +196 -0
  128. data/spec/rtanque/radar_spec.rb +87 -0
  129. data/spec/rtanque/shell_spec.rb +35 -0
  130. data/spec/rtanque_spec.rb +34 -0
  131. data/spec/spec_helper.rb +51 -0
  132. data/templates/bot.erb +17 -0
  133. 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