zyps 0.1.1 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.txt CHANGED
@@ -26,10 +26,28 @@ Make sure you have administrative privileges, then type the following at a comma
26
26
 
27
27
  Ensure Ruby-GNOME2 is installed and working.
28
28
 
29
- To see the demo, at a command line, type:
29
+ At a command line, type:
30
+
31
+ zyps
32
+
33
+ To see a tutorial on the library, at a command line, type:
30
34
 
31
35
  zyps_demo
32
36
 
37
+ To run a DRb server others can connect to and add creatures (NOTE: not thoroughly tested and probably insecure!):
38
+
39
+ zyps_server [options]
40
+ -h, --help Display program help.
41
+ -m, --max-population [number] The maximum number of allowed game
42
+ objects. 25 by default.
43
+ -p, --port [number] Port number to run the server on.
44
+ If not defined, next available one
45
+ will be selected and the URI printed
46
+ to STDOUT.
47
+ -f, --fps [frames] Number of frames to draw per second.
48
+ --view-width [pixels] Window width. 800 by default.
49
+ --view-height [pixels] Window height. 600 by default.
50
+
33
51
 
34
52
  == Development
35
53
 
data/bin/zyps CHANGED
@@ -28,14 +28,366 @@ rescue LoadError
28
28
  end
29
29
 
30
30
 
31
- begin
31
+ #Generates new creatures and places them in an environment.
32
+ class Generator
32
33
 
