zyps 0.7.3 → 0.7.4

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/bin/zyps CHANGED
@@ -153,6 +153,9 @@ class Application < Wx::App
153
153
  @log.debug "Set up a creature generator."
154
154
  @generator = CreatureGenerator.new(:environment => @environment)
155
155
 
156
+ @log.debug "Set the Utility module to cache distances and angles."
157
+ Utility.caching_enabled = true
158
+
156
159
  @log.debug "Set up a timer to update the environment."
157
160
  milliseconds_per_frame = (1.0 / @options[:fps] * 1000).to_int
158
161
  @log.debug "Timer will fire every #{milliseconds_per_frame} milliseconds."
@@ -190,6 +193,8 @@ class Application < Wx::App
190
193
  #Copy from View to drawing area.
191
194
  render(dc)
192
195
  end
196
+ #Almost all distances and angles will be different next update, so clear the cache.
197
+ Utility.clear_caches
193
198
  #Keeps dead objects from accumulating and causing hiccups later.
194
199
  GC.start
195
200
  end
@@ -644,7 +649,7 @@ class CreatureGenerator
644
649
  :turn_angle => 90,
645
650
  :breed_rate => 10,
646
651
  }.merge(options)
647
- @log.debug "options: #{@options.inspect}"
652
+ @log.debug "options: #{options.inspect}"
648
653
 
649
654
  @environment = options[:environment]
650
655
  @default_size = options[:default_size]
@@ -692,7 +697,7 @@ class CreatureGenerator
692
697
  :breed => false,
693
698
  :eat => false,
694
699
  }.merge(options)
695
- @log.debug "options: #{@options.inspect}"
700
+ @log.debug "options: #{options.inspect}"
696
701
 
697
702
  #Create a creature.
698
703
  creature = Creature.new(
@@ -707,35 +712,40 @@ class CreatureGenerator
707
712
  color.blue += 1
708
713
  creature.behaviors << Behavior.new(
709
714
  :actions => [TurnAction.new(@turn_rate, @turn_angle)],
710
- :conditions => [ProximityCondition.new(options[:action_proximity] * 2)]
715
+ :conditions => [ProximityCondition.new(options[:action_proximity] * 2)],
716
+ :condition_frequency => 20
711
717
  )
712
718
  end
713
719
  if options[:approach]
714
720
  color.red += 1
715
721
  creature.behaviors << Behavior.new(
716
722
  :actions => [ApproachAction.new(@approach_rate)],
717
- :conditions => [ProximityCondition.new(options[:action_proximity])]
723
+ :conditions => [ProximityCondition.new(options[:action_proximity])],
724
+ :condition_frequency => 10
718
725
  )
719
726
  end
720
727
  if options[:flee]
721
728
  color.red += 0.5; color.green += 0.5 #Yellow.
722
729
  creature.behaviors << Behavior.new(
723
730
  :actions => [FleeAction.new(@flee_rate)],
724
- :conditions => [ProximityCondition.new(options[:action_proximity] * 0.5)]
731
+ :conditions => [ProximityCondition.new(options[:action_proximity] * 0.5)],
732
+ :condition_frequency => 5
725
733
  )
726
734
  end
727
735
  if options[:push]
728
736
  color.red += 0.5; color.blue += 0.5 #Purple.
729
737
  creature.behaviors << Behavior.new(
730
738
  :actions => [PushAction.new(@push_strength)],
731
- :conditions => [ProximityCondition.new(options[:action_proximity] * 0.25)]
739
+ :conditions => [ProximityCondition.new(options[:action_proximity] * 0.25)],
740
+ :condition_frequency => 2
732
741
  )
733
742
  end
734
743
  if options[:pull]
735
744
  color.blue += 0.75; color.green += 0.75 #Aqua.
736
745
  creature.behaviors << Behavior.new(
737
746
  :actions => [PullAction.new(@pull_strength)],
738
- :conditions => [ProximityCondition.new(options[:action_proximity] * 0.75)]
747
+ :conditions => [ProximityCondition.new(options[:action_proximity] * 0.75)],
748
+ :condition_frequency => 7
739
749
  )
740
750
  end
741
751
  if options[:breed]
@@ -743,7 +753,8 @@ class CreatureGenerator
743
753
  color.blue -= 0.1
744
754
  creature.behaviors << Behavior.new(
745
755
  :actions => [BreedAction.new(@environment, @breed_rate)],
746
- :conditions => [CollisionCondition.new] #The default ProximityCondition won't do.
756
+ :conditions => [CollisionCondition.new], #The default ProximityCondition won't do.
757
+ :condition_frequency => 1
747
758
  )
748
759
  end
749
760
  if options[:eat]
@@ -751,15 +762,16 @@ class CreatureGenerator
751
762
  creature.behaviors << Behavior.new(
752
763
  :actions => [EatAction.new(@environment)],
753
764
  :conditions => [
754
- CollisionCondition.new, #The default ProximityCondition won't do.
755
- StrengthCondition.new #The eater should be as strong or stronger than its dinner.
756
- ]
765
+ StrengthCondition.new, #The eater should be as strong or stronger than its dinner.
766
+ CollisionCondition.new #The default ProximityCondition won't do.
767
+ ],
768
+ :condition_frequency => 1
757
769
  )
