zyps 0.4.1 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (112) hide show
  1. data/bin/zyps +32 -80
  2. data/lib/zyps/actions.rb +74 -2
  3. data/lib/zyps/environmental_factors.rb +16 -0
  4. data/lib/zyps.rb +90 -17
  5. data/test/test_zyps.rb +111 -8
  6. data/test/zyps/test_actions.rb +111 -46
  7. data/test/zyps/test_behaviors.rb +0 -0
  8. data/test/zyps/test_environmental_factors.rb +33 -0
  9. metadata +3 -134
  10. data/doc/classes/AccelerateAction.html +0 -182
  11. data/doc/classes/AccelerateAction.src/M000009.html +0 -19
  12. data/doc/classes/AccelerateAction.src/M000010.html +0 -18
  13. data/doc/classes/Action.html +0 -118
  14. data/doc/classes/AgeCondition.html +0 -181
  15. data/doc/classes/AgeCondition.src/M000039.html +0 -18
  16. data/doc/classes/AgeCondition.src/M000040.html +0 -18
  17. data/doc/classes/ApproachAction.html +0 -189
  18. data/doc/classes/ApproachAction.src/M000048.html +0 -19
  19. data/doc/classes/ApproachAction.src/M000049.html +0 -38
  20. data/doc/classes/Behavior.html +0 -202
  21. data/doc/classes/Behavior.src/M000001.html +0 -18
  22. data/doc/classes/Behavior.src/M000002.html +0 -19
  23. data/doc/classes/BlendAction.html +0 -181
  24. data/doc/classes/BlendAction.src/M000043.html +0 -18
  25. data/doc/classes/BlendAction.src/M000044.html +0 -18
  26. data/doc/classes/Clock.html +0 -165
  27. data/doc/classes/Clock.src/M000041.html +0 -18
  28. data/doc/classes/Clock.src/M000042.html +0 -21
  29. data/doc/classes/CollisionCondition.html +0 -148
  30. data/doc/classes/CollisionCondition.src/M000053.html +0 -18
  31. data/doc/classes/Color.html +0 -299
  32. data/doc/classes/Color.src/M000022.html +0 -18
  33. data/doc/classes/Color.src/M000023.html +0 -16
  34. data/doc/classes/Color.src/M000024.html +0 -16
  35. data/doc/classes/Color.src/M000025.html +0 -16
  36. data/doc/classes/Color.src/M000026.html +0 -18
  37. data/doc/classes/Color.src/M000027.html +0 -22
  38. data/doc/classes/Condition.html +0 -118
  39. data/doc/classes/Creature.html +0 -191
  40. data/doc/classes/Creature.src/M000011.html +0 -19
  41. data/doc/classes/Creature.src/M000012.html +0 -18
  42. data/doc/classes/DestroyAction.html +0 -181
  43. data/doc/classes/DestroyAction.src/M000030.html +0 -18
  44. data/doc/classes/DestroyAction.src/M000031.html +0 -18
  45. data/doc/classes/EatAction.html +0 -149
  46. data/doc/classes/EatAction.src/M000045.html +0 -21
  47. data/doc/classes/Enclosure.html +0 -206
  48. data/doc/classes/Enclosure.src/M000005.html +0 -18
  49. data/doc/classes/Enclosure.src/M000006.html +0 -31
  50. data/doc/classes/Environment.html +0 -203
  51. data/doc/classes/Environment.src/M000046.html +0 -19
  52. data/doc/classes/Environment.src/M000047.html +0 -45
  53. data/doc/classes/EnvironmentalFactor.html +0 -118
  54. data/doc/classes/FaceAction.html +0 -148
  55. data/doc/classes/FaceAction.src/M000052.html +0 -18
  56. data/doc/classes/FleeAction.html +0 -189
  57. data/doc/classes/FleeAction.src/M000054.html +0 -19
  58. data/doc/classes/FleeAction.src/M000055.html +0 -38
  59. data/doc/classes/GameObject.html +0 -282
  60. data/doc/classes/GameObject.src/M000032.html +0 -19
  61. data/doc/classes/GameObject.src/M000033.html +0 -16
  62. data/doc/classes/GameObject.src/M000034.html +0 -19
  63. data/doc/classes/GameObject.src/M000035.html +0 -16
  64. data/doc/classes/GameObject.src/M000036.html +0 -16
  65. data/doc/classes/Location.html +0 -167
  66. data/doc/classes/Location.src/M000021.html +0 -18
  67. data/doc/classes/ProximityCondition.html +0 -182
  68. data/doc/classes/ProximityCondition.src/M000037.html +0 -18
  69. data/doc/classes/ProximityCondition.src/M000038.html +0 -18
  70. data/doc/classes/TagAction.html +0 -181
  71. data/doc/classes/TagAction.src/M000028.html +0 -18
  72. data/doc/classes/TagAction.src/M000029.html +0 -18
  73. data/doc/classes/TagCondition.html +0 -181
  74. data/doc/classes/TagCondition.src/M000007.html +0 -18
  75. data/doc/classes/TagCondition.src/M000008.html +0 -18
  76. data/doc/classes/TrailsView.html +0 -209
  77. data/doc/classes/TrailsView.src/M000003.html +0 -40
  78. data/doc/classes/TrailsView.src/M000004.html +0 -78
  79. data/doc/classes/TurnAction.html +0 -182
  80. data/doc/classes/TurnAction.src/M000050.html +0 -19
  81. data/doc/classes/TurnAction.src/M000051.html +0 -18
  82. data/doc/classes/Utility.html +0 -264
  83. data/doc/classes/Utility.src/M000056.html +0 -26
  84. data/doc/classes/Utility.src/M000057.html +0 -22
  85. data/doc/classes/Utility.src/M000058.html +0 -18
  86. data/doc/classes/Utility.src/M000059.html +0 -21
  87. data/doc/classes/Utility.src/M000060.html +0 -25
  88. data/doc/classes/Utility.src/M000061.html +0 -21
  89. data/doc/classes/Utility.src/M000062.html +0 -21
  90. data/doc/classes/Vector.html +0 -281
  91. data/doc/classes/Vector.src/M000013.html +0 -19
  92. data/doc/classes/Vector.src/M000014.html +0 -16
  93. data/doc/classes/Vector.src/M000015.html +0 -21
  94. data/doc/classes/Vector.src/M000016.html +0 -16
  95. data/doc/classes/Vector.src/M000017.html +0 -18
  96. data/doc/classes/Vector.src/M000018.html +0 -16
  97. data/doc/classes/Vector.src/M000019.html +0 -18
  98. data/doc/classes/Vector.src/M000020.html +0 -25
  99. data/doc/created.rid +0 -1
  100. data/doc/files/COPYING_LESSER_txt.html +0 -311
  101. data/doc/files/COPYING_txt.html +0 -1003
  102. data/doc/files/README_txt.html +0 -214
  103. data/doc/files/lib/zyps/actions_rb.html +0 -134
  104. data/doc/files/lib/zyps/conditions_rb.html +0 -134
  105. data/doc/files/lib/zyps/environmental_factors_rb.html +0 -134
  106. data/doc/files/lib/zyps/views/trails_rb.html +0 -134
  107. data/doc/files/lib/zyps_rb.html +0 -134
  108. data/doc/fr_class_index.html +0 -53
  109. data/doc/fr_file_index.html +0 -34
  110. data/doc/fr_method_index.html +0 -88
  111. data/doc/index.html +0 -24
  112. data/doc/rdoc-style.css +0 -208