33
- puts "This is just a placeholder for the final application."
34
- puts "To see a demonstration of the library, please run 'zyps_demo'."
34
+ #Environment creatures will be spawned into.
35
+ attr_accessor :environment
36
+ #Boundary that all new objects must be inside.
37
+ attr_accessor :min_x, :min_y, :max_x, :max_y
38
+ #Array of tags to choose from when generating new one.
39
+ attr_accessor :tag_pool
40
+ #Array of colors to choose from when generating new one.
41
+ attr_accessor :color_pool
42
+ #The maximum speed a new creature should be going.
43
+ attr_accessor :max_speed
44
+ #The maximum number of behaviors a creature can have.
45
+ attr_accessor :max_behaviors
46
+ #The maximum number of conditions a Behavior can have.
47
+ attr_accessor :max_conditions
48
+ #The maximum number of actions a Behavior can have.
49
+ attr_accessor :max_actions
50
+
51
+ def initialize(environment, min_x, min_y, max_x, max_y)
52
+ @environment, @min_x, @min_y, @max_x, @max_y = environment, min_x, min_y, max_x, max_y
53
+ @tag_pool = %q{foo bar baz}
54
+ @color_pool = []
55
+ @max_behaviors = 2
56
+ @max_conditions = 2
57
+ @max_actions = 2
58
+ @max_speed = 100
59
+ end
60
+
61
+ #Create a creature.
62
+ def create_creature()
63
+
64
+ creature = Creature.new()
65
+ #Create a Color.
66
+ creature.color = create_color
67
+ #Create a Location.
68
+ creature.location = create_location
69
+ #Create a Vector.
70
+ creature.vector = create_vector()
71
+ #Create a random tag.
72
+ creature.tags << create_tag()
73
+ #Create a random number of behaviors.
74
+ generate_number(0, @max_behaviors).to_i.times {creature.behaviors << create_behavior()}
75
+
76
+ creature
77
+
78
+ end
79
+
80
+ #Create a random tag.
81
+ #Chooses from tag_pool.
82
+ def create_tag()
83
+ @tag_pool[generate_number(0, @tag_pool.length).to_i]
84
+ end
85
+ #Create a random Color.
86
+ #Chooses from color_pool, or creates a random one from scratch if color_pool is empty.
87
+ def create_color()
88
+ if (color_pool.length != 0) then
89
+ return @color_pool[generate_number(0, @color_pool.length).to_i]
90
+ else
91
+ return Color.new(generate_random_number(0, 1), generate_random_number(0, 1), generate_random_number(0, 1))
92
+ end
93
+ end
94
+ #Create a random Location.
95
+ def create_location()
96
+ Location.new(
97
+ generate_random_number(0, @max_x - @min_x) + @min_x,
98
+ generate_random_number(0, @max_y - @min_y) + @min_y
99
+ )
100
+ end
101
+ #Create a random Vector.
102
+ def create_vector()
103
+ Vector.new(generate_number(0, @max_speed), generate_number(0, 360))
104
+ end
105
+ #Create a random Behavior.
106
+ def create_behavior()
107
+ behavior = Behavior.new
108
+ action_count = generate_number(0, @max_actions).to_i
109
+ action_count.times do
110
+ behavior.actions << create_action()
111
+ end
112
+ #Only add conditions if there's an action.
113
+ if action_count > 0 then
114
+ #There should always be a proximity condition.
115
+ behavior.conditions << create_proximity_condition()
116
+ #Add some additional conditions.
117
+ generate_random_number(0, @max_conditions).to_i.times do
118
+ behavior.conditions << create_condition()
119
+ end
120
+ end
121
+ behavior
122
+ end
123
+ #Create a random condition.
124
+ def create_condition()
125
+ case generate_number(0, 2).to_i
126
+ when 0
127
+ return create_tag_condition()
128
+ when 1
129
+ return create_age_condition()
130
+ else
131
+ raise "Invalid condition"
132
+ end
133
+ end
134
+ #Create a condition that looks for a random tag.
135
+ def create_tag_condition()
136
+ tag = create_tag()
137
+ lambda {|creature, target| target.tags.include?(tag)}
138
+ end
139
+ #Create a condition that looks for a random age.
140
+ def create_age_condition()
141
+ age = generate_number(0, 60)
142
+ lambda {|creature, target| target.age > age}
143
+ end
144
+ #Create a condition that looks for a random proximity.
145
+ def create_proximity_condition()
146
+ proximity = generate_number(0, 100)
147
+ lambda do |creature, target|
148
+ Utility.find_distance(creature.location, target.location) < proximity
149
+ end
150
+ end
151
+ #Create a random action.
152
+ def create_action()
153
+ case generate_random_number(0, 9).to_i
154
+ when 0
155
+ return create_accelerate_action()
156
+ when 1
157
+ return create_approach_action()
158
+ when 2
159
+ return create_flee_action()
160
+ when 3
161
+ return create_blend_action()
162
+ when 4
163
+ return create_mate_action()
164
+ when 5
165
+ return create_spawn_action()
166
+ when 6
167
+ return create_turn_action()
168
+ when 7
169
+ return create_eat_action()
170
+ when 8
171
+ return create_tag_action()
172
+ else
173
+ raise "Invalid action"
174
+ end
175
+ end
176
+ #Create an action that accelerates at a random rate.
177
+ def create_accelerate_action()
178
+ clock = Clock.new()
179
+ rate = generate_number(0, 10)
180
+ lambda {|creature, target| creature.vector.speed += rate * clock.elapsed_time}
181
+ end
182
+ #Create an action that turns in a circle.
183
+ def create_turn_action()
184
+ clock = Clock.new()
185
+ rate = generate_number(0, 360)
186
+ lambda {|creature, target| creature.vector.pitch += rate * clock.elapsed_time}
187
+ end
188
+ #Create an action that approaches the target.
189
+ def create_approach_action()
190
+ heading = Vector.new
191
+ rate = generate_number(0, 20)
192
+ lambda do |creature, target|
193
+ #Find the difference between the current heading and the angle to the target.
194
+ turn_angle = Utility.find_angle(creature.location, target.location) - heading.pitch
195
+ #If the angle is the long way around from the current heading, change it to the smaller angle.
196
+ if turn_angle > 180 then
197
+ turn_angle -= 360.0
198
+ elsif turn_angle < -180 then
199
+ turn_angle += 360.0
200
+ end
201
+ #If turn angle is greater than allowed turn speed, reduce it.
202
+ turn_angle = Utility.constrain_value(turn_angle, rate)
203
+ #Turn the appropriate amount.
204
+ heading.pitch += turn_angle
205
+ #Apply the heading to the creature's movement vector.
206
+ creature.vector += heading
207
+ end
208
+ end
209
+ #Create an action that flees from the target.
210
+ def create_flee_action()
211
+ heading = Vector.new
212
+ rate = generate_number(0, 20)
213
+ lambda do |creature, target|
214
+ #Find the difference between the current heading and the angle AWAY from the target.
215
+ turn_angle = Utility.find_angle(creature.location, target.location) - heading.pitch + 180
216
+ #If the angle is the long way around from the current heading, change it to the smaller angle.
217
+ if turn_angle > 180 then
218
+ turn_angle -= 360.0
219
+ elsif turn_angle < -180 then
220
+ turn_angle += 360.0
221
+ end
222
+ #If turn angle is greater than allowed turn speed, reduce it.
223
+ turn_angle = Utility.constrain_value(turn_angle, rate)
224
+ #Turn the appropriate amount.
225
+ heading.pitch += turn_angle
226
+ #Apply the heading to the creature's movement vector.
227
+ creature.vector += heading
228
+ end
229
+ end
230
+ #Create an action that shifts the subject's color to match the target.
231
+ def create_blend_action()
232
+ lambda {|creature, target| creature.color += target.color}
233
+ end
234
+ #Create an action that mates with the target.
235
+ def create_mate_action()
236
+ #TODO
237
+ lambda {|creature, target| }
238
+ end
239
+ #Create an action that spawns a new creature.
240
+ def create_spawn_action()
241
+ #TODO
242
+ lambda {|creature, target| }
243
+ end
244
+ #Create an action that eats the target.
245
+ def create_eat_action()
246
+ lambda {|creature, target| @environment.objects.delete(target)}
247
+ end
248
+ #Create an action that applies a tag to the target.
249
+ def create_tag_action()
250
+ tag = create_tag()
251
+ lambda {|creature, target| target.tags << tag unless target.tags.include?(tag)}
252
+ end
253
+ #Generate a number between the given minimum and maximum, based on the current system time.
254
+ def generate_number(minimum, maximum)
255
+ (Time.new.to_f % (maximum - minimum)) + minimum
256
+ end
257
+ #Generate a random number between the given minimum and maximum.
258
+ def generate_random_number(minimum, maximum)
259
+ value = rand * (maximum - minimum) + minimum
260
+ end
261
+ end
35
262
 
