zyps 0.6.3 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.txt +11 -27
- data/bin/zyps +355 -296
- data/bin/zyps_demo +267 -249
- data/lib/zyps/actions.rb +104 -139
- data/lib/zyps/conditions.rb +39 -15
- data/lib/zyps/environmental_factors.rb +60 -48
- data/lib/zyps/views/canvas/gtk2.rb +152 -0
- data/lib/zyps/views/canvas/wx.rb +191 -0
- data/lib/zyps/views/trails.rb +56 -59
- data/lib/zyps.rb +101 -84
- data/test/test_zyps.rb +48 -75
- data/test/zyps/test_actions.rb +24 -73
- data/test/zyps/test_conditions.rb +30 -11
- data/test/zyps/test_environmental_factors.rb +28 -22
- data/test/zyps/test_remote.rb +3 -3
- metadata +16 -8
- data/bin/zyps_server +0 -235
data/lib/zyps.rb
CHANGED
@@ -30,15 +30,34 @@ class Environment
|
|
30
30
|
#An array of EnvironmentalFactor objects that act on any GameObject in the Environment.
|
31
31
|
attr_accessor :environmental_factors
|
32
32
|
|
33
|
-
|
34
|
-
|
33
|
+
#Takes a hash with these keys and defaults:
|
34
|
+
# :objects => [],
|
35
|
+
# :environmental_factors => []
|
36
|
+
def initialize (options = {})
|
37
|
+
options = {
|
38
|
+
:objects => [],
|
39
|
+
:environmental_factors => []
|
40
|
+
}.merge(options)
|
41
|
+
self.objects, self.environmental_factors = options[:objects], options[:environmental_factors]
|
35
42
|
@clock = Clock.new
|
36
43
|
end
|
37
44
|
|
45
|
+
#Make a deep copy.
|
46
|
+
def copy
|
47
|
+
copy = self.clone #Currently, we overwrite everything anyway, but we may add some clonable attributes later.
|
48
|
+
#Make a deep copy of all objects.
|
49
|
+
copy.objects = []
|
50
|
+
@objects.each {|object| copy.objects << object.copy}
|
51
|
+
#Make a deep copy of all environmental_factors.
|
52
|
+
copy.environmental_factors = []
|
53
|
+
@environmental_factors.each {|environmental_factor| copy.environmental_factors << environmental_factor.copy}
|
54
|
+
copy
|
55
|
+
end
|
56
|
+
|
38
57
|
#Allow everything in the environment to interact with each other.
|
39
58
|
#Objects are first moved according to their preexisting vectors and the amount of time since the last call.
|
40
|
-
#Then each
|
41
|
-
#Finally, each
|
59
|
+
#Then, each GameObject with an act() method is allowed to act on the environment.
|
60
|
+
#Finally, each EnvironmentalFactor is allowed to act on the Environment.
|
42
61
|
def interact
|
43
62
|
|
44
63
|
#Get time since last interaction.
|
@@ -46,7 +65,6 @@ class Environment
|
|
46
65
|
|
47
66
|
objects.each do |object|
|
48
67
|
|
49
|
-
|
50
68
|
#Move each object according to its vector.
|
51
69
|
begin
|
52
70
|
object.move(elapsed_time)
|
@@ -57,22 +75,11 @@ class Environment
|
|
57
75
|
next
|
58
76
|
end
|
59
77
|
|
60
|
-
#Have all environmental factors interact with each object.
|
61
|
-
environmental_factors.each do |factor|
|
62
|
-
begin
|
63
|
-
factor.act(object)
|
64
|
-
#Remove misbehaving environmental factors.
|
65
|
-
rescue Exception => exception
|
66
|
-
environmental_factors.delete(factor)
|
67
|
-
puts exception, exception.backtrace
|
68
|
-
next
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
78
|
#Have all creatures interact with the environment.
|
73
79
|
if object.respond_to?(:act)
|
74
80
|
begin
|
75
|
-
|
81
|
+
#Have creature act on all GameObjects other than itself.
|
82
|
+
object.act(objects.reject{|target| target.equal?(object)})
|
76
83
|
#Remove misbehaving objects.
|
77
84
|
rescue Exception => exception
|
78
85
|
puts exception, exception.backtrace
|
@@ -84,6 +91,18 @@ class Environment
|
|
84
91
|
|
85
92
|
end
|
86
93
|
|
94
|
+
#Have all environmental factors interact with environment.
|
95
|
+
environmental_factors.each do |factor|
|
96
|
+
begin
|
97
|
+
factor.act(self)
|
98
|
+
#Remove misbehaving environmental factors.
|
99
|
+
rescue Exception => exception
|
100
|
+
environmental_factors.delete(factor)
|
101
|
+
puts exception, exception.backtrace
|
102
|
+
next
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
87
106
|
#Mark environment as changed.
|
88
107
|
changed
|
89
108
|
|
@@ -115,8 +134,25 @@ class GameObject
|
|
115
134
|
#An array of Strings with tags that determine how the object will be treated by Creature and EnvironmentalFactor objects in its environment.
|
116
135
|
attr_accessor :tags
|
117
136
|
|
118
|
-
|
119
|
-
|
137
|
+
#Takes a hash with these keys and defaults:
|
138
|
+
# :name => nil,
|
139
|
+
# :location => Location.new,
|
140
|
+
# :color => Color.new,
|
141
|
+
# :vector => Vector.new,
|
142
|
+
# :age => 0,
|
143
|
+
# :size => 1,
|
144
|
+
# :tags => []
|
145
|
+
def initialize (options = {})
|
146
|
+
options = {
|
147
|
+
:name => nil,
|
148
|
+
:location => Location.new,
|
149
|
+
:color => Color.new,
|
150
|
+
:vector => Vector.new,
|
151
|
+
:age => 0,
|
152
|
+
:size => 1,
|
153
|
+
:tags => []
|
154
|
+
}.merge(options)
|
155
|
+
self.name, self.location, self.color, self.vector, self.age, self.size, self.tags = options[:name], options[:location], options[:color], options[:vector], options[:age], options[:size], options[:tags]
|
120
156
|
@identifier = generate_identifier
|
121
157
|
end
|
122
158
|
|
@@ -169,9 +205,21 @@ class Creature < GameObject
|
|
169
205
|
attr_accessor :behaviors
|
170
206
|
|
171
207
|
#Identical to the GameObject constructor, except that it also takes a list of Behavior objects.
|
172
|
-
|
173
|
-
|
174
|
-
|
208
|
+
#Takes a hash with these keys and defaults:
|
209
|
+
# :name => nil
|
210
|
+
# :location => Location.new
|
211
|
+
# :color => Color.new
|
212
|
+
# :vector => Vector.new
|
213
|
+
# :age => 0
|
214
|
+
# :size => 1
|
215
|
+
# :tags => []
|
216
|
+
# :behaviors => []
|
217
|
+
def initialize (options = {})
|
218
|
+
options = {
|
219
|
+
:behaviors => []
|
220
|
+
}.merge(options)
|
221
|
+
super
|
222
|
+
self.behaviors = options[:behaviors]
|
175
223
|
end
|
176
224
|
|
177
225
|
#Make a deep copy.
|
@@ -183,9 +231,9 @@ class Creature < GameObject
|
|
183
231
|
copy
|
184
232
|
end
|
185
233
|
|
186
|
-
#
|
187
|
-
def act(
|
188
|
-
behaviors.each {|behavior| behavior.perform(self,
|
234
|
+
#Performs all assigned behaviors on the targets.
|
235
|
+
def act(targets)
|
236
|
+
behaviors.each {|behavior| behavior.perform(self, targets)}
|
189
237
|
end
|
190
238
|
|
191
239
|
end
|
@@ -218,7 +266,6 @@ class Action
|
|
218
266
|
@started = true
|
219
267
|
end
|
220
268
|
|
221
|
-
#Action subclasses must implement a do(actor, target) instance method.
|
222
269
|
def do(actor, target)
|
223
270
|
raise NotImplementedError.new("Action subclasses must implement a do(actor, target) instance method.")
|
224
271
|
end
|
@@ -229,6 +276,9 @@ class Action
|
|
229
276
|
@started = false
|
230
277
|
end
|
231
278
|
|
279
|
+
#Synonym for started
|
280
|
+
def started?; started; end
|
281
|
+
|
232
282
|
end
|
233
283
|
|
234
284
|
|
@@ -239,9 +289,8 @@ class Condition
|
|
239
289
|
#Make a deep copy.
|
240
290
|
def copy; self.clone; end
|
241
291
|
|
242
|
-
|
243
|
-
|
244
|
-
raise NotImplementedError.new("Condition subclasses must implement a met?(actor, target) instance method.")
|
292
|
+
def select(actor, targets)
|
293
|
+
raise NotImplementedError.new("Condition subclasses must implement a select(actor, target) instance method.")
|
245
294
|
end
|
246
295
|
|
247
296
|
end
|
@@ -253,14 +302,18 @@ end
|
|
253
302
|
#Likewise, the subject can change its own attributes, it can approach or flee from the target, it can spawn new Creatures or GameObjects (like bullets), or anything else.
|
254
303
|
class Behavior
|
255
304
|
|
256
|
-
#A list of Condition objects, which are called with the object itself and its target. A condition can consider the tags on the target, the distance from the subject, or any other criteria. If any condition returns false, the behavior will not be carried out (or stopped if it has begun).
|
257
305
|
attr_accessor :conditions
|
258
|
-
#A list of Action objects, which are called with the object and its target when all conditions are met. An action can act on the subject or its target.
|
259
306
|
attr_accessor :actions
|
260
307
|
|
261
|
-
#
|
262
|
-
|
263
|
-
|
308
|
+
#Takes a hash with these keys and defaults:
|
309
|
+
# :actions => []
|
310
|
+
# :conditions => []
|
311
|
+
def initialize (options = {})
|
312
|
+
options = {
|
313
|
+
:actions => [],
|
314
|
+
:conditions => []
|
315
|
+
}.merge(options)
|
316
|
+
self.actions, self.conditions = options[:actions], options[:conditions]
|
264
317
|
#Tracks current target.
|
265
318
|
@active_target = nil
|
266
319
|
end
|
@@ -277,59 +330,25 @@ class Behavior
|
|
277
330
|
copy
|
278
331
|
end
|
279
332
|
|
280
|
-
#
|
281
|
-
#
|
282
|
-
#Then
|
283
|
-
#If
|
284
|
-
def perform(actor,
|
333
|
+
#Finds targets that meet all conditions, then acts on them.
|
334
|
+
#Calls select(actor, targets) on each Condition, each time discarding targets that fail.
|
335
|
+
#Then on each Action, calls Action#start(actor, targets) (if not already started) followed by Action#do(actor, targets).
|
336
|
+
#If no matching targets are found, calls Action#stop(actor, targets) on each Action.
|
337
|
+
def perform(actor, targets)
|
285
338
|
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
action.
|
292
|
-
|
339
|
+
choices = targets.clone
|
340
|
+
conditions.each {|condition| choices = condition.select(actor, choices)}
|
341
|
+
actions.each do |action|
|
342
|
+
if ! choices.empty?
|
343
|
+
action.start(actor, choices) unless action.started?
|
344
|
+
action.do(actor, choices)
|
345
|
+
else
|
346
|
+
action.stop(actor, targets) #Not choices; that array is empty.
|
293
347
|
end
|
294
|
-
rescue NoMatchException => exception
|
295
|
-
#If the behavior can no longer be performed, halt it.
|
296
|
-
stop(actor, target)
|
297
348
|
end
|
298
349
|
|
299
350
|
end
|
300
351
|
|
301
|
-
|
302
|
-
private
|
303
|
-
|
304
|
-
#Stop all actions and de-select the active target.
|
305
|
-
def stop(actor, target)
|
306
|
-
actions.each do |action|
|
307
|
-
action.stop(actor, target) if action.started
|
308
|
-
end
|
309
|
-
@active_target = nil
|
310
|
-
end
|
311
|
-
|
312
|
-
#Select a target that matches all conditions.
|
313
|
-
def select_target(actor, targets)
|
314
|
-
#If a target is already active, still present in the environment, and all conditions are true for it, simply re-select it.
|
315
|
-
if @active_target and targets.include?(@active_target) and conditions.all?{|condition| condition.met?(actor, @active_target)}
|
316
|
-
return @active_target
|
317
|
-
end
|
318
|
-
#For each object in environment:
|
319
|
-
targets.each do |target|
|
320
|
-
#Don't let actor target itself.
|
321
|
-
next if target == actor
|
322
|
-
#If all conditions match (or there are no conditions), select the object.
|
323
|
-
if conditions.all?{|condition| condition.met?(actor, target)}
|
324
|
-
@active_target = target
|
325
|
-
return target
|
326
|
-
end
|
327
|
-
end
|
328
|
-
#If there were no matches, throw an exception.
|
329
|
-
raise NoMatchException, "No matching targets found."
|
330
|
-
end
|
331
|
-
|
332
|
-
|
333
352
|
end
|
334
353
|
|
335
354
|
|
@@ -546,6 +565,4 @@ module Utility
|
|
546
565
|
end
|
547
566
|
|
548
567
|
|
549
|
-
class NoMatchException < RuntimeError; end
|
550
|
-
|
551
568
|
end #module Zyps
|
data/test/test_zyps.rb
CHANGED
@@ -17,6 +17,8 @@
|
|
17
17
|
|
18
18
|
|
19
19
|
require 'zyps'
|
20
|
+
require 'zyps/actions'
|
21
|
+
require 'zyps/conditions'
|
20
22
|
require 'test/unit'
|
21
23
|
|
22
24
|
|
@@ -34,14 +36,7 @@ class TestGameObject < Test::Unit::TestCase
|
|
34
36
|
|
35
37
|
def test_constraints
|
36
38
|
#Test at initialization.
|
37
|
-
object = GameObject.new(
|
38
|
-
"", #Name.
|
39
|
-
Location.new,
|
40
|
-
Color.new,
|
41
|
-
Vector.new,
|
42
|
-
0,
|
43
|
-
-1 #Size.
|
44
|
-
)
|
39
|
+
object = GameObject.new(:size => -1)
|
45
40
|
assert_equal(0, object.size)
|
46
41
|
#Test accessors.
|
47
42
|
object = GameObject.new
|
@@ -112,14 +107,14 @@ class TestCreature < Test::Unit::TestCase
|
|
112
107
|
def test_explicit_initialization
|
113
108
|
behavior = Behavior.new
|
114
109
|
creature = Creature.new(
|
115
|
-
"name",
|
116
|
-
Location.new(10, -3),
|
117
|
-
Color.new(0.5, 0.6, 0.7),
|
118
|
-
Vector.new(1.5, 225),
|
119
|
-
2.001,
|
120
|
-
5.0, #Size.
|
121
|
-
["predator", "blue team"],
|
122
|
-
[behavior]
|
110
|
+
:name => "name",
|
111
|
+
:location => Location.new(10, -3),
|
112
|
+
:color => Color.new(0.5, 0.6, 0.7),
|
113
|
+
:vector => Vector.new(1.5, 225),
|
114
|
+
:age => 2.001,
|
115
|
+
:size => 5.0, #Size.
|
116
|
+
:tags => ["predator", "blue team"],
|
117
|
+
:behaviors => [behavior]
|
123
118
|
)
|
124
119
|
assert_equal("name", creature.name)
|
125
120
|
assert_equal(10, creature.location.x)
|
@@ -158,9 +153,9 @@ class TestEnvironment < Test::Unit::TestCase
|
|
158
153
|
|
159
154
|
#Create an environment and add creatures.
|
160
155
|
@environment = Environment.new
|
161
|
-
@environment.objects << Creature.new('1')
|
162
|
-
@environment.objects << Creature.new('2')
|
163
|
-
@environment.objects << Creature.new('3')
|
156
|
+
@environment.objects << Creature.new(:name => '1')
|
157
|
+
@environment.objects << Creature.new(:name => '2')
|
158
|
+
@environment.objects << Creature.new(:name => '3')
|
164
159
|
|
165
160
|
end
|
166
161
|
|
@@ -172,9 +167,11 @@ class TestEnvironment < Test::Unit::TestCase
|
|
172
167
|
#Interactions will be logged here.
|
173
168
|
@interactions = []
|
174
169
|
end
|
175
|
-
def do(actor,
|
170
|
+
def do(actor, targets)
|
176
171
|
#Log the interaction.
|
177
|
-
|
172
|
+
targets.each do |target|
|
173
|
+
@interactions << "#{actor.name} targeting #{target.name}"
|
174
|
+
end
|
178
175
|
end
|
179
176
|
end
|
180
177
|
|
@@ -207,9 +204,9 @@ class TestEnvironment < Test::Unit::TestCase
|
|
207
204
|
#Interactions will be logged here.
|
208
205
|
@interactions = []
|
209
206
|
end
|
210
|
-
def act(
|
207
|
+
def act(environment)
|
211
208
|
#Log the interaction.
|
212
|
-
@interactions << "Environment targeting #{target.name}"
|
209
|
+
environment.objects.each {|target| @interactions << "Environment targeting #{target.name}"}
|
213
210
|
end
|
214
211
|
end
|
215
212
|
|
@@ -231,8 +228,8 @@ class TestEnvironment < Test::Unit::TestCase
|
|
231
228
|
|
232
229
|
#A condition that is false unless actor and target have specific names.
|
233
230
|
class NameCondition < Condition
|
234
|
-
def
|
235
|
-
|
231
|
+
def select(actor, targets)
|
232
|
+
targets.find_all {|target| actor.name == '1' and target.name == '2'}
|
236
233
|
end
|
237
234
|
end
|
238
235
|
|
@@ -255,30 +252,6 @@ class TestEnvironment < Test::Unit::TestCase
|
|
255
252
|
end
|
256
253
|
|
257
254
|
|
258
|
-
#Test that creatures can switch targets when one is removed from the environment.
|
259
|
-
def test_object_removal
|
260
|
-
|
261
|
-
#Set up behaviors that will log interactions.
|
262
|
-
log = LogAction.new
|
263
|
-
@environment.objects.each do |creature|
|
264
|
-
behavior = Behavior.new
|
265
|
-
behavior.actions << log
|
266
|
-
creature.behaviors << behavior
|
267
|
-
end
|
268
|
-
|
269
|
-
#Have environment elements interact.
|
270
|
-
@environment.interact
|
271
|
-
#Remove the original target from the environment.
|
272
|
-
@environment.objects.delete_at(1)
|
273
|
-
#Interact again.
|
274
|
-
@environment.interact
|
275
|
-
|
276
|
-
#Look for expected interactions (each should only occur once).
|
277
|
-
assert_equal(1, log.interactions.find_all{|i| i == "1 targeting 3"}.length)
|
278
|
-
|
279
|
-
end
|
280
|
-
|
281
|
-
|
282
255
|
end
|
283
256
|
|
284
257
|
|
@@ -486,23 +459,23 @@ class TestUtility < Test::Unit::TestCase
|
|
486
459
|
def test_collided?
|
487
460
|
#Objects apart.
|
488
461
|
assert(! Utility.collided?(
|
489
|
-
GameObject.new(
|
490
|
-
GameObject.new(
|
462
|
+
GameObject.new(:location => Location.new(0, 0), :size =>0.196), #Radius = 0.25
|
463
|
+
GameObject.new(:location => Location.new(1, 0), :size =>0.196)
|
491
464
|
))
|
492
465
|
#Objects touching (not a collision).
|
493
466
|
assert(! Utility.collided?(
|
494
|
-
GameObject.new(
|
495
|
-
GameObject.new(
|
467
|
+
GameObject.new(:location => Location.new(0, 0), :size =>0.785), #Radius = 0.5
|
468
|
+
GameObject.new(:location => Location.new(1, 0), :size =>0.785)
|
496
469
|
))
|
497
470
|
#Objects collided.
|
498
471
|
assert(Utility.collided?(
|
499
|
-
GameObject.new(
|
500
|
-
GameObject.new(
|
472
|
+
GameObject.new(:location => Location.new(0, 0), :size =>1.766), #Radius = 0.75
|
473
|
+
GameObject.new(:location => Location.new(1, 0), :size =>1.766)
|
501
474
|
))
|
502
475
|
#Objects in same place.
|
503
476
|
assert(Utility.collided?(
|
504
|
-
GameObject.new(
|
505
|
-
GameObject.new(
|
477
|
+
GameObject.new(:location => Location.new(0, 0)),
|
478
|
+
GameObject.new(:location => Location.new(0, 0))
|
506
479
|
))
|
507
480
|
end
|
508
481
|
|
@@ -511,26 +484,26 @@ end
|
|
511
484
|
|
512
485
|
class TestBehavior < Test::Unit::TestCase
|
513
486
|
|
514
|
-
#
|
487
|
+
#True for all targets.
|
515
488
|
class TrueCondition < Condition
|
516
|
-
def
|
517
|
-
|
489
|
+
def select(actor, targets)
|
490
|
+
targets #Select all targets.
|
518
491
|
end
|
519
492
|
end
|
520
|
-
#
|
493
|
+
#False for all targets.
|
521
494
|
class FalseCondition < Condition
|
522
|
-
def
|
523
|
-
|
495
|
+
def select(actor, targets)
|
496
|
+
[] #Select no targets.
|
524
497
|
end
|
525
498
|
end
|
526
499
|
|
527
500
|
|
528
501
|
def setup
|
529
|
-
@actor = Creature.new('actor')
|
530
|
-
@target = Creature.new('target')
|
531
|
-
@other = Creature.new('other')
|
532
|
-
@
|
533
|
-
@
|
502
|
+
@actor = Creature.new(:name => 'actor')
|
503
|
+
@target = Creature.new(:name => 'target')
|
504
|
+
@other = Creature.new(:name => 'other')
|
505
|
+
@targets = []
|
506
|
+
@targets << @target << @other
|
534
507
|
end
|
535
508
|
|
536
509
|
|
@@ -540,14 +513,14 @@ class TestBehavior < Test::Unit::TestCase
|
|
540
513
|
def initialize
|
541
514
|
@start_count, @stop_count, @do_count = 0, 0, 0
|
542
515
|
end
|
543
|
-
def start(actor,
|
516
|
+
def start(actor, targets)
|
544
517
|
super
|
545
518
|
@start_count += 1
|
546
519
|
end
|
547
|
-
def do(actor,
|
520
|
+
def do(actor, targets)
|
548
521
|
@do_count += 1
|
549
522
|
end
|
550
|
-
def stop(actor,
|
523
|
+
def stop(actor, targets)
|
551
524
|
super
|
552
525
|
@stop_count += 1
|
553
526
|
end
|
@@ -562,13 +535,13 @@ class TestBehavior < Test::Unit::TestCase
|
|
562
535
|
behavior.actions << action
|
563
536
|
|
564
537
|
#Perform the behavior.
|
565
|
-
behavior.perform(@actor, @
|
538
|
+
behavior.perform(@actor, @targets)
|
566
539
|
assert_equal(1, action.start_count, "start() should have been called on the mock action.")
|
567
540
|
assert_equal(1, action.do_count, "do() should have been called.")
|
568
541
|
assert_equal(0, action.stop_count, "stop() should NOT have been called.")
|
569
542
|
|
570
543
|
#Perform the behavior again.
|
571
|
-
behavior.perform(@actor, @
|
544
|
+
behavior.perform(@actor, @targets)
|
572
545
|
assert_equal(1, action.start_count, "start() should NOT have been called.")
|
573
546
|
assert_equal(2, action.do_count, "do() should have been called.")
|
574
547
|
assert_equal(0, action.stop_count, "stop() should NOT have been called.")
|
@@ -576,7 +549,7 @@ class TestBehavior < Test::Unit::TestCase
|
|
576
549
|
#Add a false condition to the behavior.
|
577
550
|
behavior.conditions << FalseCondition.new
|
578
551
|
#Perform the behavior.
|
579
|
-
behavior.perform(@actor, @
|
552
|
+
behavior.perform(@actor, @targets)
|
580
553
|
assert_equal(1, action.start_count, "start() should NOT have been called.")
|
581
554
|
assert_equal(2, action.do_count, "do() should NOT have been called, because conditions are no longer true.")
|
582
555
|
assert_equal(1, action.stop_count, "stop() SHOULD have been called.")
|
@@ -597,4 +570,4 @@ class TestBehavior < Test::Unit::TestCase
|
|
597
570
|
end
|
598
571
|
|
599
572
|
|
600
|
-
end
|
573
|
+
end
|
data/test/zyps/test_actions.rb
CHANGED
@@ -40,9 +40,9 @@ class TestActions < Test::Unit::TestCase
|
|
40
40
|
|
41
41
|
#Create and populate an environment.
|
42
42
|
def setup
|
43
|
-
@actor = Creature.new('actor', Location.new(0, 0))
|
44
|
-
@target1 = Creature.new('target1', Location.new(1, 1))
|
45
|
-
@target2 = Creature.new('target2', Location.new(-2, -2))
|
43
|
+
@actor = Creature.new(:name => 'actor', :location => Location.new(0, 0))
|
44
|
+
@target1 = Creature.new(:name => 'target1', :location => Location.new(1, 1))
|
45
|
+
@target2 = Creature.new(:name => 'target2', :location => Location.new(-2, -2))
|
46
46
|
#Create an environment, and add the objects.
|
47
47
|
@environment = Environment.new
|
48
48
|
#Order is important - we want to act on target 1 first.
|
@@ -77,104 +77,53 @@ class TestActions < Test::Unit::TestCase
|
|
77
77
|
|
78
78
|
#A TurnAction turns the actor at a given rate.
|
79
79
|
def test_turn_action
|
80
|
-
|
81
|
-
|
80
|
+
@actor.vector = Vector.new(1, 0)
|
81
|
+
#Turn 45 degrees off-heading at 1 unit/second.
|
82
|
+
add_action(TurnAction.new(1, 45), @actor)
|
82
83
|
@environment.interact
|
83
|
-
#Clock always returns 0.1 seconds, so actor
|
84
|
-
assert_in_delta(
|
84
|
+
#Clock always returns 0.1 seconds, so ensure actor's vector is adjusted accordingly.
|
85
|
+
assert_in_delta(3.778, @actor.vector.pitch, REQUIRED_ACCURACY)
|
86
|
+
assert_in_delta(1.073, @actor.vector.speed, REQUIRED_ACCURACY)
|
85
87
|
end
|
86
88
|
|
87
89
|
|
88
90
|
#An ApproachAction pushes the actor toward the target.
|
89
91
|
def test_approach_action
|
90
92
|
|
91
|
-
#Create an ApproachAction with
|
93
|
+
#Create an ApproachAction with 1 unit/sec thrust.
|
92
94
|
@actor.vector = Vector.new(0, 0)
|
93
|
-
|
94
|
-
add_action(action, @actor)
|
95
|
-
#Act.
|
96
|
-
@environment.interact
|
97
|
-
#Ensure action's thrust vector is correct for 0.1 seconds.
|
98
|
-
assert_in_delta(1.0, action.heading.speed, REQUIRED_ACCURACY)
|
99
|
-
assert_in_delta(4.0, action.heading.pitch, REQUIRED_ACCURACY)
|
100
|
-
#Ensure actor's resulting vector is correct.
|
101
|
-
assert_in_delta(0.997, @actor.vector.x, REQUIRED_ACCURACY)
|
102
|
-
assert_in_delta(0.069, @actor.vector.y, REQUIRED_ACCURACY)
|
103
|
-
|
104
|
-
end
|
105
|
-
|
106
|
-
#Ensure ApproachAction doesn't oversteer.
|
107
|
-
def test_approach_action_accuracy
|
108
|
-
|
109
|
-
#Create an ApproachAction with a 0-degree vector, turn rate high enough to turn more than 45 degrees in 0.1 seconds.
|
110
|
-
#Action should only turn as far as target.
|
111
|
-
@actor.vector = Vector.new(0, 0)
|
112
|
-
action = ApproachAction.new(500, Vector.new(1, 0))
|
113
|
-
add_action(action, @actor)
|
95
|
+
add_action(ApproachAction.new(1), @actor)
|
114
96
|
#Act.
|
115
97
|
@environment.interact
|
98
|
+
#Ensure actor's vector is correct after action's thrust is applied for 0.1 seconds.
|
99
|
+
assert_in_delta(0.1, @actor.vector.speed, REQUIRED_ACCURACY)
|
100
|
+
assert_in_delta(45, @actor.vector.pitch, REQUIRED_ACCURACY)
|
116
101
|
|
117
|
-
#Ensure actor is approaching target directly.
|
118
|
-
assert_in_delta(1.0, action.heading.speed, REQUIRED_ACCURACY)
|
119
|
-
assert_in_delta(45, action.heading.pitch, REQUIRED_ACCURACY)
|
120
|
-
#Ensure actor's resulting vector is correct.
|
121
|
-
assert_in_delta(0.707, @actor.vector.x, REQUIRED_ACCURACY)
|
122
|
-
assert_in_delta(0.707, @actor.vector.y, REQUIRED_ACCURACY)
|
123
|
-
|
124
102
|
end
|
125
103
|
|
126
|
-
|
127
104
|
#A FleeAction pushes the actor away from a target.
|
128
105
|
def test_flee_action
|
129
106
|
|
130
107
|
#Create a FleeAction with a 0-degree vector, turn rate of 40 degrees/sec.
|
131
108
|
@actor.vector = Vector.new(0, 0)
|
132
|
-
action = FleeAction.new(
|
133
|
-
add_action(action, @actor)
|
134
|
-
#Act.
|
135
|
-
@environment.interact
|
136
|
-
#Ensure action's thrust vector is correct for 0.1 seconds.
|
137
|
-
assert_in_delta(1.0, action.heading.speed, REQUIRED_ACCURACY)
|
138
|
-
assert_in_delta(356.0, action.heading.pitch, REQUIRED_ACCURACY) #Should be heading away.
|
139
|
-
#Ensure actor's resulting vector is correct.
|
140
|
-
assert_in_delta(0.997, @actor.vector.x, REQUIRED_ACCURACY)
|
141
|
-
assert_in_delta(-0.069, @actor.vector.y, REQUIRED_ACCURACY)
|
142
|
-
end
|
143
|
-
|
144
|
-
#Ensure flee action doesn't oversteer.
|
145
|
-
def test_flee_action_accuracy
|
146
|
-
|
147
|
-
#Create a FleeAction with a 0-degree vector, turn rate high enough to turn more than 135 degrees in 0.1 seconds.
|
148
|
-
#Action should turn directly away from target, but no farther.
|
149
|
-
@actor.vector = Vector.new(0, 0)
|
150
|
-
action = FleeAction.new(1400, Vector.new(1, 0))
|
109
|
+
action = FleeAction.new(1)
|
151
110
|
add_action(action, @actor)
|
152
111
|
#Act.
|
153
112
|
@environment.interact
|
154
|
-
#Ensure actor is
|
155
|
-
assert_in_delta(1
|
156
|
-
assert_in_delta(225,
|
157
|
-
#Ensure actor's resulting vector is correct.
|
158
|
-
assert_in_delta(-0.707, @actor.vector.x, REQUIRED_ACCURACY)
|
159
|
-
assert_in_delta(-0.707, @actor.vector.y, REQUIRED_ACCURACY)
|
160
|
-
|
113
|
+
#Ensure actor's resulting vector is correct after 0.1 seconds of thrust.
|
114
|
+
assert_in_delta(0.1, @actor.vector.speed, REQUIRED_ACCURACY)
|
115
|
+
assert_in_delta(225, @actor.vector.pitch, REQUIRED_ACCURACY)
|
161
116
|
end
|
162
117
|
|
163
|
-
|
164
118
|
#A DestroyAction removes the target from the environment.
|
165
119
|
def test_destroy_action
|
166
120
|
#Create a DestroyAction, linked to the environment.
|
167
121
|
add_action(DestroyAction.new(@environment), @actor)
|
168
122
|
#Act.
|
169
123
|
@environment.interact
|
170
|
-
#Verify
|
124
|
+
#Verify targets are removed from environment.
|
171
125
|
assert(! @environment.objects.include?(@target1))
|
172
|
-
|
173
|
-
assert(@environment.objects.include?(@target2))
|
174
|
-
#Act again.
|
175
|
-
@environment.interact
|
176
|
-
#Verify targets were switched.
|
177
|
-
assert(! @environment.objects.include?(@target2), "Targets should have been switched.")
|
126
|
+
assert(! @environment.objects.include?(@target2))
|
178
127
|
end
|
179
128
|
|
180
129
|
|
@@ -185,11 +134,13 @@ class TestActions < Test::Unit::TestCase
|
|
185
134
|
#Act.
|
186
135
|
@actor.size = 1
|
187
136
|
@target1.size = 1
|
137
|
+
@target2.size = 2
|
188
138
|
@environment.interact
|
189
|
-
#Verify
|
139
|
+
#Verify targets are removed from environment.
|
190
140
|
assert(! @environment.objects.include?(@target1))
|
141
|
+
assert(! @environment.objects.include?(@target2))
|
191
142
|
#Verify creature has grown by the appropriate amount.
|
192
|
-
assert_in_delta(
|
143
|
+
assert_in_delta(4, @actor.size, REQUIRED_ACCURACY)
|
193
144
|
end
|
194
145
|
|
195
146
|
|