data/bin/zyps CHANGED
@@ -37,13 +37,6 @@ end
37
37
 
38
38
  class Application
39
39
 
40
- #Tags to randomly choose from.
41
- attr_accessor :tag_pool
42
- #Colors to randomly choose from.
43
- attr_accessor :color_pool
44
- #Seconds between creature creation.
45
- attr_accessor :birth_rate
46
-
47
40
  #Create app window, game environment, and view.
48
41
  def initialize(width, height)
49
42
 
@@ -81,6 +74,9 @@ class Application
81
74
  enclosure.top = @height
82
75
  enclosure.right = @width
83
76
  @environment.environmental_factors << enclosure
77
+
78
+ #Keep all objects under a certain speed.
79
+ @environment.environmental_factors << SpeedLimit.new(100)
84
80
 
85
81
  #Create thread to update environment.
86
82
  thread = Thread.new do
@@ -147,32 +143,24 @@ class Application
147
143
  Utility.find_angle(@press_location, @release_location) #Move in direction of drag.
148
144
  ),
149
145
  0,
150
- 1,
151
- @tags_box.text.split(/,\s*/)
146
+ 1
152
147
  )
153
148
  behavior = Behavior.new
154
- condition_tags = @tag_condition_box.text
155
- condition_tags.split(/,\s*/).each do |tag|
156
- behavior.conditions << TagCondition.new(tag)
157
- end
158
- proximity = @proximity_condition_slider.value
159
- behavior.conditions << ProximityCondition.new(proximity) if proximity > 0
160
- age = @age_condition_slider.value
161
- behavior.conditions << AgeCondition.new(age) if age > 0
162
- acceleration = @acceleration_slider.value
163
- behavior.actions << AccelerateAction.new(acceleration) if acceleration != 0
164
- turn = @turn_slider.value
165
- behavior.actions << TurnAction.new(turn) if turn != 0
166
- approach_angle = @approach_slider.value
167
- behavior.actions << ApproachAction.new(Vector.new(1, creature.vector.pitch), approach_angle) if approach_angle > 0
168
- flee_angle = @flee_slider.value
169
- behavior.actions << FleeAction.new(Vector.new(5, creature.vector.pitch), flee_angle) if flee_angle > 0
170
- behavior.actions << EatAction.new(@environment) if @eat_flag.active?
171
- apply_tags = @tag_action_box.text
172
- apply_tags.split(/,\s*/).each do |tag|
173
- behavior.actions << TagAction.new(tag)
174
- end
149
+ behavior.conditions << ProximityCondition.new(200)
150
+ behavior.actions << AccelerateAction.new(1) if @accelerate_flag.active?
151
+ behavior.actions << TurnAction.new(90) if @turn_flag.active?
152
+ behavior.actions << ApproachAction.new(creature.vector, 720) if @approach_flag.active?
153
+ behavior.actions << FleeAction.new(creature.vector) if @flee_flag.active?
154
+ behavior.actions << PushAction.new(1000) if @push_flag.active?
155
+ behavior.actions << PullAction.new(1000) if @pull_flag.active?
156
+ behavior.actions << BlendAction.new(creature.color) if @paint_flag.active?
175
157
  creature.behaviors << behavior