758
770
  end
759
771
 
760
772
  creature.color = color
761
773
 
762
- @log.debug "Created creature: #{creature.inspect}"
774
+ @log.debug "Created creature: #{creature.to_s}"
763
775
 
764
776
  creature
765
777
 
data/bin/zyps_demo CHANGED
@@ -93,6 +93,12 @@ class Demo < Wx::App
93
93
  @update_count = 0
94
94
  @environment.add_observer(self)
95
95
 
96
+ say "The Utility module calculates distances and angles between objects."
97
+ say "Utility is sometimes asked to compute the same distance or angle over and over."
98
+ say "We can speed this by returning a cached result instead of re-calculating it."
99
+ say "Let's turn this feature on."
100
+ Utility.caching_enabled = true
101
+
96
102
  say "We want to update the environment #{FRAMES_PER_SECOND} times per second."
97
103
  milliseconds_per_frame = (1.0 / FRAMES_PER_SECOND * 1000).to_int
98
104
  say "So, we'll set up a timer to fire every #{milliseconds_per_frame} milliseconds."
@@ -100,6 +106,7 @@ class Demo < Wx::App
100
106
  timer = Wx::Timer.new(self, timer_id)
101
107
  say "The timer will trigger the environment update."
102
108
  say "Then it copies the updated view's buffer to our window."
109
+ say "We'll clear the cached values from the Utility module to save memory."
103
110
  say "We also call the Ruby garbage collector with each update."
104
111
  say "This keeps dead objects from accumulating and causing hiccups later."
105
112
  evt_timer(timer_id) do
@@ -107,6 +114,7 @@ class Demo < Wx::App
107
114
  window.paint do |surface|
108
115
  surface.draw_bitmap(view.canvas.buffer, 0, 0, false)
109
116
  end
117
+ Utility.clear_caches
110
118
  GC.start
111
119
  end
112
120
  timer.start(milliseconds_per_frame)
@@ -67,7 +67,16 @@ end
67
67
  class CollisionCondition < Condition
68
68
  #Returns an array of targets that have collided with the actor.
69
69
  def select(actor, targets)
70
- targets.find_all {|target| Utility.collided?(actor, target)}
70
+ return [] unless targets.length > 0
71
+ #The size of the largest other object
72
+ max_size = targets.map{|t| t.size}.max
73
+ #The maximum distance on a straight line the largest object and self could be and still be touching.
74
+ max_diff = Math.sqrt(actor.size / Math::PI) + Math.sqrt(max_size / Math::PI)
75
+ x_range = (actor.location.x - max_diff .. actor.location.x + max_diff)
76
+ y_range = (actor.location.y - max_diff .. actor.location.y + max_diff)
77
+ targets.select do | target |
78
+ x_range.include?(target.location.x) and y_range.include?(target.location.y) and Utility.collided?(actor, target)
79
+ end
71
80
  end
72
81
  end
73
82
 
data/lib/zyps.rb CHANGED
@@ -302,20 +302,44 @@ end
302
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.
303
303
  class Behavior
304
304
 
305
+ #An array of Condition subclasses.
306
+ #Condition#select(actor, targets) will be called on each.
305
307
  attr_accessor :conditions
308
+ #An array of Action subclasses.
309
+ #Action#start(actor, targets) and action.do(actor, targets) will be called on each when all conditions are true.
310
+ #Action#stop(actor, targets) will be called when any condition is false.
306
311
  attr_accessor :actions
312
+ #Number of updates before behavior is allowed to select a new group of targets to act on.
313
+ attr_accessor :condition_frequency
314
+
315
+ #Will be used to distribute condition processing time between all Behaviors with the same condition_frequency.
316
+ @@condition_order = Hash.new {|h, k| h[k] = 0}
307
317
 
308
318
  #Takes a hash with these keys and defaults:
309
319
  # :actions => []
310
320
  # :conditions => []
321
+ # :condition_frequency => 1
311
322
  def initialize (options = {})
312
323
  options = {
313
324
  :actions => [],
314
- :conditions => []
325
+ :conditions => [],
326
+ :condition_frequency => 1
315
327
  }.merge(options)
