zyps 0.6.3 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/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
|