158
+ if @eat_flag.active?
159
+ behavior = Behavior.new
160
+ behavior.actions << EatAction.new(@environment)
161
+ behavior.conditions << ProximityCondition.new(10)
162
+ creature.behaviors << behavior
163
+ end
176
164
  @environment.objects << creature
177
165
  end
178
166
 
@@ -195,64 +183,28 @@ class Application
195
183
  attribute_controls.pack_start(@blue_slider, expand, fill, padding)
196
184
  @red_slider.value, @green_slider.value, @blue_slider.value = 1, 1, 1
197
185
 
198
- #Add a text box for the tags.
199
- attribute_controls.pack_start(Gtk::Label.new("Tags"), expand, fill, padding)
200
- @tags_box = Gtk::Entry.new
201
- attribute_controls.pack_start(@tags_box, expand, fill, padding)
202
-
203
186
  #Add the attribute controls to the panel.
204
187
  control_panel.pack_start(attribute_controls, expand, fill, padding)
205
188
 
206
- #Create a group for the conditions.
207
- condition_controls = Gtk::VBox.new(homogeneous, spacing)
208
- condition_controls.pack_start(Gtk::Label.new("Target"), expand, fill, padding)
209
- #Add a text box for the tag condition.
210
- condition_controls.pack_start(Gtk::Label.new("Tags"), expand, fill, padding)
211
- @tag_condition_box = Gtk::Entry.new
212
- condition_controls.pack_start(@tag_condition_box, expand, fill, padding)
213
- #Add a slider for the age condition.
214
- @age_condition_slider = Gtk::HScale.new(0, 300, 30)
215
- condition_controls.pack_start(Gtk::Label.new("Age"), expand, fill, padding)
216
- condition_controls.pack_start(@age_condition_slider, expand, fill, padding)
217
- #Add a slider for the proximity condition.
218
- @proximity_condition_slider = Gtk::HScale.new(1, 100, 10)
219
- condition_controls.pack_start(Gtk::Label.new("Proximity"), expand, fill, padding)
220
- condition_controls.pack_start(@proximity_condition_slider, expand, fill, padding)
221
- #Add the condition controls to the panel.
222
- control_panel.pack_start(condition_controls, expand, fill, padding)
223
-
224
189
  #Create a group for the actions.