316
- self.actions, self.conditions = options[:actions], options[:conditions]
317
- #Tracks current target.
318
- @active_target = nil
328
+ self.actions = options[:actions]
329
+ self.conditions = options[:conditions]
330
+ self.condition_frequency = options[:condition_frequency]
331
+ #Tracks number of calls to perform() so conditions can be evaluated with appropriate frequency.
332
+ @condition_evaluation_count = 0
333
+ #Targets currently selected to act upon.
334
+ @current_targets = []
335
+ end
336
+
337
+ def condition_frequency= (value)
338
+ #Condition frequency must be 1 or more.
339
+ @condition_frequency = (value >= 1 ? value : 1)
340
+ #This will be used to distribute condition evaluation time among all behaviors with this frequency.
341
+ @condition_order = @@condition_order[@condition_frequency]
342
+ @@condition_order[@condition_frequency] += 1
319
343
  end
320
344
 
321
345
  #Make a deep copy.
@@ -336,19 +360,33 @@ class Behavior
336
360
  #If no matching targets are found, calls Action#stop(actor, targets) on each Action.
337
361
  def perform(actor, targets)
338
362
 
339
- choices = targets.clone
340
- conditions.each {|condition| choices = condition.select(actor, choices)}
363
+ if condition_evaluation_turn?
364
+ @current_targets = targets.clone
365
+ conditions.each {|condition| @current_targets = condition.select(actor, @current_targets)}
366
+ end
341
367
  actions.each do |action|
342
- if ! choices.empty?
343
- action.start(actor, choices) unless action.started?
344
- action.do(actor, choices)
368
+ if ! @current_targets.empty?
369
+ action.start(actor, @current_targets) unless action.started?
370
+ action.do(actor, @current_targets)
345
371
  else
346
- action.stop(actor, targets) #Not choices; that array is empty.
372
+ action.stop(actor, targets) #Not @current_targets; that array is empty.
347
373
  end
348
374
  end
349
375
 
376
+
350
377
  end
351
378
 
379
+ private
380
+
381
+ #Return true if it's our turn to choose targets, false otherwise.
382
+ def condition_evaluation_turn?
383
+ #Every condition_frequency turns (plus our turn order within the group), return true.
384
+ our_turn = ((@condition_evaluation_count + @condition_order) % @condition_frequency == 0) ? true : false
385
+ #Track number of calls to perform() for staggering condition evaluation.
386
+ @condition_evaluation_count += 1
387
+ our_turn
388
+ end
389
+
352
390
  end
353
391
 
354
392
 
@@ -499,8 +537,29 @@ module Utility
499
537
 
500
538
  PI2 = Math::PI * 2.0 #:nodoc:
501
539
 
540
+ #Empty cached return values.
541
+ def Utility.clear_caches
542
+ @@angles = Hash.new {|h, k| h[k] = {}}
543
+ @@distances = Hash.new {|h, k| h[k] = {}}
544
+ end
545
+
546
+ #Initialize caches for return values.
547
+ Utility.clear_caches
548
+
549
+ #Turn caching of return values on or off.
550
+ @@caching_enabled = false
551
+ def Utility.caching_enabled= (value)
552
+ @@caching_enabled = value
553
+ Utility.clear_caches if ! @@caching_enabled
554
+ end
555
+
502
556
  #Get the angle (in degrees) from one Location to another.
503
557
  def Utility.find_angle(origin, target)
558
+ if @@caching_enabled
559
+ #Return cached angle if there is one.
560
+ return @@angles[origin][target] if @@angles[origin][target]
561
+ return @@angles[target][origin] if @@angles[target][origin]
562
+ end
504
563
  #Get vector from origin to target.
505
564
  x_difference = target.x - origin.x
506
565
  y_difference = target.y - origin.y
@@ -509,16 +568,37 @@ module Utility
509
568
  #Result will range from negative Pi to Pi, so correct it.
510
569
  radians += PI2 if radians < 0
511
570
  #Convert to degrees.
512
- to_degrees(radians)
571
+ angle = to_degrees(radians)
572
+ #Cache angle if caching enabled.
573
+ if @@caching_enabled
574
+ @@angles[origin][target] = angle
575
+ #angle + 180 = angle from target to origin.
576
+ @@angles[target][origin] = (angle + 180 % 360)
577
+ end
578
+ #Return result.
579
+ angle
513
580
  end
514
581
 
515
582
  #Get the distance from one Location to another.
516
583
  def Utility.find_distance(origin, target)
584
+ if @@caching_enabled
585
+ #Return cached distance if there is one.
586
+ return @@distances[origin][target] if @@distances[origin][target]
587
+ end
517
588
  #Get vector from origin to target.
518
589
  x_difference = origin.x - target.x
519
590
  y_difference = origin.y - target.y
520
591
  #Get distance.
