graphics 1.0.0b1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.autotest +26 -0
- data/.gemtest +0 -0
- data/History.rdoc +12 -0
- data/Manifest.txt +37 -0
- data/README.rdoc +71 -0
- data/Rakefile +26 -0
- data/examples/boid.rb +653 -0
- data/examples/bounce.rb +86 -0
- data/examples/collision.rb +74 -0
- data/examples/demo.rb +90 -0
- data/examples/editor.rb +70 -0
- data/examples/fluid.rb +246 -0
- data/examples/fluid2.rb +199 -0
- data/examples/lito.rb +108 -0
- data/examples/lito2.rb +110 -0
- data/examples/logo.rb +73 -0
- data/examples/math.rb +42 -0
- data/examples/radar.rb +31 -0
- data/examples/tank.rb +160 -0
- data/examples/tank2.rb +173 -0
- data/examples/targeting.rb +46 -0
- data/examples/vants.rb +69 -0
- data/examples/walker.rb +116 -0
- data/examples/zenspider1.rb +93 -0
- data/examples/zenspider2.rb +123 -0
- data/examples/zenspider3.rb +104 -0
- data/examples/zenspider4.rb +90 -0
- data/examples/zombies.rb +385 -0
- data/lib/graphics.rb +9 -0
- data/lib/graphics/body.rb +216 -0
- data/lib/graphics/extensions.rb +48 -0
- data/lib/graphics/simulation.rb +377 -0
- data/lib/graphics/trail.rb +69 -0
- data/lib/graphics/v.rb +71 -0
- data/resources/images/body.png +0 -0
- data/resources/images/turret.png +0 -0
- data/rubysdl_setup.sh +34 -0
- data/test/test_graphics.rb +408 -0
- metadata +191 -0
- metadata.gz.sig +2 -0
@@ -0,0 +1,90 @@
|
|
1
|
+
#!/usr/bin/ruby -w
|
2
|
+
|
3
|
+
srand 42
|
4
|
+
|
5
|
+
require "graphics"
|
6
|
+
|
7
|
+
class Array
|
8
|
+
def sorted_include? o
|
9
|
+
a, b = o
|
10
|
+
!!bsearch { |(x, y)|
|
11
|
+
c = a - x
|
12
|
+
c.zero? ? b - y : c
|
13
|
+
}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class ZenspiderGol
|
18
|
+
delta = [-1, 0, 1]
|
19
|
+
same = [0, 0]
|
20
|
+
|
21
|
+
DELTAS = (delta.product(delta) - [same]).sort
|
22
|
+
MIN = { true => 2, false => 3 }
|
23
|
+
|
24
|
+
@@neighbors = Hash.new { |h, k| h[k] = {} }
|
25
|
+
|
26
|
+
attr_accessor :cells
|
27
|
+
attr_accessor :cache
|
28
|
+
|
29
|
+
def initialize
|
30
|
+
self.cells = []
|
31
|
+
end
|
32
|
+
|
33
|
+
def randomize n, pct
|
34
|
+
m = ((n*n) * pct).to_i
|
35
|
+
dimensions = n.times.to_a
|
36
|
+
cells.replace dimensions.product(dimensions).sample(m).sort
|
37
|
+
end
|
38
|
+
|
39
|
+
def update
|
40
|
+
cells.replace considered.select { |(x, y)| alive? x, y }.sort
|
41
|
+
end
|
42
|
+
|
43
|
+
def considered
|
44
|
+
cells.map { |(x, y)| neighbors_for(x, y) }.flatten(1).uniq
|
45
|
+
end
|
46
|
+
|
47
|
+
def alive? x, y
|
48
|
+
count = (neighbors_for(x, y) & cells).size
|
49
|
+
min = MIN[cells.sorted_include? [x, y]]
|
50
|
+
count.between? min, 3
|
51
|
+
end
|
52
|
+
|
53
|
+
def neighbors_for x, y
|
54
|
+
@@neighbors[x][y] ||=
|
55
|
+
DELTAS.map { |(dx, dy)| [x+dx, y+dy] }.reject { |(m, n)| m < 0 || n < 0 }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class ZenspiderGolSimulation < Graphics::Simulation
|
60
|
+
attr_accessor :gol
|
61
|
+
|
62
|
+
SIZE, WIDTH = 10, 64
|
63
|
+
|
64
|
+
def initialize
|
65
|
+
super 640, 640, 16, "Conway's Game of Life"
|
66
|
+
|
67
|
+
self.gol = ZenspiderGol.new
|
68
|
+
gol.randomize WIDTH, 0.15
|
69
|
+
end
|
70
|
+
|
71
|
+
def draw n
|
72
|
+
clear
|
73
|
+
|
74
|
+
gol.cells.each do |(x, y)|
|
75
|
+
ellipse x*SIZE, y*SIZE, (SIZE-1)/2, (SIZE-1)/2, :white, :filled
|
76
|
+
end
|
77
|
+
|
78
|
+
fps n
|
79
|
+
end
|
80
|
+
|
81
|
+
def update n
|
82
|
+
gol.update.reject! { |(x, y)| x >= WIDTH || y >= WIDTH }
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
if ARGV.first == "prof" then
|
87
|
+
ZenspiderGolSimulation.new.run 5
|
88
|
+
else
|
89
|
+
ZenspiderGolSimulation.new.run
|
90
|
+
end
|
data/examples/zombies.rb
ADDED
@@ -0,0 +1,385 @@
|
|
1
|
+
#!/usr/local/bin/ruby -w
|
2
|
+
|
3
|
+
srand 42
|
4
|
+
|
5
|
+
require "graphics"
|
6
|
+
|
7
|
+
class Entity
|
8
|
+
attr_accessor :x, :y
|
9
|
+
attr_accessor :speed
|
10
|
+
attr_accessor :sim
|
11
|
+
|
12
|
+
@@colors = false
|
13
|
+
|
14
|
+
def initialize sim
|
15
|
+
self.sim = sim
|
16
|
+
self.speed = 5
|
17
|
+
|
18
|
+
self.x = rand(sim.w / sim.scale)
|
19
|
+
self.y = rand(sim.h / sim.scale)
|
20
|
+
end
|
21
|
+
|
22
|
+
def distance_from_squared p
|
23
|
+
dx = p.x - x
|
24
|
+
dy = p.y - y
|
25
|
+
dx * dx + dy * dy
|
26
|
+
end
|
27
|
+
|
28
|
+
VISIBILITY = 16
|
29
|
+
VIS_SQ = VISIBILITY * VISIBILITY
|
30
|
+
|
31
|
+
def near? p
|
32
|
+
distance_from_squared(p) < VIS_SQ
|
33
|
+
end
|
34
|
+
|
35
|
+
def touching? p
|
36
|
+
distance_from_squared(p) < 4 # 2 * 2
|
37
|
+
end
|
38
|
+
|
39
|
+
def partition
|
40
|
+
(x / sim.width_of_partition) + sim.side * (y / sim.width_of_partition)
|
41
|
+
end
|
42
|
+
|
43
|
+
def random_walk
|
44
|
+
self.x += rand(speed)-speed/2
|
45
|
+
self.y += rand(speed)-speed/2
|
46
|
+
end
|
47
|
+
|
48
|
+
def max
|
49
|
+
@@max ||= sim.max
|
50
|
+
end
|
51
|
+
|
52
|
+
def limit_bounds
|
53
|
+
self.x = 0 if x < 0
|
54
|
+
self.x = max - 1 if x >= max
|
55
|
+
self.y = 0 if y < 0
|
56
|
+
self.y = max - 1 if y >= max
|
57
|
+
end
|
58
|
+
|
59
|
+
def move_towards entity
|
60
|
+
self.x += (entity.x - x) <=> 0
|
61
|
+
self.y += (entity.y - y) <=> 0
|
62
|
+
limit_bounds
|
63
|
+
end
|
64
|
+
|
65
|
+
def draw
|
66
|
+
raise "subclass responsibility"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class Person < Entity
|
71
|
+
COUNT = 750
|
72
|
+
|
73
|
+
NORMAL = 1
|
74
|
+
FREAKD = 2
|
75
|
+
INFECT = 3
|
76
|
+
|
77
|
+
INFECT_STEPS = 50.0 # must be a float
|
78
|
+
|
79
|
+
NORMAL_COLOR = :blue
|
80
|
+
FREAKD_COLOR = :yellow
|
81
|
+
|
82
|
+
attr_accessor :state, :infect, :speed
|
83
|
+
|
84
|
+
def initialize sim, state = NORMAL
|
85
|
+
super(sim)
|
86
|
+
|
87
|
+
self.state = state
|
88
|
+
self.speed = 5
|
89
|
+
self.infect = nil
|
90
|
+
|
91
|
+
initialize_colors unless @@colors
|
92
|
+
end
|
93
|
+
|
94
|
+
def initialize_colors
|
95
|
+
@@colors = true
|
96
|
+
|
97
|
+
INFECT_STEPS.to_i.times do |n|
|
98
|
+
r = (255 * ((INFECT_STEPS - n) / INFECT_STEPS)).to_i
|
99
|
+
g = (192 * (n / INFECT_STEPS)).to_i
|
100
|
+
b = 0
|
101
|
+
sim.register_color "infect#{n}", r, g, b
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def infected?
|
106
|
+
state == INFECT
|
107
|
+
end
|
108
|
+
|
109
|
+
def freaked?
|
110
|
+
state == FREAKD
|
111
|
+
end
|
112
|
+
|
113
|
+
def color
|
114
|
+
case state
|
115
|
+
when NORMAL
|
116
|
+
NORMAL_COLOR
|
117
|
+
when FREAKD
|
118
|
+
FREAKD_COLOR
|
119
|
+
when INFECT
|
120
|
+
"infect#{INFECT_STEPS.to_i - infect}"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def draw
|
125
|
+
sim.fast_rect x*2, y*2, 2, 2, color
|
126
|
+
end
|
127
|
+
|
128
|
+
def update_infection
|
129
|
+
if @infect then
|
130
|
+
@infect -= 1
|
131
|
+
sim.zombie << Zombie.from_person(sim.person.delete(self), sim) if @infect <= 0
|
132
|
+
true
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def visible
|
137
|
+
sim.part_z[partition].find_all { |p| self.near? p }
|
138
|
+
end
|
139
|
+
|
140
|
+
def nearest
|
141
|
+
visible.sort_by { |p| self.distance_from_squared p }.first
|
142
|
+
end
|
143
|
+
|
144
|
+
def update i
|
145
|
+
return if update_infection
|
146
|
+
random_walk
|
147
|
+
limit_bounds
|
148
|
+
|
149
|
+
nearest = self.nearest
|
150
|
+
|
151
|
+
unless nearest then
|
152
|
+
if state == FREAKD then
|
153
|
+
self.state = NORMAL
|
154
|
+
self.speed = 5
|
155
|
+
end
|
156
|
+
else
|
157
|
+
unless touching? nearest then
|
158
|
+
if state == NORMAL then
|
159
|
+
self.state = FREAKD
|
160
|
+
self.speed = 9
|
161
|
+
end
|
162
|
+
else
|
163
|
+
self.state = INFECT
|
164
|
+
self.infect = INFECT_STEPS.to_i
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def kill
|
170
|
+
sim.person.delete self
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
class Hunter < Person
|
175
|
+
COUNT = 6
|
176
|
+
COLOR = :white
|
177
|
+
|
178
|
+
def color
|
179
|
+
if @infect then
|
180
|
+
"infect#{INFECT_STEPS.to_i - infect}"
|
181
|
+
else
|
182
|
+
COLOR
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def update i
|
187
|
+
return if update_infection
|
188
|
+
random_walk
|
189
|
+
limit_bounds
|
190
|
+
|
191
|
+
baddies = sim.zombie + sim.person.select(&:infect)
|
192
|
+
nearest = baddies.sort_by { |z| self.distance_from_squared z }.first
|
193
|
+
if nearest then
|
194
|
+
if self.touching? nearest then
|
195
|
+
if Person === nearest then
|
196
|
+
nearest.kill
|
197
|
+
else
|
198
|
+
if rand(10) != 0 then
|
199
|
+
nearest.kill
|
200
|
+
else
|
201
|
+
self.state = INFECT
|
202
|
+
self.infect = INFECT_STEPS.to_i
|
203
|
+
end
|
204
|
+
end
|
205
|
+
elsif near? nearest then
|
206
|
+
move_towards nearest
|
207
|
+
else
|
208
|
+
move_towards nearest
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def draw
|
214
|
+
sim.circle x*2, y*2, VISIBILITY, color
|
215
|
+
super
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
class Zombie < Entity
|
220
|
+
COUNT = 5
|
221
|
+
ZOMBIE_COLOR = :red
|
222
|
+
|
223
|
+
def self.from_person p, sim
|
224
|
+
z = new sim
|
225
|
+
z.x = p.x
|
226
|
+
z.y = p.y
|
227
|
+
z
|
228
|
+
end
|
229
|
+
|
230
|
+
def initialize sim
|
231
|
+
super
|
232
|
+
self.speed = 3
|
233
|
+
end
|
234
|
+
|
235
|
+
def draw
|
236
|
+
sim.fast_rect x*2, y*2, 2, 2, ZOMBIE_COLOR
|
237
|
+
end
|
238
|
+
|
239
|
+
def visible
|
240
|
+
sim.part_p[partition].find_all { |p| Hunter === p || p.freaked? }
|
241
|
+
end
|
242
|
+
|
243
|
+
def nearest
|
244
|
+
visible.sort_by { |p| self.distance_from_squared p }.first
|
245
|
+
end
|
246
|
+
|
247
|
+
def update i
|
248
|
+
nearest = self.nearest
|
249
|
+
|
250
|
+
if nearest then
|
251
|
+
move_towards nearest
|
252
|
+
else
|
253
|
+
random_walk
|
254
|
+
end
|
255
|
+
|
256
|
+
limit_bounds
|
257
|
+
end
|
258
|
+
|
259
|
+
def kill
|
260
|
+
sim.zombie.delete self
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
class ZombieGame < Graphics::Simulation
|
265
|
+
attr_accessor :person, :zombie
|
266
|
+
attr_accessor :part_p, :part_z
|
267
|
+
attr_accessor :scale, :partitions
|
268
|
+
attr_accessor :start
|
269
|
+
|
270
|
+
def initialize
|
271
|
+
super 512, 512, 16, "Zombie Epidemic Simulator"
|
272
|
+
self.scale = 2
|
273
|
+
self.partitions = 64
|
274
|
+
|
275
|
+
self.part_p = Array.new(partitions) do [] end
|
276
|
+
self.part_z = Array.new(partitions) do [] end
|
277
|
+
|
278
|
+
self.person = []
|
279
|
+
self.zombie = []
|
280
|
+
|
281
|
+
populate Person, person
|
282
|
+
populate Zombie, zombie
|
283
|
+
populate Hunter, person
|
284
|
+
|
285
|
+
self.start = Time.now
|
286
|
+
end
|
287
|
+
|
288
|
+
def draw tick
|
289
|
+
clear
|
290
|
+
|
291
|
+
person.each do |p|
|
292
|
+
p.draw
|
293
|
+
end
|
294
|
+
|
295
|
+
zombie.each do |p|
|
296
|
+
p.draw
|
297
|
+
end
|
298
|
+
|
299
|
+
fps tick
|
300
|
+
end
|
301
|
+
|
302
|
+
def update i
|
303
|
+
partition_into person, part_p, partitions, side
|
304
|
+
partition_into zombie, part_z, partitions, side
|
305
|
+
|
306
|
+
person.each do |p|
|
307
|
+
p.update i
|
308
|
+
end
|
309
|
+
|
310
|
+
zombie.each do |p|
|
311
|
+
p.update i
|
312
|
+
end
|
313
|
+
|
314
|
+
if zombie.empty? or person.all?(&:infected?) then
|
315
|
+
t = Time.now - start
|
316
|
+
if zombie.empty? then
|
317
|
+
print "Infestation stopped "
|
318
|
+
else
|
319
|
+
print "All people infected "
|
320
|
+
end
|
321
|
+
puts "in #{i} iterations, #{t} sec"
|
322
|
+
puts " #{i / t} frames / sec"
|
323
|
+
|
324
|
+
exit
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
def populate klass, coll
|
329
|
+
klass::COUNT.times do
|
330
|
+
coll << klass.new(self)
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
# TODO: rename
|
335
|
+
def side
|
336
|
+
@side ||= Math.sqrt(partitions).to_i
|
337
|
+
end
|
338
|
+
|
339
|
+
def partition_into from, to, size, side
|
340
|
+
to.each(&:clear)
|
341
|
+
|
342
|
+
from.each do |p|
|
343
|
+
part = p.partition
|
344
|
+
|
345
|
+
# -3 or less - do nothing
|
346
|
+
# -2 - add 0
|
347
|
+
# -1 - add 0, 1
|
348
|
+
# 0 or more - add idx..idx+2
|
349
|
+
|
350
|
+
idx = part - side - 1
|
351
|
+
if idx >= 0 then
|
352
|
+
to[idx] << p
|
353
|
+
to[idx+1] << p
|
354
|
+
to[idx+2] << p
|
355
|
+
else
|
356
|
+
to[0] << p if idx >= -2
|
357
|
+
to[1] << p if idx >= -1
|
358
|
+
end
|
359
|
+
|
360
|
+
idx = part - 1
|
361
|
+
to[idx] << p if idx >= 0
|
362
|
+
idx += 1
|
363
|
+
to[idx] << p
|
364
|
+
idx += 1
|
365
|
+
to[idx] << p if idx < size
|
366
|
+
|
367
|
+
idx = part + side - 1
|
368
|
+
to[idx] << p if idx < size
|
369
|
+
idx += 1
|
370
|
+
to[idx] << p if idx < size
|
371
|
+
idx += 1
|
372
|
+
to[idx] << p if idx < size
|
373
|
+
end
|
374
|
+
end
|
375
|
+
|
376
|
+
def width_of_partition
|
377
|
+
@width_of_partition ||= (w / scale) / side
|
378
|
+
end
|
379
|
+
|
380
|
+
def max
|
381
|
+
@max ||= w / scale
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
ZombieGame.new.run
|