225
190
  action_controls = Gtk::VBox.new(homogeneous, spacing)
226
191
  action_controls.pack_start(Gtk::Label.new("Actions"), expand, fill, padding)
227
- #Add a slider for the accelerate action.
228
- @acceleration_slider = Gtk::HScale.new(-10, 10, 2)
229
- action_controls.pack_start(Gtk::Label.new("Accelerate"), expand, fill, padding)
230
- action_controls.pack_start(@acceleration_slider, expand, fill, padding)
231
- @acceleration_slider.value = 0
232
- #Add a slider for the turn action.
233
- @turn_slider = Gtk::HScale.new(-180, 180, 20)
234
- action_controls.pack_start(Gtk::Label.new("Turn"), expand, fill, padding)
235
- action_controls.pack_start(@turn_slider, expand, fill, padding)
236
- @turn_slider.value = 0
237
- #Add a slider for the approach angle.
238
- @approach_slider = Gtk::HScale.new(-180, 180, 20)
239
- action_controls.pack_start(Gtk::Label.new("Approach"), expand, fill, padding)
240
- action_controls.pack_start(@approach_slider, expand, fill, padding)
241
- @approach_slider.value = 0
242
- #Add a slider for the flee action.
243
- @flee_slider = Gtk::HScale.new(-180, 180, 20)
244
- action_controls.pack_start(Gtk::Label.new("Flee"), expand, fill, padding)
245
- action_controls.pack_start(@flee_slider, expand, fill, padding)
246
- @flee_slider.value = 0
247
- #Add a checkbox for the eat action.
192
+ @accelerate_flag = Gtk::CheckButton.new("Accelerate")
193
+ action_controls.pack_start(@accelerate_flag, expand, fill, padding)
194
+ @turn_flag = Gtk::CheckButton.new("Turn")
195
+ action_controls.pack_start(@turn_flag, expand, fill, padding)
196
+ @approach_flag = Gtk::CheckButton.new("Approach")
197
+ action_controls.pack_start(@approach_flag, expand, fill, padding)
198
+ @flee_flag = Gtk::CheckButton.new("Flee")
199
+ action_controls.pack_start(@flee_flag, expand, fill, padding)
200
+ @push_flag = Gtk::CheckButton.new("Push")
201
+ action_controls.pack_start(@push_flag, expand, fill, padding)
202
+ @pull_flag = Gtk::CheckButton.new("Pull")
203
+ action_controls.pack_start(@pull_flag, expand, fill, padding)
248
204
  @eat_flag = Gtk::CheckButton.new("Eat")
249
205
  action_controls.pack_start(@eat_flag, expand, fill, padding)
250
- #Add a checkbox for the spawn action.
251
- #TODO
252
- #Add a text box for the tag action.
253
- action_controls.pack_start(Gtk::Label.new("Apply Tag"), expand, fill, padding)
254
- @tag_action_box = Gtk::Entry.new
255
- action_controls.pack_start(@tag_action_box, expand, fill, padding)
206
+ @paint_flag = Gtk::CheckButton.new("Paint")
207
+ action_controls.pack_start(@paint_flag, expand, fill, padding)
256
208
  #Add the action controls to the panel.
257
209
  control_panel.pack_start(action_controls, expand, fill, padding)
258
210
 
data/lib/zyps/actions.rb CHANGED
@@ -35,6 +35,11 @@ class AccelerateAction < Action
35
35
  self.rate = rate
36
36
  @clock = Clock.new
37
37
  end
