redshift 1.3.15
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/.gitignore +8 -0
- data/README +5 -0
- data/RELEASE-NOTES +455 -0
- data/TODO +431 -0
- data/bench/alg-state.rb +61 -0
- data/bench/bench +26 -0
- data/bench/bench.rb +10 -0
- data/bench/continuous.rb +76 -0
- data/bench/diff-bench +86 -0
- data/bench/discrete.rb +101 -0
- data/bench/euler.rb +50 -0
- data/bench/formula.rb +78 -0
- data/bench/half-strict.rb +103 -0
- data/bench/inertness.rb +116 -0
- data/bench/queue.rb +92 -0
- data/bench/run +66 -0
- data/bench/simple.rb +74 -0
- data/bench/strictness.rb +86 -0
- data/examples/ball-tkar.rb +72 -0
- data/examples/ball.rb +123 -0
- data/examples/collide.rb +70 -0
- data/examples/connect-parallel.rb +48 -0
- data/examples/connect.rb +109 -0
- data/examples/constants.rb +27 -0
- data/examples/delay.rb +80 -0
- data/examples/derivative.rb +77 -0
- data/examples/euler.rb +46 -0
- data/examples/external-lib.rb +33 -0
- data/examples/guard-debugger.rb +77 -0
- data/examples/lotka-volterra.rb +33 -0
- data/examples/persist-ball.rb +68 -0
- data/examples/pid.rb +87 -0
- data/examples/ports.rb +60 -0
- data/examples/queue.rb +56 -0
- data/examples/queue2.rb +98 -0
- data/examples/reset-with-event-val.rb +28 -0
- data/examples/scheduler.rb +104 -0
- data/examples/set-dest.rb +23 -0
- data/examples/simulink/README +1 -0
- data/examples/simulink/delay.mdl +827 -0
- data/examples/simulink/derivative.mdl +655 -0
- data/examples/step-discrete-profiler.rb +103 -0
- data/examples/subsystem.rb +109 -0
- data/examples/sync-deadlock.rb +32 -0
- data/examples/sync-queue.rb +91 -0
- data/examples/sync-retry.rb +20 -0
- data/examples/sync.rb +51 -0
- data/examples/thermostat.rb +53 -0
- data/examples/zeno.rb +53 -0
- data/lib/accessible-index.rb +47 -0
- data/lib/redshift.rb +1 -0
- data/lib/redshift/component.rb +412 -0
- data/lib/redshift/meta.rb +183 -0
- data/lib/redshift/mixins/zeno-debugger.rb +69 -0
- data/lib/redshift/port.rb +57 -0
- data/lib/redshift/queue.rb +104 -0
- data/lib/redshift/redshift.rb +111 -0
- data/lib/redshift/state.rb +31 -0
- data/lib/redshift/syntax.rb +558 -0
- data/lib/redshift/target/c.rb +37 -0
- data/lib/redshift/target/c/component-gen.rb +1303 -0
- data/lib/redshift/target/c/flow-gen.rb +325 -0
- data/lib/redshift/target/c/flow/algebraic.rb +85 -0
- data/lib/redshift/target/c/flow/buffer.rb +74 -0
- data/lib/redshift/target/c/flow/delay.rb +203 -0
- data/lib/redshift/target/c/flow/derivative.rb +101 -0
- data/lib/redshift/target/c/flow/euler.rb +67 -0
- data/lib/redshift/target/c/flow/expr.rb +113 -0
- data/lib/redshift/target/c/flow/rk4.rb +80 -0
- data/lib/redshift/target/c/library.rb +85 -0
- data/lib/redshift/target/c/world-gen.rb +1370 -0
- data/lib/redshift/target/spec.rb +34 -0
- data/lib/redshift/world.rb +300 -0
- data/rakefile +37 -0
- data/test/test.rb +52 -0
- data/test/test_buffer.rb +58 -0
- data/test/test_connect.rb +242 -0
- data/test/test_connect_parallel.rb +47 -0
- data/test/test_connect_strict.rb +135 -0
- data/test/test_constant.rb +74 -0
- data/test/test_delay.rb +145 -0
- data/test/test_derivative.rb +48 -0
- data/test/test_discrete.rb +592 -0
- data/test/test_discrete_isolated.rb +92 -0
- data/test/test_exit.rb +59 -0
- data/test/test_flow.rb +200 -0
- data/test/test_flow_link.rb +288 -0
- data/test/test_flow_sub.rb +100 -0
- data/test/test_flow_trans.rb +292 -0
- data/test/test_inherit.rb +127 -0
- data/test/test_inherit_event.rb +74 -0
- data/test/test_inherit_flow.rb +139 -0
- data/test/test_inherit_link.rb +65 -0
- data/test/test_inherit_setup.rb +56 -0
- data/test/test_inherit_state.rb +66 -0
- data/test/test_inherit_transition.rb +168 -0
- data/test/test_numerics.rb +34 -0
- data/test/test_queue.rb +90 -0
- data/test/test_queue_alone.rb +115 -0
- data/test/test_reset.rb +209 -0
- data/test/test_setup.rb +119 -0
- data/test/test_strict_continuity.rb +410 -0
- data/test/test_strict_reset_error.rb +30 -0
- data/test/test_strictness_error.rb +32 -0
- data/test/test_sync.rb +185 -0
- data/test/test_world.rb +328 -0
- metadata +204 -0
data/bench/inertness.rb
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'redshift'
|
2
|
+
require 'enumerator'
|
3
|
+
|
4
|
+
include RedShift
|
5
|
+
|
6
|
+
module Inertness
|
7
|
+
class Inert < Component
|
8
|
+
# An inert component is one that has no transitions out of its
|
9
|
+
# current state. However, flows are allowed. But for a sharper
|
10
|
+
# comparison, we don't define any.
|
11
|
+
#
|
12
|
+
# flow do
|
13
|
+
# diff " t' = 1 "
|
14
|
+
# end
|
15
|
+
|
16
|
+
# Adding just one trivial transition prevents the inert optimization,
|
17
|
+
# with dramatic effects (increase of 60% cpu time in first case).
|
18
|
+
#
|
19
|
+
# 1000 comps X 10000 steps X 0 non-inert: +0.60
|
20
|
+
# 1000 comps X 10000 steps X 1 non-inert: +0.59
|
21
|
+
#
|
22
|
+
# transition Enter => Exit do
|
23
|
+
# guard "0"
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# Turning off the inert optimization in the world source, without adding
|
27
|
+
# a transition, is a bit less dramatic, but a more realistic indicator
|
28
|
+
# of the value of the optimization:
|
29
|
+
#
|
30
|
+
# 1000 comps X 10000 steps X 0 non-inert: +0.39
|
31
|
+
# 1000 comps X 10000 steps X 1 non-inert: +0.36
|
32
|
+
end
|
33
|
+
|
34
|
+
class NonInert < Component
|
35
|
+
n_states = 10
|
36
|
+
# increasing this doesn't make the difference (w/ and w/o the inert
|
37
|
+
# optimization) larger because the inerts go on strict sleep anyway.
|
38
|
+
my_states = state((0...n_states).map {|i| "S#{i}"})
|
39
|
+
start S0
|
40
|
+
flow S0 do
|
41
|
+
diff " t' = 1 "
|
42
|
+
end
|
43
|
+
transition S0 => S1 do
|
44
|
+
guard " t >= 0.1 "
|
45
|
+
reset :t => 0
|
46
|
+
end
|
47
|
+
my_states[1..-1].each_cons(2) do |s, t|
|
48
|
+
transition s => t
|
49
|
+
end
|
50
|
+
transition my_states.last => my_states.first
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.make_world n_inert, n_non_inert=0
|
54
|
+
w = World.new
|
55
|
+
n_inert.times {w.create(Inert)}
|
56
|
+
n_non_inert.times {w.create(NonInert)}
|
57
|
+
w
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.do_bench
|
61
|
+
[0, 1].each do |n_non_inert|
|
62
|
+
[ [ 0, 10_000],
|
63
|
+
[ 1000, 10_000] ].each do
|
64
|
+
| n_inert, n_s|
|
65
|
+
|
66
|
+
w = make_world(n_inert, n_non_inert)
|
67
|
+
w.run 1 # warm up
|
68
|
+
r = bench do
|
69
|
+
w.run(n_s)
|
70
|
+
end
|
71
|
+
|
72
|
+
yield " - %10d comps X %10d steps X %10d non-inert: %8.2f" %
|
73
|
+
[n_inert, n_s, n_non_inert, r]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
if __FILE__ == $0
|
80
|
+
|
81
|
+
require File.join(File.dirname(__FILE__), 'bench')
|
82
|
+
puts "inert:"
|
83
|
+
Inertness.do_bench {|l| puts l}
|
84
|
+
|
85
|
+
exit
|
86
|
+
|
87
|
+
n_inert = 10000
|
88
|
+
n_non_inert = 0
|
89
|
+
n_steps = 1000
|
90
|
+
|
91
|
+
|
92
|
+
times = Process.times
|
93
|
+
t0 = Time.now
|
94
|
+
pt0 = times.utime #+ times.stime
|
95
|
+
|
96
|
+
w.run n_steps
|
97
|
+
|
98
|
+
times = Process.times
|
99
|
+
t1 = Time.now
|
100
|
+
pt1 = times.utime #+ times.stime
|
101
|
+
puts "process time: %8.2f" % (pt1-pt0)
|
102
|
+
puts "elapsed time: %8.2f" % (t1-t0)
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
__END__
|
107
|
+
|
108
|
+
The best case so far is:
|
109
|
+
|
110
|
+
without inert optimization
|
111
|
+
process time: 6.58
|
112
|
+
elapsed time: 6.59
|
113
|
+
|
114
|
+
with inert optimization
|
115
|
+
process time: 5.11
|
116
|
+
elapsed time: 5.12
|
data/bench/queue.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
# Measures performance of redshift queues.
|
2
|
+
|
3
|
+
require 'redshift'
|
4
|
+
|
5
|
+
module Queue
|
6
|
+
class Clock < RedShift::Component
|
7
|
+
# the only continuous var in the whole system
|
8
|
+
strictly_continuous :time
|
9
|
+
flow {
|
10
|
+
diff " time' = 1 "
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
class Sender < RedShift::Component
|
15
|
+
strict_link :clock => Clock
|
16
|
+
|
17
|
+
constant :next_wakeup
|
18
|
+
strictly_constant :period
|
19
|
+
|
20
|
+
# list of target queues that this comp will send to
|
21
|
+
def targets
|
22
|
+
@targets ||= []
|
23
|
+
end
|
24
|
+
|
25
|
+
setup do
|
26
|
+
self.next_wakeup = period
|
27
|
+
end
|
28
|
+
|
29
|
+
transition do
|
30
|
+
guard " clock.time >= next_wakeup "
|
31
|
+
reset :next_wakeup => " clock.time + period "
|
32
|
+
action do
|
33
|
+
targets.each do |target|
|
34
|
+
target << :awake
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class Receiver < RedShift::Component
|
41
|
+
queue :q
|
42
|
+
transition do
|
43
|
+
wait :q
|
44
|
+
action do
|
45
|
+
q.pop
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.make_world n_sender=1, n_receiver=0
|
51
|
+
w = RedShift::World.new
|
52
|
+
clock = w.create(Clock)
|
53
|
+
n_sender.times do |i|
|
54
|
+
sender = w.create(Sender) do |s|
|
55
|
+
s.clock = clock
|
56
|
+
s.next_wakeup = ((i % 99)+1) / 10.0
|
57
|
+
s.period = 10
|
58
|
+
n_receiver.times do
|
59
|
+
w.create(Receiver) do |r|
|
60
|
+
s.targets << r.q
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
w
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.do_bench
|
69
|
+
[1, 10, 100].each do |n_r|
|
70
|
+
[1, 10, 100].each do |n_s|
|
71
|
+
n_steps = 100_000 / (n_r * n_s)
|
72
|
+
do_bench_one(n_s, n_steps, n_r) {|r| yield r}
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.do_bench_one(n_s, n_steps, n_r)
|
78
|
+
w = make_world(n_s, n_r)
|
79
|
+
r = bench do
|
80
|
+
w.run(n_steps)
|
81
|
+
end
|
82
|
+
|
83
|
+
yield " - %10d senders X %10d steps X %5d receivers: %8.2f" %
|
84
|
+
[n_s, n_steps, n_r, r]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
if __FILE__ == $0
|
89
|
+
require File.join(File.dirname(__FILE__), 'bench')
|
90
|
+
puts "queue:"
|
91
|
+
Queue.do_bench_one(*ARGV.map{|s|s.to_i}) {|l| puts l}
|
92
|
+
end
|
data/bench/run
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'my-profile.rb'
|
4
|
+
|
5
|
+
class Object
|
6
|
+
def rbprofile prof_flag = true
|
7
|
+
if block_given?
|
8
|
+
$profiler.instance_eval do
|
9
|
+
save = @do_profiling
|
10
|
+
@do_profiling = prof_flag
|
11
|
+
yield
|
12
|
+
@do_profiling = save
|
13
|
+
end
|
14
|
+
else
|
15
|
+
$profiler.instance_eval {@do_profiling = prof_flag}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
bench_flag = ARGV.delete("b") || ARGV.delete("bench")
|
21
|
+
profile_flag = ARGV.delete("p") || ARGV.delete("profile")
|
22
|
+
rbprof_flag = ARGV.delete("rbprof")
|
23
|
+
|
24
|
+
prog_name = ARGV.shift
|
25
|
+
|
26
|
+
$REDSHIFT_CLIB_NAME = prog_name
|
27
|
+
|
28
|
+
case
|
29
|
+
when bench_flag
|
30
|
+
require prog_name; steps = $steps
|
31
|
+
require 'benchmark'
|
32
|
+
include Benchmark
|
33
|
+
bm(12) do |test|
|
34
|
+
for step in steps
|
35
|
+
test.report(step[0] + ":") do
|
36
|
+
step[1].call
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
when profile_flag
|
42
|
+
require prog_name; steps = $steps
|
43
|
+
for step in steps
|
44
|
+
# require 'profile' if step[0] == "run" # don't profile compilation
|
45
|
+
profile (step[0] == "run") do # only profile run, not compile or create
|
46
|
+
step[1].call
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
when rbprof_flag
|
51
|
+
require 'rbprof'; rbprofile false
|
52
|
+
require prog_name; steps = $steps
|
53
|
+
for step in steps
|
54
|
+
if step[0] == "run" # don't profile compilation
|
55
|
+
rbprofile {step[1].call}
|
56
|
+
else
|
57
|
+
step[1].call
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
else
|
62
|
+
require prog_name; steps = $steps
|
63
|
+
for step in steps
|
64
|
+
step[1].call
|
65
|
+
end
|
66
|
+
end
|
data/bench/simple.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'redshift'
|
2
|
+
|
3
|
+
class Thing < RedShift::Component
|
4
|
+
flow {
|
5
|
+
diff "w' = 1"
|
6
|
+
alg "ww = 2*w + (sin(w) + cos(w))*(sin(w) + cos(w))"
|
7
|
+
alg "www = ww-2"
|
8
|
+
diff "u' = www"
|
9
|
+
}
|
10
|
+
def inspect data = nil
|
11
|
+
d = "; #{data}" if data
|
12
|
+
vars = [
|
13
|
+
"w = #{w}"
|
14
|
+
]
|
15
|
+
super "#{vars.join(", ")}#{d}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class Base < RedShift::Component
|
20
|
+
flow {
|
21
|
+
diff "x' = 1"
|
22
|
+
}
|
23
|
+
def inspect data = nil
|
24
|
+
d = "; #{data}" if data
|
25
|
+
vars = [
|
26
|
+
"x = #{x}"
|
27
|
+
]
|
28
|
+
super "#{vars.join(", ")}#{d}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class Tester < Base
|
33
|
+
link :thing => Thing
|
34
|
+
flow {
|
35
|
+
alg "xx = 3*x"
|
36
|
+
diff "y' = xx"
|
37
|
+
diff "z' = y + 4*(thing.w+thing.u)"
|
38
|
+
}
|
39
|
+
|
40
|
+
def inspect data = nil
|
41
|
+
d = "; #{data}" if data
|
42
|
+
vars = [
|
43
|
+
"xx = #{xx}",
|
44
|
+
"y = #{y}",
|
45
|
+
"z = #{z}"
|
46
|
+
]
|
47
|
+
super "#{vars.join(", ")}#{d}"
|
48
|
+
end
|
49
|
+
|
50
|
+
setup do
|
51
|
+
self.thing = create Thing
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
#----------------------------------#
|
56
|
+
|
57
|
+
n_obj = 100
|
58
|
+
n_iter = 100_000
|
59
|
+
|
60
|
+
# 10, 100_000 ==> 8.87 seconds
|
61
|
+
|
62
|
+
world = nil
|
63
|
+
$steps = [
|
64
|
+
["commit", proc { world = RedShift::World.new {|w| w.time_step = 0.05} }],
|
65
|
+
["create", proc { n_obj.times do world.create Tester end }],
|
66
|
+
["run", proc { world.run n_iter }]
|
67
|
+
]
|
68
|
+
|
69
|
+
END {
|
70
|
+
# puts "time_step = #{world.time_step}"
|
71
|
+
# puts "clock = #{world.clock}"
|
72
|
+
# t = world.find { |c| c.is_a? Tester }
|
73
|
+
# p t
|
74
|
+
}
|
data/bench/strictness.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'my-profile'
|
2
|
+
|
3
|
+
$strict = ARGV.delete("-s")
|
4
|
+
|
5
|
+
# This makes a 5x difference!
|
6
|
+
if $strict
|
7
|
+
$REDSHIFT_CLIB_NAME = "strictness-on"
|
8
|
+
else
|
9
|
+
$REDSHIFT_CLIB_NAME = "strictness-off"
|
10
|
+
end
|
11
|
+
|
12
|
+
require 'redshift'
|
13
|
+
include RedShift
|
14
|
+
|
15
|
+
class SimpleComponent < Component
|
16
|
+
|
17
|
+
continuous :x
|
18
|
+
if $strict
|
19
|
+
strictly_continuous :y
|
20
|
+
strict_link :other => SimpleComponent
|
21
|
+
else
|
22
|
+
continuous :y
|
23
|
+
link :other => SimpleComponent
|
24
|
+
end
|
25
|
+
|
26
|
+
state :A, :B; default { start A; self.other = self }
|
27
|
+
|
28
|
+
flow A do
|
29
|
+
diff " y' = 1 + x " # y still strict even though x is not
|
30
|
+
end
|
31
|
+
|
32
|
+
5.times do
|
33
|
+
transition A => B do
|
34
|
+
guard " pow(y, 2) - sin(y) + cos(y) + other.y < 0 "
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
class ComplexComponent < Component
|
41
|
+
|
42
|
+
attr_accessor :start_value
|
43
|
+
|
44
|
+
state :A, :B, :C, :D, :E1, :F; default { start A }
|
45
|
+
|
46
|
+
flow A do
|
47
|
+
diff "t' = 1"
|
48
|
+
end
|
49
|
+
|
50
|
+
transition A => B do
|
51
|
+
guard "t > 1"
|
52
|
+
action do
|
53
|
+
if @start_value
|
54
|
+
self.t = @start_value
|
55
|
+
@start_value = nil
|
56
|
+
else
|
57
|
+
self.t = 0
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
transition B => C, C => D, D => E1, E1 => F, F => A
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
n = 1000
|
67
|
+
hz = 100
|
68
|
+
ts = 1.0/hz
|
69
|
+
w = World.new { |w| w.time_step = ts }
|
70
|
+
n.times do w.create SimpleComponent end
|
71
|
+
hz.times do |i|
|
72
|
+
cc = w.create ComplexComponent
|
73
|
+
cc.start_value = i*ts
|
74
|
+
end
|
75
|
+
|
76
|
+
times = Process.times
|
77
|
+
t0 = Time.now
|
78
|
+
pt0 = times.utime #+ times.stime
|
79
|
+
profile false do
|
80
|
+
w.run 1000
|
81
|
+
end
|
82
|
+
times = Process.times
|
83
|
+
t1 = Time.now
|
84
|
+
pt1 = times.utime #+ times.stime
|
85
|
+
puts "process time: %8.2f" % (pt1-pt0)
|
86
|
+
puts "elapsed time: %8.2f" % (t1-t0)
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# Example showing how to use tkar to animate a redshift simulation.
|
2
|
+
#
|
3
|
+
# Tkar can be found at http://path.berkeley.edu/~vjoel/vis/tkar
|
4
|
+
|
5
|
+
require 'redshift'
|
6
|
+
include RedShift
|
7
|
+
|
8
|
+
class Ball < Component
|
9
|
+
continuous :x, :y, :v
|
10
|
+
|
11
|
+
flow do
|
12
|
+
diff "y' = v"
|
13
|
+
diff "v' = -9.8"
|
14
|
+
end
|
15
|
+
|
16
|
+
transition do
|
17
|
+
guard "y - 5 < 0 && v < 0"
|
18
|
+
reset :v => "-v"
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_accessor :id # so we can keep track of which is which in animation
|
22
|
+
end
|
23
|
+
|
24
|
+
world = World.new
|
25
|
+
#world.time_step = 0.01
|
26
|
+
|
27
|
+
ball_count = 10
|
28
|
+
balls = Array.new(ball_count) do |id|
|
29
|
+
world.create(Ball) do |ball|
|
30
|
+
ball.x = id * 10
|
31
|
+
ball.y = rand(450) + 100
|
32
|
+
ball.v = 0
|
33
|
+
ball.id = id
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
begin
|
38
|
+
## need --quiet option?
|
39
|
+
IO.popen("tkar --flip -v", "w") do |tkar|
|
40
|
+
# --flip means positive y is up
|
41
|
+
tkar.puts %{
|
42
|
+
title Bouncing ball
|
43
|
+
background gray95
|
44
|
+
height 600
|
45
|
+
width 600
|
46
|
+
bounds -300 0 300 600
|
47
|
+
shape ball oval-5,-5,5,5,fc:darkgreen,oc:red
|
48
|
+
shape ground line*0,0,*1,0,fc:purple,wi:4
|
49
|
+
add ground 10000 - 10 0 0 0 -300 300
|
50
|
+
view_at 0 540
|
51
|
+
}
|
52
|
+
balls.each do |ball|
|
53
|
+
tkar.puts %{
|
54
|
+
add ball #{ball.id} - 100 #{ball.x} #{ball.y} 0
|
55
|
+
}
|
56
|
+
end
|
57
|
+
tkar.flush
|
58
|
+
world.evolve 1000 do
|
59
|
+
balls.each do |ball|
|
60
|
+
tkar.puts "move #{ball.id} #{ball.x} #{ball.y}"
|
61
|
+
end
|
62
|
+
tkar.puts "update"
|
63
|
+
tkar.flush
|
64
|
+
#sleep 0.01
|
65
|
+
## need timer to make this realistic
|
66
|
+
end
|
67
|
+
puts "Press <enter> when done"
|
68
|
+
gets
|
69
|
+
end
|
70
|
+
rescue Errno::EPIPE, Interrupt
|
71
|
+
puts "Exited."
|
72
|
+
end
|