36
- rescue => exception
37
263
 
264
+ #Keeps all objects within a set of walls.
265
+ class Enclose < Behavior
266
+ attr_accessor :left, :top, :right, :bottom
267
+ def initialize
268
+ super
269
+ @actions << lambda do |boundary, object|
270
+ #If object is beyond a boundary, set its position equal to the boundary and reflect it.
271
+ if (object.location.x < @left) then
272
+ object.location.x = @left
273
+ object.vector.pitch = Utility.find_reflection_angle(90, object.vector.pitch)
274
+ elsif (object.location.x > @right) then
275
+ object.location.x = @right
276
+ object.vector.pitch = Utility.find_reflection_angle(270, object.vector.pitch)
277
+ end
278
+ if (object.location.y < @top) then
279
+ object.location.y = @top
280
+ object.vector.pitch = Utility.find_reflection_angle(0, object.vector.pitch)
281
+ elsif (object.location.y > @bottom) then
282
+ object.location.y = @bottom
283
+ object.vector.pitch = Utility.find_reflection_angle(180, object.vector.pitch)
284
+ end
285
+ end
286
+ end
287
+ end
288
+
289
+
290
+ class Application
291
+
292
+ #Tags to randomly choose from.
293
+ attr_accessor :tag_pool
294
+ #Colors to randomly choose from.
295
+ attr_accessor :color_pool
296
+ #Seconds between creature creation.
297
+ attr_accessor :birth_rate
298
+
299
+ #Create app window, game environment, and view.
300
+ def initialize(width, height)
301
+
302
+ @width, @height = width, height
303
+ @tag_pool, @color_pool = [], []
304
+ @birth_rate = 1
305
+
306
+ #Create a window, and set GTK up to quit when it is closed.
307
+ window = Gtk::Window.new
308
+ window.signal_connect("delete_event") {false}
309
+ window.signal_connect("destroy") {Gtk.main_quit}
310
+
311
+ #Add view to window.
312
+ @view = TrailsView.new(@width, @height)
313
+ window.add(@view.canvas)
314
+ window.show_all
315
+
316
+ #Create environment.
317
+ @environment = Environment.new
318
+
319
+ #Point view at environment.
320
+ @environment.add_observer(@view)
321
+
322
+ end
323
+
324
+
325
+ def main
326
+
327
+ #Keep all objects within a boundary.
328
+ enclose = Enclose.new()
329
+ enclose.left = 0
330
+ enclose.right = @width
331
+ enclose.top = 0
332
+ enclose.bottom = @height
333
+ @environment.environmental_factors << EnvironmentalFactor.new([enclose])
334
+
335
+ #Create a creature generator.
336
+ generator = Generator.new(@environment, 0, 0, @width, @height)
337
+ generator.tag_pool = @tag_pool
338
+ generator.color_pool = @color_pool
339
+
340
+ #Create thread to update environment.
341
+ thread = Thread.new do
342
+ begin
343
+ birth_clock = Clock.new
344
+ time_since_birth = 0
345
+ loop do
346
+ @environment.interact
347
+ #Control population.
348
+ @environment.objects.shift while @environment.objects.length > 25
349
+ #Create new creature every few seconds.
350
+ if (time_since_birth += birth_clock.elapsed_time) > birth_rate
351
+ @environment.objects << generator.create_creature()
352
+ time_since_birth = 0
353
+ end
354
+ #Delay 1/60th second to avoid screen flicker.
355
+ sleep 1.0 / 60.0
356
+ end
357
+ rescue Exception => exception
358
+ puts exception, exception.backtrace
359
+ end
360
+ end
361
+
362
+ #Activate the GUI.
363
+ Gtk.main
364
+
365
+ end
366
+
367
+
368
+ end
369
+
370
+
371
+ begin
372
+ TAG_POOL = %w{x y z}
373
+ COLOR_POOL = [
374
+ Color.new(1, 0, 0),
375
+ Color.new(1, 0.75, 0),
376
+ Color.new(1, 1, 0),
377
+ Color.new(0, 1, 0),
378
+ Color.new(0, 0, 1),
379
+ Color.new(1, 0, 1)
380
+ ]
381
+ #The view width.
382
+ WIDTH = 500
383
+ #The view height.
384
+ HEIGHT = 400
385
+ #Run the application.
386
+ application = Application.new(WIDTH, HEIGHT)
387
+ application.color_pool = COLOR_POOL
388
+ application.tag_pool = TAG_POOL
389
+ application.main
390
+ rescue => exception
38
391
  #Print error to STDERR and exit with an abnormal status.
