zyps 0.6.3 → 0.7.0
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/README.txt +11 -27
- data/bin/zyps +355 -296
- data/bin/zyps_demo +267 -249
- data/lib/zyps/actions.rb +104 -139
- data/lib/zyps/conditions.rb +39 -15
- data/lib/zyps/environmental_factors.rb +60 -48
- data/lib/zyps/views/canvas/gtk2.rb +152 -0
- data/lib/zyps/views/canvas/wx.rb +191 -0
- data/lib/zyps/views/trails.rb +56 -59
- data/lib/zyps.rb +101 -84
- data/test/test_zyps.rb +48 -75
- data/test/zyps/test_actions.rb +24 -73
- data/test/zyps/test_conditions.rb +30 -11
- data/test/zyps/test_environmental_factors.rb +28 -22
- data/test/zyps/test_remote.rb +3 -3
- metadata +16 -8
- data/bin/zyps_server +0 -235
data/bin/zyps
CHANGED
@@ -20,13 +20,15 @@
|
|
20
20
|
|
21
21
|
gems_loaded = false
|
22
22
|
begin
|
23
|
-
require '
|
23
|
+
require 'logger'
|
24
|
+
require 'wx'
|
24
25
|
require 'zyps'
|
25
26
|
require 'zyps/actions'
|
26
27
|
require 'zyps/conditions'
|
27
28
|
require 'zyps/environmental_factors'
|
28
29
|
require 'zyps/remote'
|
29
30
|
require 'zyps/views/trails'
|
31
|
+
require 'zyps/views/canvas/wx'
|
30
32
|
rescue LoadError
|
31
33
|
if gems_loaded == false
|
32
34
|
require 'rubygems'
|
@@ -41,304 +43,368 @@ end
|
|
41
43
|
include Zyps
|
42
44
|
|
43
45
|
|
44
|
-
|
45
|
-
|
46
|
-
DEFAULT_MAX_SPEED = 200
|
47
|
-
DEFAULT_MAX_POPULATION = 100
|
48
|
-
DEFAULT_FPS = 60
|
46
|
+
LOG_HANDLE = STDOUT
|
47
|
+
LOG_LEVEL = Logger::WARN
|
49
48
|
|
50
49
|
|
51
|
-
|
50
|
+
#The Zyps GUI.
|
51
|
+
class Application < Wx::App
|
52
52
|
|
53
|
-
|
54
|
-
|
55
|
-
#
|
56
|
-
|
57
|
-
#
|
58
|
-
|
59
|
-
|
60
|
-
#
|
61
|
-
#
|
62
|
-
def initialize(
|
63
|
-
uri = nil,
|
64
|
-
view_width = DEFAULT_VIEW_WIDTH,
|
65
|
-
view_height = DEFAULT_VIEW_HEIGHT,
|
66
|
-
fps = DEFAULT_FPS,
|
67
|
-
max_population = DEFAULT_MAX_POPULATION,
|
68
|
-
max_speed = DEFAULT_MAX_SPEED,
|
69
|
-
enclosure = true
|
70
|
-
)
|
71
|
-
|
72
|
-
@uri, @view_width, @view_height, @fps, @max_population, @max_speed, @enclosure =
|
73
|
-
uri, view_width, view_height, fps, max_population, max_speed, enclosure
|
74
|
-
|
75
|
-
#Create a window, and set GTK up to quit when it is closed.
|
76
|
-
window = Gtk::Window.new
|
77
|
-
window.resizable = false
|
78
|
-
window.signal_connect("delete_event") {false}
|
79
|
-
window.signal_connect("destroy") {Gtk.main_quit}
|
80
|
-
|
81
|
-
#Create environment.
|
82
|
-
@environment = Environment.new
|
83
|
-
|
84
|
-
#Set up controls.
|
85
|
-
#Also initializes @view.
|
86
|
-
window.add(create_controls)
|
53
|
+
|
54
|
+
#Takes a hash with these keys and defaults:
|
55
|
+
# :width => 800
|
56
|
+
# :height => 600
|
57
|
+
# :max_speed => 200
|
58
|
+
# :max_population => 100
|
59
|
+
# :fps => 60
|
60
|
+
# :enclosure => true
|
61
|
+
# :uri => nil
|
62
|
+
def initialize(options = {})
|
87
63
|
|
88
|
-
|
89
|
-
window.show_all
|
90
|
-
|
91
|
-
#Point view at environment.
|
92
|
-
@environment.add_observer(@view)
|
93
|
-
|
94
|
-
end
|
64
|
+
super()
|
95
65
|
|
66
|
+
@log = Logger.new(LOG_HANDLE)
|
67
|
+
@log.level = LOG_LEVEL
|
68
|
+
@log.progname = self
|
69
|
+
|
70
|
+
@log.debug "Load options and merge with defaults."
|
71
|
+
@options = Configuration.load('options')
|
72
|
+
@options = {
|
73
|
+
:width => 800,
|
74
|
+
:height => 600,
|
75
|
+
:max_speed => 200,
|
76
|
+
:max_population => 50,
|
77
|
+
:fps => 30,
|
78
|
+
:enclosure => true,
|
79
|
+
:uri => nil,
|
80
|
+
}.merge(options)
|
81
|
+
@log.debug "options: #{@options.inspect}"
|
96
82
|
|
97
|
-
|
83
|
+
end
|
98
84
|
|
99
|
-
#Keep objects on screen.
|
100
|
-
if @enclosure
|
101
|
-
enclosure = Enclosure.new()
|
102
|
-
enclosure.left = 0
|
103
|
-
enclosure.bottom = 0
|
104
|
-
enclosure.top = @view_height
|
105
|
-
enclosure.right = @view_width
|
106
|
-
@environment.environmental_factors << enclosure
|
107
|
-
end
|
108
|
-
|
109
|
-
#Keep all objects under a certain speed.
|
110
|
-
@environment.environmental_factors << SpeedLimit.new(@max_speed) if @max_speed
|
111
|
-
|
112
|
-
#Limit population.
|
113
|
-
@environment.environmental_factors << PopulationLimit.new(@environment, @max_population) if @max_population
|
114
|
-
|
115
|
-
#Set up a creature generator.
|
116
|
-
@generator = CreatureGenerator.new(@environment)
|
117
85
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
86
|
+
#Create GUI objects.
|
87
|
+
def on_init
|
88
|
+
|
89
|
+
@log.debug "Create the main window."
|
90
|
+
frame = Wx::Frame.new(
|
91
|
+
nil, #No parent.
|
92
|
+
:size => [@options[:width], @options[:height]],
|
93
|
+
:title => "Zyps"
|
94
|
+
)
|
95
|
+
@log.debug "All controls in frame should be resized when frame is."
|
96
|
+
frame.sizer = Wx::BoxSizer.new(Wx::HORIZONTAL)
|
97
|
+
@log.debug "Application should exit when frame is closed."
|
98
|
+
frame.evt_close {on_exit}
|
99
|
+
|
100
|
+
@log.debug "Create a view and canvas."
|
101
|
+
@view = TrailsView.new(
|
102
|
+
:width => @options[:width],
|
103
|
+
:height => @options[:height],
|
104
|
+
:canvas => WxCanvas.new(frame)
|
105
|
+
)
|
106
|
+
frame.sizer.add(@view.canvas.drawing_area, 1, Wx::GROW)
|
107
|
+
@log.debug "Set up mouse click/drag handlers for canvas."
|
108
|
+
@press_location = nil
|
109
|
+
@view.canvas.drawing_area.evt_left_down {|event| on_mouse_press(event)}
|
110
|
+
@view.canvas.drawing_area.evt_left_up {|event| on_mouse_release(event)}
|
111
|
+
@log.debug "Resize view and enclosure when canvas changes size."
|
112
|
+
@view.canvas.drawing_area.evt_size {|event| on_view_resize(event)}
|
113
|
+
|
114
|
+
@log.debug "Create an interface."
|
115
|
+
@controls = ControlPanel.new(frame)
|
116
|
+
@controls.clear_button.evt_button(@controls.clear_button.get_id) do |event|
|
117
|
+
@environment.objects = []
|
118
|
+
end
|
119
|
+
@controls.length_slider.evt_slider(@controls.length_slider.get_id) do |event|
|
120
|
+
@view.trail_length = @controls.length_slider.value
|
121
|
+
end
|
122
|
+
@controls.trails_flag.evt_checkbox(@controls.trails_flag.get_id) do |event|
|
123
|
+
@view.erase_flag = ! @controls.trails_flag.is_checked
|
146
124
|
end
|
125
|
+
frame.sizer.add(@controls, 0, Wx::GROW)
|
147
126
|
|
127
|
+
@log.debug "Create an environment, and link it to the view."
|
128
|
+
@environment = Environment.new
|
129
|
+
@environment.add_observer(@view)
|
148
130
|
|
149
|
-
|
150
|
-
if @
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
131
|
+
@log.debug "Set up environmental factors."
|
132
|
+
if @options[:enclosure]
|
133
|
+
@enclosure = Enclosure.new(
|
134
|
+
:left => 0,
|
135
|
+
:bottom => 0,
|
136
|
+
:top => @view.canvas.drawing_area.size.height,
|
137
|
+
:right => @view.canvas.drawing_area.size.width
|
138
|
+
)
|
139
|
+
@environment.environmental_factors << @enclosure
|
155
140
|
end
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
141
|
+
@environment.environmental_factors << SpeedLimit.new(@options[:max_speed]) if @options[:max_speed]
|
142
|
+
@environment.environmental_factors << PopulationLimit.new(@options[:max_population]) if @options[:max_population]
|
143
|
+
|
144
|
+
@log.debug "Set up a creature generator."
|
145
|
+
@generator = CreatureGenerator.new(:environment => @environment)
|
146
|
+
|
147
|
+
@log.debug "Set up a timer to update the environment."
|
148
|
+
milliseconds_per_frame = (1.0 / @options[:fps] * 1000).to_int
|
149
|
+
@log.debug "Timer will fire every #{milliseconds_per_frame} milliseconds."
|
150
|
+
timer_id = Wx::ID_HIGHEST + 1
|
151
|
+
timer = Wx::Timer.new(self, timer_id)
|
152
|
+
evt_timer(timer_id) do
|
153
|
+
@environment.interact
|
154
|
+
#Keeps dead objects from accumulating and causing hiccups later.
|
155
|
+
GC.start
|
156
|
+
end
|
157
|
+
timer.start(milliseconds_per_frame)
|
160
158
|
|
159
|
+
@log.debug "Display GUI."
|
160
|
+
frame.show
|
161
|
+
|
161
162
|
end
|
163
|
+
|
162
164
|
|
165
|
+
#Save config and shut down.
|
166
|
+
def on_exit
|
167
|
+
Configuration.save('options', @options)
|
168
|
+
exit
|
169
|
+
end
|
163
170
|
|
164
|
-
#Create a view and controls.
|
165
|
-
def create_controls(view_width = @view_width, view_height = @view_height, homogeneous = false, spacing = 0, expand = false, fill = false, padding = 0)
|
166
171
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
@
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
#Create a creature on button release.
|
181
|
-
@view.canvas.add_events(Gdk::Event::BUTTON_RELEASE_MASK)
|
182
|
-
@view.canvas.signal_connect("button-release-event") do |canvas, event|
|
172
|
+
#When mouse button pressed, record location for use in release event handler.
|
173
|
+
def on_mouse_press(event)
|
174
|
+
@log.debug "Mouse pressed at #{event.x}, #{event.y}."
|
175
|
+
@press_location = Location.new(event.x, event.y)
|
176
|
+
end
|
177
|
+
|
178
|
+
|
179
|
+
#Create a creature on button release.
|
180
|
+
def on_mouse_release(event)
|
181
|
+
@log.debug "Mouse released at #{event.x}, #{event.y}."
|
182
|
+
#Ensure the mouse was pressed within the canvas.
|
183
|
+
if @press_location
|
184
|
+
#Create creature at release location.
|
183
185
|
@release_location = Location.new(event.x, event.y)
|
184
|
-
@
|
186
|
+
@log.debug "Release location: #{@release_location.inspect}"
|
187
|
+
@environment.objects << @generator.create_creature(
|
185
188
|
:x => event.x,
|
186
189
|
:y => event.y,
|
187
190
|
:speed => Utility.find_distance(@press_location, @release_location) * 2, #Use distance dragged as speed.
|
188
191
|
:pitch => Utility.find_angle(@press_location, @release_location), #Move in direction of drag.
|
189
|
-
:
|
190
|
-
:
|
191
|
-
:
|
192
|
-
:
|
193
|
-
:
|
194
|
-
:
|
195
|
-
:
|
196
|
-
:eat => @eat_flag.active?
|
192
|
+
:turn => @controls.turn_flag.is_checked,
|
193
|
+
:approach => @controls.approach_flag.is_checked,
|
194
|
+
:flee => @controls.flee_flag.is_checked,
|
195
|
+
:push => @controls.push_flag.is_checked,
|
196
|
+
:pull => @controls.pull_flag.is_checked,
|
197
|
+
:breed => @controls.breed_flag.is_checked,
|
198
|
+
:eat => @controls.eat_flag.is_checked
|
197
199
|
)
|
200
|
+
#Nullify press location, as it is no longer applicable.
|
201
|
+
@press_location = nil
|
198
202
|
end
|
199
|
-
|
200
|
-
#Create a VBox for all controls.
|
201
|
-
control_panel = Gtk::VBox.new(homogeneous, spacing)
|
202
|
-
|
203
|
-
#Create a group for the actions.
|
204
|
-
action_controls = Gtk::VBox.new(homogeneous, spacing)
|
205
|
-
action_controls.pack_start(Gtk::Label.new("Actions"), expand, fill, padding)
|
206
|
-
@accelerate_flag = Gtk::CheckButton.new("Accelerate")
|
207
|
-
action_controls.pack_start(@accelerate_flag, expand, fill, padding)
|
208
|
-
@turn_flag = Gtk::CheckButton.new("Turn")
|
209
|
-
action_controls.pack_start(@turn_flag, expand, fill, padding)
|
210
|
-
@approach_flag = Gtk::CheckButton.new("Approach")
|
211
|
-
action_controls.pack_start(@approach_flag, expand, fill, padding)
|
212
|
-
@flee_flag = Gtk::CheckButton.new("Flee")
|
213
|
-
action_controls.pack_start(@flee_flag, expand, fill, padding)
|
214
|
-
@push_flag = Gtk::CheckButton.new("Push")
|
215
|
-
action_controls.pack_start(@push_flag, expand, fill, padding)
|
216
|
-
@pull_flag = Gtk::CheckButton.new("Pull")
|
217
|
-
action_controls.pack_start(@pull_flag, expand, fill, padding)
|
218
|
-
@breed_flag = Gtk::CheckButton.new("Breed")
|
219
|
-
action_controls.pack_start(@breed_flag, expand, fill, padding)
|
220
|
-
@eat_flag = Gtk::CheckButton.new("Eat")
|
221
|
-
action_controls.pack_start(@eat_flag, expand, fill, padding)
|
222
|
-
#Add the action controls to the panel.
|
223
|
-
control_panel.pack_start(action_controls, expand, fill, padding)
|
224
|
-
|
225
|
-
#Create a group for environment controls.
|
226
|
-
environment_controls = Gtk::VBox.new(homogeneous, spacing)
|
227
|
-
environment_controls.pack_start(Gtk::Label.new("Environment"), expand, fill, padding)
|
228
|
-
@clear_button = Gtk::Button.new("Clear")
|
229
|
-
@clear_button.signal_connect("clicked") {
|
230
|
-
@environment.objects = []
|
231
|
-
}
|
232
|
-
environment_controls.pack_start(@clear_button, expand, fill, padding)
|
233
|
-
#Add the environment controls to the panel.
|
234
|
-
control_panel.pack_start(environment_controls, expand, fill, padding)
|
235
|
-
|
236
|
-
#Add the control panel to the interface.
|
237
|
-
interface.pack_start(control_panel, expand, fill, padding)
|
238
|
-
|
239
|
-
interface
|
240
|
-
|
241
203
|
end
|
204
|
+
|
205
|
+
|
206
|
+
#Resize objects that are dependent on the view size.
|
207
|
+
def on_view_resize(event)
|
208
|
+
@view.height = @view.canvas.drawing_area.size.height
|
209
|
+
@view.width = @view.canvas.drawing_area.size.width
|
210
|
+
@enclosure.top = @view.canvas.drawing_area.size.height
|
211
|
+
@enclosure.right = @view.canvas.drawing_area.size.width
|
212
|
+
end
|
213
|
+
|
214
|
+
|
215
|
+
end
|
216
|
+
|
242
217
|
|
243
218
|
|
244
|
-
|
245
|
-
|
219
|
+
#TODO
|
220
|
+
module Configuration
|
221
|
+
def Configuration.load(name)
|
222
|
+
{}
|
223
|
+
end
|
224
|
+
def Configuration.save(name, options)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
|
229
|
+
|
230
|
+
#Controls for manipulating the environment.
|
231
|
+
class ControlPanel < Wx::Panel
|
232
|
+
|
233
|
+
|
234
|
+
attr_accessor :turn_flag
|
235
|
+
attr_accessor :approach_flag
|
236
|
+
attr_accessor :flee_flag
|
237
|
+
attr_accessor :push_flag
|
238
|
+
attr_accessor :pull_flag
|
239
|
+
attr_accessor :breed_flag
|
240
|
+
attr_accessor :eat_flag
|
241
|
+
attr_accessor :length_slider
|
242
|
+
attr_accessor :trails_flag
|
243
|
+
attr_accessor :clear_button
|
244
|
+
|
245
|
+
|
246
|
+
def initialize(parent)
|
246
247
|
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
"--no-enclosure",
|
270
|
-
"Disables the barrier that normally keeps objects on the screen."
|
271
|
-
) {|value| @enclosure = false}
|
272
|
-
options.on(
|
273
|
-
"-u",
|
274
|
-
"--uri [uri]",
|
275
|
-
String,
|
276
|
-
"dRuby URI to run the server on. If not defined, one will be selected and printed to STDOUT."
|
277
|
-
) {|value| @uri = value}
|
278
|
-
options.on(
|
279
|
-
"-f",
|
280
|
-
"--fps [frames]",
|
281
|
-
Integer,
|
282
|
-
"Number of frames to draw per second. #{DEFAULT_FPS} by default."
|
283
|
-
) {|value| @fps = value}
|
284
|
-
options.on(
|
285
|
-
"--view-width [pixels]",
|
286
|
-
Integer,
|
287
|
-
"Window width. #{DEFAULT_VIEW_WIDTH} by default."
|
288
|
-
) {|value| @view_width = value}
|
289
|
-
options.on(
|
290
|
-
"--view-height [pixels]",
|
291
|
-
Integer,
|
292
|
-
"Window height. #{DEFAULT_VIEW_HEIGHT} by default."
|
293
|
-
) {|value| @view_height = value}
|
294
|
-
|
295
|
-
#Parse the options, printing usage if parsing fails.
|
296
|
-
options.parse(arguments) rescue puts "#{$!}\nType '#{$0} --help' for valid options."
|
248
|
+
super(parent)
|
249
|
+
self.sizer = Wx::BoxSizer.new(Wx::VERTICAL)
|
250
|
+
|
251
|
+
action_controls = add_panel(self)
|
252
|
+
add_label(action_controls, :label => "Actions")
|
253
|
+
@turn_flag = add_check_box(action_controls, :label => "Turn")
|
254
|
+
@approach_flag = add_check_box(action_controls, :label => "Chase")
|
255
|
+
@flee_flag = add_check_box(action_controls, :label => "Flee")
|
256
|
+
@push_flag = add_check_box(action_controls, :label => "Push")
|
257
|
+
@pull_flag = add_check_box(action_controls, :label => "Pull")
|
258
|
+
@breed_flag = add_check_box(action_controls, :label => "Breed")
|
259
|
+
@eat_flag = add_check_box(action_controls, :label => "Eat")
|
260
|
+
|
261
|
+
display_controls = add_panel(self)
|
262
|
+
add_label(display_controls, :label => "Length")
|
263
|
+
@length_slider = add_slider(display_controls, :min_value => 2, :max_value => 100)
|
264
|
+
@length_slider.value = 5
|
265
|
+
@trails_flag = add_check_box(display_controls, :label => "Trails")
|
266
|
+
|
267
|
+
environment_controls = add_panel(self)
|
268
|
+
add_label(environment_controls, :label => "Environment")
|
269
|
+
@clear_button = add_button(environment_controls, :label => "Clear")
|
297
270
|
|
298
271
|
end
|
299
272
|
|
300
273
|
|
274
|
+
private
|
275
|
+
|
276
|
+
#Other methods will call this with their particular type of control to add.
|
277
|
+
#Adds control to parent's sizer if it has one.
|
278
|
+
#Returns the control.
|
279
|
+
def add_control(control)
|
280
|
+
begin
|
281
|
+
control.parent.sizer.add(control, 0, Wx::ALL, 3)
|
282
|
+
rescue RuntimeError
|
283
|
+
#Parent had no sizer; take no action.
|
284
|
+
#Can't find another way to test for presence of a sizer as of wxRuby 1.9.2.
|
285
|
+
end
|
286
|
+
#Return the object.
|
287
|
+
control
|
288
|
+
end
|
289
|
+
|
290
|
+
#Create a check box with the given label, and add it to the given container.
|
291
|
+
def add_check_box(parent, options = {})
|
292
|
+
add_control(Wx::CheckBox.new(parent, options))
|
293
|
+
end
|
294
|
+
|
295
|
+
#Create a slider control with the given options, and add it to the given container.
|
296
|
+
def add_slider(parent, options = {})
|
297
|
+
add_control(Wx::Slider.new(parent, options))
|
298
|
+
end
|
299
|
+
|
300
|
+
#Create a button with the given options, and add it to the given container.
|
301
|
+
def add_button(parent, options = {})
|
302
|
+
add_control(Wx::Button.new(parent, options))
|
303
|
+
end
|
304
|
+
|
305
|
+
#Create the given label, and add it to the given container.
|
306
|
+
def add_label(parent, options = {})
|
307
|
+
control = Wx::StaticText.new(parent, options)
|
308
|
+
control.parent.sizer.add(control, 0, Wx::ALIGN_CENTER_HORIZONTAL|Wx::ALL, 3)
|
309
|
+
control
|
310
|
+
end
|
311
|
+
|
312
|
+
#Create a panel, and add it to the given container.
|
313
|
+
#Takes a hash with the following keys and defaults, in addition to those taken by Wx::Panel.new():
|
314
|
+
# :sizer => Wx::BoxSizer.new(Wx::VERTICAL)
|
315
|
+
def add_panel(parent, options = {})
|
316
|
+
options = {
|
317
|
+
:sizer => Wx::BoxSizer.new(Wx::VERTICAL),
|
318
|
+
}.merge(options)
|
319
|
+
panel = Wx::Panel.new(parent, options)
|
320
|
+
panel.sizer = options[:sizer]
|
321
|
+
panel.parent.sizer.add(panel, 1, Wx::GROW|Wx::ALL, 3)
|
322
|
+
panel
|
323
|
+
end
|
324
|
+
|
325
|
+
|
301
326
|
end
|
302
327
|
|
303
328
|
|
304
329
|
|
330
|
+
#Creates Creature objects.
|
305
331
|
class CreatureGenerator
|
306
|
-
|
307
|
-
|
332
|
+
|
333
|
+
|
334
|
+
#Environment creatures will be placed in.
|
308
335
|
attr_accessor :environment
|
336
|
+
#Default size of new creatures.
|
337
|
+
attr_accessor :default_size
|
309
338
|
#Default required proximity for actions.
|
310
|
-
attr_accessor :
|
311
|
-
#Speed of new AccelerateActions.
|
312
|
-
attr_accessor :acceleration_rate
|
339
|
+
attr_accessor :default_proximity
|
313
340
|
#Rate of new TurnActions.
|
314
341
|
attr_accessor :turn_rate
|
315
|
-
#
|
316
|
-
attr_accessor :
|
317
|
-
#
|
318
|
-
attr_accessor :
|
342
|
+
#Acceleration rate of new ApproachActions.
|
343
|
+
attr_accessor :approach_rate
|
344
|
+
#Acceleration rate of new FleeActions.
|
345
|
+
attr_accessor :flee_rate
|
319
346
|
#Strength of new PullActions.
|
320
347
|
attr_accessor :pull_strength
|
321
348
|
#Strength of new PushActions.
|
322
349
|
attr_accessor :push_strength
|
350
|
+
|
323
351
|
|
324
|
-
|
352
|
+
#Takes a hash with these keys and defaults:
|
353
|
+
# :environment => nil
|
354
|
+
# :default_size => 5
|
355
|
+
# :default_proximity => 200
|
356
|
+
# :approach_rate => 200
|
357
|
+
# :flee_rate => :approach_rate
|
358
|
+
# :push_strength => :approach_rate * 2
|
359
|
+
# :pull_strength => :push_strength * 0.75
|
360
|
+
# :turn_rate => :approach_rate * 1.1
|
361
|
+
# :turn_angle => 90
|
362
|
+
# :breed_rate => 10
|
363
|
+
def initialize(options = {})
|
325
364
|
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
@
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
365
|
+
#Set up logger.
|
366
|
+
@log = Logger.new(LOG_HANDLE)
|
367
|
+
@log.level = LOG_LEVEL
|
368
|
+
@log.progname = self
|
369
|
+
|
370
|
+
options = {
|
371
|
+
:default_size => 5,
|
372
|
+
:default_proximity => 200,
|
373
|
+
:approach_rate => 200,
|
374
|
+
:turn_angle => 90,
|
375
|
+
:breed_rate => 10,
|
376
|
+
}.merge(options)
|
377
|
+
@log.debug "options: #{@options.inspect}"
|
378
|
+
|
379
|
+
@environment = options[:environment]
|
380
|
+
@default_size = options[:default_size]
|
381
|
+
@default_proximity = options[:default_proximity]
|
382
|
+
@approach_rate = options[:approach_rate]
|
383
|
+
@turn_angle = options[:turn_angle]
|
384
|
+
@breed_rate = options[:breed_rate]
|
385
|
+
@flee_rate = options[:flee_rate] || @approach_rate * 2
|
386
|
+
@push_strength = options [:push_strength] || @approach_rate * 2
|
387
|
+
@pull_strength = options[:pull_strength] || @push_strength * 0.75
|
388
|
+
@turn_rate = options[:turn_rate] || @approach_rate * 1.1
|
337
389
|
|
338
390
|
end
|
339
|
-
|
340
|
-
|
341
|
-
#Create a creature
|
391
|
+
|
392
|
+
|
393
|
+
#Create a creature with the given attributes and behaviors.
|
394
|
+
#Takes a hash with these keys and defaults:
|
395
|
+
# :x => 0,
|
396
|
+
# :y => 0,
|
397
|
+
# :speed => 1,
|
398
|
+
# :pitch => 0,
|
399
|
+
# :size => @default_size,
|
400
|
+
# :action_proximity => @default_proximity,
|
401
|
+
# :turn => false,
|
402
|
+
# :approach => false,
|
403
|
+
# :flee => false,
|
404
|
+
# :push => false,
|
405
|
+
# :pull => false,
|
406
|
+
# :breed => false,
|
407
|
+
# :eat => false,
|
342
408
|
def create_creature(options = {})
|
343
409
|
|
344
410
|
options = {
|
@@ -346,8 +412,8 @@ class CreatureGenerator
|
|
346
412
|
:y => 0,
|
347
413
|
:speed => 1,
|
348
414
|
:pitch => 0,
|
349
|
-
:
|
350
|
-
:
|
415
|
+
:size => @default_size,
|
416
|
+
:action_proximity => @default_proximity,
|
351
417
|
:turn => false,
|
352
418
|
:approach => false,
|
353
419
|
:flee => false,
|
@@ -356,79 +422,76 @@ class CreatureGenerator
|
|
356
422
|
:breed => false,
|
357
423
|
:eat => false,
|
358
424
|
}.merge(options)
|
425
|
+
@log.debug "options: #{@options.inspect}"
|
359
426
|
|
360
427
|
#Create a creature.
|
361
428
|
creature = Creature.new(
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
Vector.new(options[:speed], options[:pitch]),
|
366
|
-
0,
|
367
|
-
5
|
429
|
+
:location => Location.new(options[:x], options[:y]),
|
430
|
+
:vector => Vector.new(options[:speed], options[:pitch]),
|
431
|
+
:size => options[:size]
|
368
432
|
)
|
369
433
|
|
370
434
|
#Set up actions and merge colors according to selected behaviors.
|
371
435
|
color = Color.new(0.25, 0.25, 0.25)
|
372
|
-
if options[:accelerate]
|
373
|
-
color.red += 0.25
|
374
|
-
color.green += 0.25
|
375
|
-
color.blue += 0.25
|
376
|
-
creature.behaviors << create_behavior(:actions => [AccelerateAction.new(@acceleration_rate)])
|
377
|
-
end
|
378
436
|
if options[:turn]
|
379
437
|
color.blue += 1
|
380
|
-
creature.behaviors <<
|
438
|
+
creature.behaviors << Behavior.new(
|
439
|
+
:actions => [TurnAction.new(@turn_rate, @turn_angle)],
|
440
|
+
:conditions => [ProximityCondition.new(options[:action_proximity] * 2)]
|
441
|
+
)
|
381
442
|
end
|
382
443
|
if options[:approach]
|
383
444
|
color.red += 1
|
384
|
-
creature.behaviors <<
|
445
|
+
creature.behaviors << Behavior.new(
|
446
|
+
:actions => [ApproachAction.new(@approach_rate)],
|
447
|
+
:conditions => [ProximityCondition.new(options[:action_proximity])]
|
448
|
+
)
|
385
449
|
end
|
386
450
|
if options[:flee]
|
387
451
|
color.red += 0.5; color.green += 0.5 #Yellow.
|
388
|
-
creature.behaviors <<
|
452
|
+
creature.behaviors << Behavior.new(
|
453
|
+
:actions => [FleeAction.new(@flee_rate)],
|
454
|
+
:conditions => [ProximityCondition.new(options[:action_proximity] * 0.5)]
|
455
|
+
)
|
389
456
|
end
|
390
457
|
if options[:push]
|
391
458
|
color.red += 0.5; color.blue += 0.5 #Purple.
|
392
|
-
creature.behaviors <<
|
459
|
+
creature.behaviors << Behavior.new(
|
460
|
+
:actions => [PushAction.new(@push_strength)],
|
461
|
+
:conditions => [ProximityCondition.new(options[:action_proximity] * 0.25)]
|
462
|
+
)
|
393
463
|
end
|
394
464
|
if options[:pull]
|
395
465
|
color.blue += 0.75; color.green += 0.75 #Aqua.
|
396
|
-
creature.behaviors <<
|
466
|
+
creature.behaviors << Behavior.new(
|
467
|
+
:actions => [PullAction.new(@pull_strength)],
|
468
|
+
:conditions => [ProximityCondition.new(options[:action_proximity] * 0.75)]
|
469
|
+
)
|
397
470
|
end
|
398
471
|
if options[:breed]
|
399
472
|
color.green -= 0.1 #Make a bit redder.
|
400
473
|
color.blue -= 0.1
|
401
|
-
creature.behaviors <<
|
474
|
+
creature.behaviors << Behavior.new(
|
402
475
|
:actions => [BreedAction.new(@environment, @breed_rate)],
|
403
476
|
:conditions => [CollisionCondition.new] #The default ProximityCondition won't do.
|
404
477
|
)
|
405
478
|
end
|
406
479
|
if options[:eat]
|
407
480
|
color.green += 1
|
408
|
-
creature.behaviors <<
|
481
|
+
creature.behaviors << Behavior.new(
|
409
482
|
:actions => [EatAction.new(@environment)],
|
410
|
-
:conditions => [
|
483
|
+
:conditions => [
|
484
|
+
CollisionCondition.new, #The default ProximityCondition won't do.
|
485
|
+
StrengthCondition.new #The eater should be as strong or stronger than its dinner.
|
486
|
+
]
|
411
487
|
)
|
412
488
|
end
|
413
489
|
|
414
490
|
creature.color = color
|
415
491
|
|
416
|
-
@
|
492
|
+
@log.debug "Created creature: #{creature.inspect}"
|
417
493
|
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
def create_behavior(options = {})
|
422
|
-
|
423
|
-
options = {
|
424
|
-
:actions => [],
|
425
|
-
:conditions => [ProximityCondition.new(@action_proximity)],
|
426
|
-
}.merge(options)
|
427
|
-
|
428
|
-
behavior = Behavior.new
|
429
|
-
behavior.actions = options[:actions]
|
430
|
-
behavior.conditions = options[:conditions]
|
431
|
-
behavior
|
494
|
+
creature
|
432
495
|
|
433
496
|
end
|
434
497
|
|
@@ -436,15 +499,11 @@ class CreatureGenerator
|
|
436
499
|
end
|
437
500
|
|
438
501
|
|
439
|
-
|
440
502
|
begin
|
441
|
-
|
442
|
-
|
443
|
-
#
|
444
|
-
|
445
|
-
#Start the server.
|
446
|
-
application.main
|
447
|
-
rescue => exception
|
503
|
+
Application.new.main_loop
|
504
|
+
rescue SystemExit
|
505
|
+
#No action.
|
506
|
+
rescue Exception => exception
|
448
507
|
#Print error to STDERR and exit with an abnormal status.
|
449
|
-
abort "Error: " + exception.message
|
508
|
+
abort "Error: " + exception.message
|
450
509
|
end
|