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/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
- def initialize (objects = [], environmental_factors = [])
34
- self.objects, self.environmental_factors = objects, environmental_factors
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 EnvironmentalFactor is allowed to act on each object.
41
- #Finally, each GameObject with an act() method is allowed to act on the environment.
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
- object.act(self)
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
- def initialize (name = nil, location = Location.new, color = Color.new, vector = Vector.new, age = 0, size = 1, tags = [])
119
- self.name, self.location, self.color, self.vector, self.age, self.size, self.tags = name, location, color, vector, age, size, tags
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
- def initialize (name = nil, location = Location.new, color = Color.new, vector = Vector.new, age = 0, size = 1, tags = [], behaviors = [])
173
- super(name, location, color, vector, age, size, tags)
174
- self.behaviors = behaviors
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
- #Call Behavior.perform(self, environment) on each of the creature's assigned Behaviors.
187
- def act(environment)
188
- behaviors.each {|behavior| behavior.perform(self, environment)}
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
- #Condition subclasses must implement a met?(actor, target) instance method.
243
- def met?(actor, target)
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
- #Optionally takes an array of actions and one of conditions.
262
- def initialize (actions = [], conditions = [])
263
- self.actions, self.conditions = actions, conditions
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
- #Test all conditions against each object in the evironment.
281
- #For the first object that meets all of them, mark it active (and operate on it first next time).
282
- #Then call start() (if applicable) and perform() for all actions against the active target.
283
- #If any action or condition returns false, stop all actions, and deselect the active target.
284
- def perform(actor, environment)
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
- begin
287
- #Select a target.
288
- target = select_target(actor, environment.objects)
289
- #Do the actions on the target.
290
- actions.each do |action|
291
- action.start(actor, target) unless action.started
292
- action.do(actor, target)
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, #Age.
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, target)
170
+ def do(actor, targets)
176
171
  #Log the interaction.
177
- @interactions << "#{actor.name} targeting #{target.name}"
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(target)
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 met?(actor, target)
235
- return true if actor.name == '1' and target.name == '2'
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("", Location.new(0, 0), Color.new, Vector.new, 0, 0.196), #Radius = 0.25
490
- GameObject.new("", Location.new(1, 0), Color.new, Vector.new, 0, 0.196)
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("", Location.new(0, 0), Color.new, Vector.new, 0, 0.785), #Radius = 0.5
495
- GameObject.new("", Location.new(1, 0), Color.new, Vector.new, 0, 0.785)
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("", Location.new(0, 0), Color.new, Vector.new, 0, 1.766), #Radius = 0.75
500
- GameObject.new("", Location.new(1, 0), Color.new, Vector.new, 0, 1.766)
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("", Location.new(0, 0)),
505
- GameObject.new("", Location.new(0, 0))
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
- #Always true.
487
+ #True for all targets.
515
488
  class TrueCondition < Condition
516
- def met?(actor, target)
517
- true
489
+ def select(actor, targets)
490
+ targets #Select all targets.
518
491
  end
519
492
  end
520
- #Always false.
493
+ #False for all targets.
521
494
  class FalseCondition < Condition
522
- def met?(actor, target)
523
- false
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
- @environment = Environment.new
533
- @environment.objects << @actor << @target << @other
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, target)
516
+ def start(actor, targets)
544
517
  super
545
518
  @start_count += 1
546
519
  end
547
- def do(actor, target)
520
+ def do(actor, targets)
548
521
  @do_count += 1
549
522
  end
550
- def stop(actor, target)
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, @environment)
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, @environment)
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, @environment)
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
@@ -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
- #Turn 1 degree per second.
81
- add_action(TurnAction.new(1), @actor)
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 should be turned 0.1 degrees.
84
- assert_in_delta(0.1, @actor.vector.pitch, REQUIRED_ACCURACY)
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 a 0-degree vector, turn rate of 40 degrees/sec.
93
+ #Create an ApproachAction with 1 unit/sec thrust.
92
94
  @actor.vector = Vector.new(0, 0)
93
- action = ApproachAction.new(40, Vector.new(1, 0))
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(40, Vector.new(1, 0))
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 fleeing directly away from target.
155
- assert_in_delta(1.0, action.heading.speed, REQUIRED_ACCURACY)
156
- assert_in_delta(225, action.heading.pitch, REQUIRED_ACCURACY)
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 target is removed from environment.
124
+ #Verify targets are removed from environment.
171
125
  assert(! @environment.objects.include?(@target1))
172
- #Verify non-target is removed from environment.
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 target is removed from environment.
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(2, @actor.size, REQUIRED_ACCURACY)
143
+ assert_in_delta(4, @actor.size, REQUIRED_ACCURACY)
193
144
  end
194
145
 
195
146