38
+ #Begin tracking time between actions.
39
+ def start(actor, target)
40
+ super
41
+ @clock.reset_elapsed_time
42
+ end
38
43
  #Increase or decrease speed according to elapsed time.
39
44
  def do(actor, target)
40
45
  actor.vector.speed += @clock.elapsed_time * rate
@@ -51,6 +56,11 @@ class TurnAction < Action
51
56
  self.rate = rate
52
57
  @clock = Clock.new
53
58
  end
59
+ #Begin tracking time between actions.
60
+ def start(actor, target)
61
+ super
62
+ @clock.reset_elapsed_time
63
+ end
54
64
  #Turn according to elapsed time.
55
65
  def do(actor, target)
56
66
  actor.vector.pitch += @clock.elapsed_time * rate
@@ -68,6 +78,11 @@ class ApproachAction < Action
68
78
  self.heading, self.turn_rate = heading, turn_rate
69
79
  @clock = Clock.new
70
80
  end
81
+ #Begin tracking time between actions.
82
+ def start(actor, target)
83
+ super
84
+ @clock.reset_elapsed_time
85
+ end
71
86
  #Accelerate toward the target, but limited by turn rate.
72
87
  def do(actor, target)
73
88
  #Find the difference between the current heading and the angle to the target.
@@ -105,6 +120,11 @@ class FleeAction < Action
105
120
  self.heading, self.turn_rate = heading, turn_rate
106
121
  @clock = Clock.new
107
122
  end
123
+ #Begin tracking time between actions.
124
+ def start(actor, target)
125
+ super
126
+ @clock.reset_elapsed_time
127
+ end
108
128
  #Accelerate away from the target, but limited by turn rate.
109
129
  def do(actor, target)
110
130
  #Find the difference between the current heading and the angle to the target.
@@ -142,6 +162,8 @@ class DestroyAction < Action
142
162
  #Remove the target from the environment.
143
163
  def do(actor, target)
144
164
  @environment.objects.delete(target)
165
+ #Discontinue action.
166
+ return false
145
167
  end
146
168
  end
147
169
 
@@ -150,10 +172,10 @@ end
150
172
  class EatAction < DestroyAction
151
173
  #Remove the target from the environment, and increase actor's size by size of target.
152
174
  def do(actor, target)
153
- #Remove the target from the environment.
154
- super
155
175
  #Grow in size.
156
176
  actor.size += target.size
177
+ #Remove the target from the environment.
178
+ super
157
179
  end
158
180
  end
159
181
 
@@ -184,3 +206,53 @@ class BlendAction < Action
184
206
  target.color += @color
185
207
  end
186
208
  end
209
+
210
+
211
+ #Pushes target away.
212
+ class PushAction < Action
213
+ #Units/second to accelerate target by.
214
+ attr_accessor :force
215
+ def initialize(force = 1)
216
+ @force = force
217
+ @clock = Clock.new
218
+ end
219
+ #Begin tracking time between actions.
220
+ def start(actor, target)
221
+ super
222
+ @clock.reset_elapsed_time
223
+ end
224
+ #Accelerate away from the target, but limited by turn rate.
225
+ def do(actor, target)
226
+ #Angle to target is also angle of push force.
227
+ push_angle = Utility.find_angle(actor.location, target.location)
228
+ #Acceleration will be limited by elapsed time.
229
+ push_force = @force * @clock.elapsed_time
230
+ #Apply the force to the creature's movement vector.
231
+ target.vector += Vector.new(push_force, push_angle)
232
+ end
233
+ end
234
+
235
+
236
+ #Pulls target toward actor.
237
+ class PullAction < Action
238
+ #Units/second to accelerate target by.
239
+ attr_accessor :force
240
+ def initialize(force = 1)
241
+ @force = force
242
+ @clock = Clock.new
243
+ end
244
+ #Begin tracking time between actions.
245
+ def start(actor, target)
246
+ super
247
+ @clock.reset_elapsed_time
248
+ end
249
+ #Accelerate away from the target, but limited by turn rate.
250
+ def do(actor, target)
251
+ #Angle from target to actor is also angle of pull force (opposite of that for push).
252
+ pull_angle = Utility.find_angle(target.location, actor.location)
253
+ #Acceleration will be limited by elapsed time.
254
+ pull_force = @force * @clock.elapsed_time
255
+ #Apply the force to the creature's movement vector.
256
+ target.vector += Vector.new(pull_force, pull_angle)
257
+ end
258
+ end
@@ -53,3 +53,19 @@ end
53
53
 