39
392
  abort "Error: " + exception.message + exception.backtrace.join("\n")
40
-
41
393
  end
data/bin/zyps_demo CHANGED
@@ -28,8 +28,6 @@ rescue LoadError
28
28
  end
29
29
 
30
30
 
31
-
32
-
33
31
  class Demo
34
32
 
35
33
  #The view width.
@@ -521,5 +519,10 @@ class Demo
521
519
  end
522
520
 
523
521
 
524
- #Run the demos.
525
- Demo.new.main
522
+ begin
523
+ #Run the demos.
524
+ Demo.new.main
525
+ rescue => exception
526
+ #Print error to STDERR and exit with an abnormal status.
527
+ abort "Error: " + exception.message
528
+ end
data/bin/zyps_server ADDED
@@ -0,0 +1,178 @@
1
+ #!/usr/local/bin/ruby
2
+
3
+ # Copyright 2007 Jay McGavren, jay@mcgavren.com.
4
+ #
5
+ # This file is part of Zyps.
6
+ #
7
+ # Zyps is free software; you can redistribute it and/or modify
8
+ # it under the terms of the GNU Lesser General Public License as published by
9
+ # the Free Software Foundation; either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # This program is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Lesser General Public License
18
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
19
+
20
+
21
+ begin
22
+ require 'optparse'
23
+ require 'zyps'
24
+ require 'zyps/views/trails'
25
+ require 'drb'
26
+ rescue LoadError
27
+ require 'optparse'
28
+ require 'rubygems'
29
+ require 'zyps'
30
+ require 'zyps/views/trails'
31
+ require 'drb'
32
+ end
33
+
34
+
35
+ #An Environment intended for serving over DRb.
36
+ class RemoteEnvironment < Environment
37
+ def initialize
38
+ super
39
+ @objects.extend DRbUndumped
40
+ @environmental_factors.extend DRbUndumped
41
+ end
42
+ def objects=(value)
43
+ @objects = value
44
+ @objects.extend DRbUndumped
45
+ end
46
+ def environmental_factors=(value)
47
+ @environmental_factors = value
48
+ @environmental_factors.extend DRbUndumped
49
+ end
50
+ end
51
+
52
+
53
+ #A server for a Zyps Environment.
54
+ class EnvironmentServer
55
+
56
+ #Port to open service on.
57
+ attr_accessor :port
58
+ #Maximum allowed number of objects.
59
+ attr_accessor :max_population
60
+ #View dimensions.
61
+ attr_accessor :view_width, :view_height
62
+
63
+ #Set up default values.
64
+ def initialize(port = nil, max_population = 25, fps = 60, view_width = 800, view_height = 600)
65
+ @port, @max_population, @fps, @view_width, @view_height = port, max_population, fps, view_width, view_height
66
+ end
67
+
68
+ #Create app window, game environment, view, and network service.
69
+ def main
70
+
71
+ #A clock to track frames to draw this second.
72
+ clock = Clock.new
73
+
74
+ #Create a window, and set GTK up to quit when it is closed.
75
+ window = Gtk::Window.new
76
+ window.signal_connect("delete_event") {false}
77
+ window.signal_connect("destroy") {Gtk.main_quit}
78
+
79
+ #Add view to window.
80
+ view = TrailsView.new(@view_width, @view_height)
81
+ window.add(view.canvas)
82
+ window.show_all
83
+
84
+ #Create environment.
85
+ environment = RemoteEnvironment.new
86
+ #Point view at environment.
87
+ environment.add_observer(view)
88
+
89
+ #Create thread to update environment.
90
+ update_thread = Thread.new do
91
+ begin
92
+ loop do
93
+ environment.interact
94
+ #Control population.
95
+ environment.objects.shift while environment.objects.length > @max_population
96
+ #Sleep for the rest of this frame.
97
+ delay = (1.0 / @fps) - clock.elapsed_time
98
+ sleep delay if delay > 0
99
+ end
100
+ rescue Exception => exception
101
+ puts exception, exception.backtrace
102
+ end
103
+ end
104
+
105
+ #Start a network service.
106
+ DRb.start_service(
107
+ @port ? "druby://localhost:#{@port}" : nil,
108
+ environment
109
+ )
110
+ #If port was automatically chosen, display our URI.
111
+ puts DRb.uri unless @port
112
+
113
+ #Activate the GUI.
114
+ Gtk.main
115
+
116
+ end
117
+
118
+
119
+ #Set attributes according to command-line arguments.
120
+ def process_options(arguments)
121
+
122
+ #Set up option parser.
123
+ options = OptionParser.new
124
+
125
+ #Define valid options.
126
+ options.on("-h", "--help", TrueClass, "Display program help.") {
127
+ puts options.help
128
+ exit
129
+ }
130
+ options.on(
131
+ "-m",
132
+ "--max-population [number]",
133
+ Integer,
134
+ "The maximum number of allowed game objects. 25 by default."
135
+ ) {|value| @max_population = value}
136
+ options.on(
137
+ "-p",
138
+ "--port [number]",
139
+ Integer,
140
+ "Port number to run the server on. If not defined, next available one will be selected and the URI printed to STDOUT."
141
+ ) {|value| @port = value}
142
+ options.on(
143
+ "-f",
144
+ "--fps [frames]",
145
+ Integer,
146
+ "Number of frames to draw per second."
147
+ ) {|value| @fps = value}
148
+ options.on(
149
+ "--view-width [pixels]",
150
+ Integer,
151
+ "Window width. 800 by default."
152
+ ) {|value| @view_width = value}
153
+ options.on(
154
+ "--view-height [pixels]",
155
+ Integer,
156
+ "Window height. 600 by default."
157
+ ) {|value| @view_height = value}
158
+
159
+ #Parse the options, printing usage if parsing fails.
160
+ options.parse(arguments) rescue puts "#{$!}\nType '#{$0} --help' for valid options."
161
+
162
+ end
163
+
164
+
165
+ end
166
+
167
+
168
+ begin
169
+ #Create a server.
170
+ server = EnvironmentServer.new
171
+ #Parse the command line.
172
+ server.process_options(ARGV)
173
+ #Start the server.
174
+ server.main
175
+ rescue => exception
176
+ #Print error to STDERR and exit with an abnormal status.
177
+ abort "Error: " + exception.message + exception.backtrace.join("\n")
178
+ end
data/lib/zyps.rb CHANGED
@@ -19,7 +19,6 @@
19
19
  require 'observer'