521
- Math.sqrt(x_difference ** 2 + y_difference ** 2)
592
+ distance = Math.sqrt(x_difference ** 2 + y_difference ** 2)
593
+ #Cache distance if caching enabled.
594
+ if @@caching_enabled
595
+ #Origin to target distance = target to origin distance.
596
+ #Cache such that either will be found.
597
+ @@distances[origin][target] = distance
598
+ @@distances[target][origin] = distance
599
+ end
600
+ #Return result.
601
+ distance
522
602
  end
523
603
 
524
604
  #Convert radians to degrees.
data/test/test_zyps.rb CHANGED
@@ -402,6 +402,11 @@ end
402
402
 
403
403
 
404
404
  class TestUtility < Test::Unit::TestCase
405
+
406
+
407
+ def setup
408
+ Utility.caching_enabled = true
409
+ end
405
410
 
406
411
 
407
412
  def test_to_radians
metadata CHANGED
@@ -1,81 +1,88 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.9.2
3
- specification_version: 1
4
2
  name: zyps
5
3
  version: !ruby/object:Gem::Version
6
- version: 0.7.3
7
- date: 2008-02-04 00:00:00 -07:00
8
- summary: A game library for Ruby
9
- require_paths:
10
- - lib
11
- email: jay@mcgavren.com
12
- homepage: http://jay.mcgavren.com/zyps/
13
- rubyforge_project: zyps
14
- description:
15
- autorequire: zyps
16
- default_executable:
17
- bindir: bin
18
- has_rdoc: true
19
- required_ruby_version: !ruby/object:Gem::Version::Requirement
20
- requirements:
21
- - - ">"
22
- - !ruby/object:Gem::Version
23
- version: 0.0.0
24
- version:
4
+ version: 0.7.4
25
5
  platform: ruby
26
- signing_key:
27
- cert_chain:
28
- post_install_message:
29
6
  authors:
30
7
  - Jay McGavren
8
+ autorequire: zyps
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-02-05 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: wxruby
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.9.2
23
+ version:
24
+ description:
25
+ email: jay@mcgavren.com
26
+ executables:
27
+ - zyps
28
+ extensions: []
29
+
30
+ extra_rdoc_files:
31
+ - README.txt
32
+ - COPYING.LESSER.txt
33
+ - COPYING.txt
31
34
  files:
32
35
  - COPYING.LESSER.txt
33
36
  - COPYING.txt
34
- - README.txt
35
37
  - README_windows.txt
38
+ - README.txt
36
39
  - bin/zyps
37
40
  - bin/zyps_demo
38
41
  - lib/zyps
39
- - lib/zyps/actions.rb
40
- - lib/zyps/conditions.rb
41
- - lib/zyps/environmental_factors.rb
42
- - lib/zyps/remote.rb
43
42
  - lib/zyps/views
44
43
  - lib/zyps/views/canvas
45
44
  - lib/zyps/views/canvas/wx.rb
46
45
  - lib/zyps/views/trails.rb
46
+ - lib/zyps/actions.rb
47
+ - lib/zyps/environmental_factors.rb
48
+ - lib/zyps/conditions.rb
49
+ - lib/zyps/remote.rb
47
50
  - lib/zyps.rb
48
- - test/test_zyps.rb
49
51
  - test/zyps
50
- - test/zyps/test_actions.rb
51
52
  - test/zyps/test_behaviors.rb
52
- - test/zyps/test_conditions.rb
53
+ - test/zyps/test_actions.rb
53
54
  - test/zyps/test_environmental_factors.rb
55
+ - test/zyps/test_conditions.rb
54
56
  - test/zyps/test_remote.rb
55
- test_files:
56
57
  - test/test_zyps.rb
58
+ has_rdoc: true
59
+ homepage: http://jay.mcgavren.com/zyps/
60
+ post_install_message:
57
61
  rdoc_options:
58
62
  - --title
59
63
  - Zyps - A game library for Ruby
60
64
  - --main
61
65
  - README.txt
62
- extra_rdoc_files:
63
- - README.txt
64
- - COPYING.LESSER.txt
65
- - COPYING.txt
66
- executables:
67
- - zyps
68
- extensions: []
69
-
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: "0"
73
+ version:
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: "0"
79
+ version:
70
80
  requirements: []
71
81
 
72
- dependencies:
73
- - !ruby/object:Gem::Dependency
74
- name: wxruby
75
- version_requirement:
76
- version_requirements: !ruby/object:Gem::Version::Requirement
77
- requirements:
78
- - - ">="
79
- - !ruby/object:Gem::Version
80
- version: 1.9.2
81
- version:
82
+ rubyforge_project: zyps
83
+ rubygems_version: 1.0.1
84
+ signing_key:
85
+ specification_version: 2
86
+ summary: A game library for Ruby
87
+ test_files:
88
+ - test/test_zyps.rb