54
54
 
55
55
 
56
+ #Keeps all objects at/under the assigned speed.
57
+ class SpeedLimit < EnvironmentalFactor
58
+
59
+ #Maximum allowed speed in units.
60
+ attr_accessor :maximum
61
+ def initialize(units)
62
+ self.maximum = units
63
+ end
64
+ #If object is over the speed, reduce its speed.
65
+ def act(object)
66
+ object.vector.speed = Utility.constrain_value(object.vector.speed, @maximum)
67
+ end
68
+ end
69
+
70
+
71
+
data/lib/zyps.rb CHANGED
@@ -37,7 +37,7 @@ class Environment
37
37
  #Allow everything in the environment to interact with each other.
38
38
  #Objects are first moved according to their preexisting vectors and the amount of time since the last call.
39
39
  #Then each EnvironmentalFactor is allowed to act on each object.
40
- #Finally, each GameObject with an act() method is allowed to act on every other object.
40
+ #Finally, each GameObject with an act() method is allowed to act on the environment.
41
41
  def interact
42
42
 
43
43
  #Get time since last interaction.
@@ -51,12 +51,9 @@ class Environment
51
51
  #Have all environmental factors interact with each object.
52
52
  environmental_factors.each {|factor| factor.act(object)}
53
53
 
54
- #Have all creatures interact with each other.
54
+ #Have all creatures interact with the environment.
55
55
  if object.respond_to?(:act)
56
- objects.each do |target|
57
- next if target.equal?(object) #Ensure object does not act on itself.
58
- object.act(target)
59
- end
56
+ object.act(self)
60
57
  end
61
58
 
62
59
  end
@@ -126,9 +123,9 @@ class Creature < GameObject
126
123
  self.behaviors = behaviors
127
124
  end
128
125
 
129
- #Call Behavior.perform on each of the object's assigned Behaviors, with the object and a target as arguments.
130
- def act(target)
131
- behaviors.each {|behavior| behavior.perform(self, target)}
126
+ #Call Behavior.perform(self, environment) on each of the creature's assigned Behaviors.
127
+ def act(environment)
128
+ behaviors.each {|behavior| behavior.perform(self, environment)}
132
129
  end
133
130
 
134
131
  end
@@ -143,8 +140,32 @@ end
143
140
 
144
141
 
145
142
  #An action that one Creature takes on another.
146
- #Actions must implement a do(actor, target) instance method.
147
143
  class Action
144
+
145
+ #Whether the action was previously started.
146
+ attr_reader :started
147
+
148
+ def initialize
149
+ @started = false
150
+ end
151
+
152
+ #Start the action.
153
+ #Overriding subclasses must either call "super" or set the @started attribute to true.
154
+ def start(actor, target)
155
+ @started = true
156
+ end
157
+
158
+ #Perform the action.
159
+ #Subclasses should override this.
160
+ def do(actor, target)
161
+ end
162
+
163
+ #Stop the action.
164
+ #Overriding subclasses must either call "super" or set the @started attribute to false.
165
+ def stop(actor, target)
166
+ @started = false
167
+ end
168
+
148
169
  end
149
170
 
150
171
 
@@ -161,7 +182,7 @@ end
161
182
  #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.
162
183
  class Behavior
163
184
 
164
- #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.
185
+ #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).
165
186
  attr_accessor :conditions
166
187
  #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.
167
188
  attr_accessor :actions
@@ -169,15 +190,63 @@ class Behavior
169
190
  #Optionally takes an array of actions and one of conditions.
170
191
  def initialize (actions = [], conditions = [])
171
192
  self.actions, self.conditions = actions, conditions
193
+ #Tracks current target.
194
+ @active_target = nil
172
195
  end
173
196
 