20
20
 
21
21
 
22
-
23
22
  #A virtual environment.
24
23
  class Environment
25
24
 
@@ -47,17 +46,16 @@ class Environment
47
46
  objects.each do |object|
48
47
 
49
48
  #Move each object according to its vector.
50
- object.location.x += object.vector.x * elapsed_time
51
- object.location.y += object.vector.y * elapsed_time
49
+ object.move(elapsed_time)
52
50
 
53
51
  #Have all environmental factors interact with each object.
54
- environmental_factors.each {|factor| factor.act(factor, object)}
52
+ environmental_factors.each {|factor| factor.act(object)}
55
53
 
56
54
  #Have all creatures interact with each other.
57
55
  if object.respond_to?(:act)
58
56
  objects.each do |target|
59
57
  next if target.equal?(object) #Ensure object does not act on itself.
60
- object.act(object, target)
58
+ object.act(target)
61
59
  end
62
60
  end
63
61
 
@@ -78,6 +76,9 @@ end
78
76
  #An object in the virtual environment.
79
77
  class GameObject
80
78
 
79
+ #A universal identifier for the object.
80
+ #Needed for DRb transmission, etc.
81
+ attr_reader :identifier
81
82
  #The object's Location in space.
82
83
  attr_accessor :location
83
84
  #A Color that will be used to draw the object.
@@ -91,9 +92,16 @@ class GameObject
91
92
 
92
93
  def initialize (name = nil, location = Location.new, color = Color.new, vector = Vector.new, age = 0, tags = [])
93
94
  @name, @location, @color, @vector, @tags = name, location, color, vector, tags
95
+ @identifier = rand(99999999) #TODO: Current setup won't necessarily be unique.
94
96
  self.age = age
95
97
  end
96
98
 
99
+ #Move according to vector over the given number of seconds.
100
+ def move (elapsed_time)
101
+ @location.x += @vector.x * elapsed_time
102
+ @location.y += @vector.y * elapsed_time
103
+ end
104
+
97
105
  #Time since the object was created, in seconds.
98
106
  def age; Time.new.to_f - @birth_time; end
99
107
  def age=(age); @birth_time = Time.new.to_f - age; end
@@ -106,8 +114,8 @@ end
106
114
  module Responsive
107
115
 
108
116
  #Call Behavior.perform on each of the object's assigned Behaviors, with the object and a target as arguments.
109
- def act(subject, target)
110
- behaviors.each {|behavior| behavior.perform(subject, target)}
117
+ def act(target)
118
+ behaviors.each {|behavior| behavior.perform(self, target)}
111
119
  end
112
120
 
113
121
  end
@@ -188,7 +196,7 @@ class Color
188
196
  attr_accessor :red, :green, :blue
189
197
 
190
198
  def initialize (red = 1, green = 1, blue = 1)