174
- #Calls each Proc object in the list of conditions with the subject and its target. Returns nil if any condition returns false.
175
- #Then calls each Proc object in the list of actions, also with the subject and its target.
176
- def perform(subject, target)
177
- conditions.each {|condition| return unless condition.test(subject, target)}
178
- actions.each {|action| action.do(subject, target)}
197
+ #Test all conditions against each object in the evironment.
198
+ #For the first object that meets all of them, mark it active (and operate on it first next time).
199
+ #Then call start() (if applicable) and perform() for all actions against the active target.
200
+ #If any action or condition returns false, stop all actions, and deselect the active target.
201
+ def perform(actor, environment)
202
+
203
+ begin
204
+ #Select a target.
205
+ target = select_target(actor, environment.objects)
206
+ #Do the actions on the target.
207
+ actions.each do |action|
208
+ action.start(actor, target) unless action.started
209
+ action.do(actor, target)
210
+ end
211
+ rescue
212
+ #If the behavior can no longer be performed, halt it.
213
+ stop(actor, target)
214
+ end
215
+
179
216
  end
180
217
 
218
+
219
+ private
220
+
221
+ #Stop all actions and de-select the active target.
222
+ def stop(actor, target)
223
+ actions.each do |action|
224
+ action.stop(actor, target) if action.started
225
+ end
226
+ @active_target = nil
227
+ end
228
+
229
+ #Select a target that matches all conditions.
230
+ def select_target(actor, targets)
231
+ #If a target is already active, still present in the environment, and all conditions are true for it, simply re-select it.
232
+ if @active_target and targets.include?(@active_target) and conditions.all?{|condition| condition.test(actor, @active_target)}
233
+ return @active_target
234
+ end
235
+ #For each object in environment:
236
+ targets.each do |target|
237
+ #Don't let actor target itself.
238
+ next if target == actor
239
+ #If all conditions match (or there are no conditions), select the object.
240
+ if conditions.all?{|condition| condition.test(actor, target)}
241
+ @active_target = target
242
+ return target
243
+ end
244
+ end
245
+ #If there were no matches, throw an exception.
246
+ raise "No matching targets found."
247
+ end
248
+
249
+
181
250
  end
182
251
 
183
252
 
@@ -292,7 +361,7 @@ end
292
361
  class Clock
293
362
 
294
363
  def initialize
295
- @last_check_time = Time.new.to_f
364
+ reset_elapsed_time
296
365
  end
297
366
 
298
367
  #Returns the time in (fractional) seconds since this method was last called (or on the first call, time since the Clock was created).
@@ -303,6 +372,10 @@ class Clock
303
372
  elapsed_time
304
373
  end
305
374
 
375
+ def reset_elapsed_time
376
+ @last_check_time = Time.new.to_f
377
+ end
378
+
306
379
  end
307
380
 
308
381
 
data/test/test_zyps.rb CHANGED
@@ -125,6 +125,7 @@ class TestEnvironment < Test::Unit::TestCase
125
125
  @environment = Environment.new
126
126
  @environment.objects << Creature.new('1')
127
127
  @environment.objects << Creature.new('2')
128
+ @environment.objects << Creature.new('3')
128
129
 
129
130
  end
130
131
 
@@ -144,11 +145,13 @@ class TestEnvironment < Test::Unit::TestCase
144
145
 
145
146
  def test_interactions
146
147
 
147
- #Set up behavior that will log interactions.
148
- behavior = Behavior.new
148
+ #Set up behaviors that will log interactions.
149
149
  log = LogAction.new
150
- behavior.actions << log
151
- @environment.objects.each {|creature| creature.behaviors << behavior}
150
+ @environment.objects.each do |creature|
151
+ behavior = Behavior.new
152
+ behavior.actions << log
153
+ creature.behaviors << behavior
154
+ end
152
155
 
153
156
  #Have environment elements interact.
154
157
  @environment.interact
@@ -211,10 +214,32 @@ class TestEnvironment < Test::Unit::TestCase
211
214
  #Have environment elements interact.
212
215
  @environment.interact
213
216
 