191
- @red, @green, @blue = red, green, blue
199
+ self.red, self.green, self.blue = red, green, blue
192
200
  end
193
201
 
194
202
  #Automatically constrains value to the range 0 - 1.
@@ -248,7 +256,6 @@ class Vector
248
256
  def pitch=(degrees)
249
257
  #Constrain degrees to 0 to 360.
250
258
  value = degrees % 360
251
- value += 360 if value < 0
252
259
  #Store as radians internally.
253
260
  @pitch = Utility.to_radians(value)
254
261
  end
@@ -352,7 +359,21 @@ module Utility
352
359
  value
353
360
  end
354
361
 
355
- end
356
-
357
-
362
+ #Given a normal and an angle, find the reflection angle.
363
+ def Utility.find_reflection_angle(normal, angle)
364
+ incidence_angle = normal - angle
365
+ reflection_angle = normal + incidence_angle
366
+ reflection_angle %= 360
367
+ reflection_angle
368
+ end
358
369
 
370
+ #Given a location and the upper left and lower right corners of a box, determine if the point is within the box.
371
+ def Utility.inside_box?(point, upper_left, lower_right)
372
+ return false if point.x < upper_left.x
373
+ return false if point.y < upper_left.y
374
+ return false if point.x > lower_right.x
375
+ return false if point.y > lower_right.y
376
+ true
377
+ end
378
+
379
+ end
@@ -84,20 +84,20 @@ class TrailsView
84
84
 
85
85
  #For each GameObject in the environment:
86
86
  environment.objects.each do |object|
87
-
87
+
88
88
  #Add the object's current location to the list.
89
- @locations[object] << [object.location.x, object.location.y]
89
+ @locations[object.identifier] << [object.location.x, object.location.y]
90
90
  #If the list is larger than the number of tail segments, delete the first position.
91
- @locations[object].shift if @locations[object].length > @trail_length
91
+ @locations[object.identifier].shift if @locations[object.identifier].length > @trail_length
92
92
 
93
93
  #For each location in this object's list:
94
- @locations[object].each_with_index do |location, index|
94
+ @locations[object.identifier].each_with_index do |location, index|
95
95
 
96
96
  #Skip first location.
97
97
  next if index == 0
98
98
 
99
99
  #Divide the current segment number by trail segment count to get the multiplier to use for brightness and width.
100
- multiplier = index.to_f / @locations[object].length.to_f
100
+ multiplier = index.to_f / @locations[object.identifier].length.to_f
101
101
 
102
102
  #Set the drawing color to use the object's colors, adjusted by the multiplier.
103
103
  graphics_context.rgb_fg_color = Gdk::Color.new( #Don't use Gdk::GC.foreground= here, as that requires a color to be in the color map already.
@@ -115,7 +115,7 @@ class TrailsView
115
115
  )
116
116
 
117
117
  #Get previous location so we can draw a line from it.
118
- previous_location = @locations[object][index - 1]
118
+ previous_location = @locations[object.identifier][index - 1]
119
119
 
120
120
  #Draw a line with the current width from the prior location to the current location.