214
- #Creature '1' should NOT have been acted on.
215
- assert(log.interactions.find_all{|i| i == "2 targeting 1"}.length == 0)
216
- #Creature '2' SHOULD have been acted on.
217
- assert(log.interactions.find_all{|i| i == "1 targeting 2"}.length == 1)
217
+ assert_equal(0, log.interactions.find_all{|i| i == "2 targeting 1"}.length, "Creature '1' should NOT have been acted on.")
218
+ assert_equal(1, log.interactions.find_all{|i| i == "1 targeting 2"}.length, "Creature '2' SHOULD have been acted on.")
219
+
220
+ end
221
+
222
+
223
+ #Test that creatures can switch targets when one is removed from the environment.
224
+ def test_object_removal
225
+
226
+ #Set up behaviors that will log interactions.
227
+ log = LogAction.new
228
+ @environment.objects.each do |creature|
229
+ behavior = Behavior.new
230
+ behavior.actions << log
231
+ creature.behaviors << behavior
232
+ end
233
+
234
+ #Have environment elements interact.
235
+ @environment.interact
236
+ #Remove the original target from the environment.
237
+ @environment.objects.delete_at(1)
238
+ #Interact again.
239
+ @environment.interact
240
+
241
+ #Look for expected interactions (each should only occur once).
242
+ assert_equal(1, log.interactions.find_all{|i| i == "1 targeting 3"}.length)
218
243
 
219
244
  end
220
245
 
@@ -446,4 +471,82 @@ class TestUtility < Test::Unit::TestCase
446
471
  ))
447
472
  end
448
473
 
474
+ end
475
+
476
+ #TODO: Rewrite to pass environment.
477
+ class TestBehavior < Test::Unit::TestCase
478
+
479
+ #Always true.
480
+ class TrueCondition < Condition
481
+ def test(actor, target)
482
+ true
483
+ end
484
+ end
485
+ #Always false.
486
+ class FalseCondition < Condition
487
+ def test(actor, target)
488
+ false
489
+ end
490
+ end
491
+
492
+
493
+ def setup
494
+ @actor = Creature.new('actor')
495
+ @target = Creature.new('target')
496
+ @other = Creature.new('other')
497
+ @environment = Environment.new
498
+ @environment.objects << @actor << @target << @other
499
+ end
500
+
501
+
502
+ #Tracks number of times its start, stop, and do methods are called.
503
+ class MockAction < Action
504
+ attr_accessor :start_count, :stop_count, :do_count
505
+ def initialize
506
+ @start_count, @stop_count, @do_count = 0, 0, 0
507
+ end
508
+ def start(actor, target)
509
+ super
510
+ @start_count += 1
511
+ end
512
+ def do(actor, target)
513
+ @do_count += 1
514
+ end
515
+ def stop(actor, target)
516
+ super
517
+ @stop_count += 1
518
+ end
519
+ end
520
+
521
+ def test_actions
522
+
523
+ #Set up a behavior with a true condition and a mock action.
524
+ behavior = Behavior.new
525
+ behavior.conditions << TrueCondition.new
526
+ action = MockAction.new
527
+ behavior.actions << action
528
+
529
+ #Perform the behavior.
530
+ behavior.perform(@actor, @environment)
531
+ assert_equal(1, action.start_count, "start() should have been called on the mock action.")
532
+ assert_equal(1, action.do_count, "do() should have been called.")
533
+ assert_equal(0, action.stop_count, "stop() should NOT have been called.")
534
+
535
+ #Perform the behavior again.
536
+ behavior.perform(@actor, @environment)
537
+ assert_equal(1, action.start_count, "start() should NOT have been called.")
538
+ assert_equal(2, action.do_count, "do() should have been called.")
539
+ assert_equal(0, action.stop_count, "stop() should NOT have been called.")
540
+
541
+ #Add a false condition to the behavior.
542
+ behavior.conditions << FalseCondition.new
543
+ #Perform the behavior.
544
+ behavior.perform(@actor, @environment)
545
+ assert_equal(1, action.start_count, "start() should NOT have been called.")
546
+ assert_equal(2, action.do_count, "do() should NOT have been called, because conditions are no longer true.")
547
+ assert_equal(1, action.stop_count, "stop() SHOULD have been called.")
548
+
549
+ end
550
+
551
+
449
552
  end