121
121
  buffer.draw_line(
data/test/test_zyps.rb CHANGED
@@ -20,11 +20,31 @@ require 'zyps'
20
20
  require 'test/unit'
21
21
 
22
22
 
23
+ class TestGameObject < Test::Unit::TestCase
24
+
25
+
26
+ def test_move
27
+ #Set up moving object.
28
+ object = GameObject.new
29
+ object.location = Location.new(0, 0)
30
+ object.vector = Vector.new(1.4142, 45)
31
+ #Move for 1 second.
32
+ object.move(1)
33
+ #Check object moved to expected coordinates.
34
+ assert_in_delta(1, object.location.x, 0.001)
35
+ assert_in_delta(1, object.location.y, 0.001)
36
+ end
37
+
38
+
39
+ end
40
+
41
+
23
42
  class TestCreature < Test::Unit::TestCase
24
43
 
25
44
 
26
45
  def test_default_initialization
27
46
  creature = Creature.new
47
+ assert_not_nil(creature.identifier)
28
48
  assert_equal(0, creature.location.x)
29
49
  assert_equal(0, creature.location.y)
30
50
  assert_equal(1, creature.color.red)
@@ -36,6 +56,8 @@ class TestCreature < Test::Unit::TestCase
36
56
  assert_in_delta(0, creature.age, 0.1)
37
57
  assert_equal([], creature.tags)
38
58
  assert_equal([], creature.behaviors)
59
+ #Identifiers should be unique.
60
+ assert_not_equal(creature.identifier, Creature.new.identifier)
39
61
  end
40
62
 
41
63
 
@@ -98,7 +120,8 @@ class TestEnvironment < Test::Unit::TestCase
98
120
  #Look for expected interactions (each should only occur once).
99
121
  assert(@interactions.find_all{|i| i == "2 targeting 1"}.length == 1)
100
122
  assert(@interactions.find_all{|i| i == "1 targeting 2"}.length == 1)
101
- #TODO: Ensure creatures don't target selves.
123
+ assert(@interactions.find_all{|i| i == "1 targeting 1"}.length == 0)
124
+ assert(@interactions.find_all{|i| i == "2 targeting 2"}.length == 0)
102
125
 
103
126
  end
104
127
 
@@ -305,6 +328,32 @@ class TestUtility < Test::Unit::TestCase
305
328
  assert_in_delta(1.4142, Utility.find_distance(origin, Location.new(-1,-1)), 0.001)
306
329
  assert_in_delta(1.4142, Utility.find_distance(origin, Location.new(1,-1)), 0.001)
307
330
  end
331
+
332
+
333
+ def test_find_reflection_angle
334
+ assert_equal(210, Utility.find_reflection_angle(0, 150))
335
+ assert_equal(330, Utility.find_reflection_angle(0, 30))
336
+ assert_equal(150, Utility.find_reflection_angle(90, 30))
337
+ assert_equal(210, Utility.find_reflection_angle(90, 330))
338
+ assert_equal(30, Utility.find_reflection_angle(180, 330))
339
+ assert_equal(150, Utility.find_reflection_angle(180, 210))
340
+ assert_equal(330, Utility.find_reflection_angle(270, 210))
341
+ assert_equal(30, Utility.find_reflection_angle(270, 150))
342
+ end
308
343
 
309
344
 
310
- end
345
+ def test_inside_box?
346
+ #Too far left.
347
+ assert(! Utility.inside_box?(Location.new(1, 3), Location.new(2, 2), Location.new(4, 4)))
348
+ #Too far right.
349
+ assert(! Utility.inside_box?(Location.new(5, 3), Location.new(2, 2), Location.new(4, 4)))
350
+ #Too far up.
351
+ assert(! Utility.inside_box?(Location.new(3, 1), Location.new(2, 2), Location.new(4, 4)))
352
+ #Too far down.
353
+ assert(! Utility.inside_box?(Location.new(3, 5), Location.new(2, 2), Location.new(4, 4)))
354
+ #Inside.
355
+ assert(Utility.inside_box?(Location.new(3, 3), Location.new(2, 2), Location.new(4, 4)))
356
+ end
357
+
358
+
359
+ end
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.2
3
3
  specification_version: 1
4
4
  name: zyps
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.1.1
7
- date: 2007-07-25 00:00:00 -07:00
6
+ version: 0.2.1
7
+ date: 2007-08-06 00:00:00 -07:00
8
8
  summary: A simulation/game with autonomous creatures
9
9
  require_paths:
10
10
  - lib
@@ -34,6 +34,7 @@ files:
34
34
  - README.txt
35
35
  - bin/zyps
36
36
  - bin/zyps_demo
37
+ - bin/zyps_server
37
38
  - lib/zyps
38
39
  - lib/zyps/views
39
40
  - lib/zyps/views/trails.rb
@@ -53,6 +54,7 @@ extra_rdoc_files:
53
54
  executables:
54
55
  - zyps
55
56
  - zyps_demo
57
+ - zyps_server
56
58
  extensions: []
57
59
 
58
60
  requirements: