redshift 1.3.17 → 1.3.18
Sign up to get free protection for your applications and to get access to all the features.
- data/.bnsignore +23 -0
- data/.gitignore +7 -6
- data/README +22 -4
- data/RELEASE-NOTES +16 -0
- data/examples/lotka-volterra.rb +1 -1
- data/examples/modular-component-def.rb +49 -0
- data/examples/robots/README +49 -0
- data/examples/robots/lib/base.rb +38 -0
- data/examples/robots/lib/explosion.rb +48 -0
- data/examples/robots/lib/missile.rb +75 -0
- data/examples/robots/lib/radar.rb +56 -0
- data/examples/robots/lib/robot.rb +105 -0
- data/examples/robots/lib/shell-world.rb +167 -0
- data/examples/robots/lib/tracker.rb +16 -0
- data/examples/robots/robots.rb +53 -0
- data/examples/shell.rb +1 -2
- data/ext/redshift/buffer/buffer.c +102 -0
- data/ext/redshift/buffer/buffer.h +17 -0
- data/ext/redshift/buffer/dir.rb +1 -0
- data/ext/redshift/buffer/extconf.rb +2 -0
- data/ext/redshift/util/isaac/extconf.rb +2 -0
- data/ext/redshift/util/isaac/isaac.c +129 -0
- data/ext/redshift/util/isaac/rand.c +140 -0
- data/ext/redshift/util/isaac/rand.h +61 -0
- data/lib/redshift/mixins/shell.rb +72 -0
- data/lib/redshift/redshift.rb +1 -1
- data/lib/redshift/syntax.rb +12 -5
- data/lib/redshift/target/c/component-gen.rb +11 -8
- data/lib/redshift/target/c/flow/buffer.rb +17 -126
- data/lib/redshift/target/c/flow/delay.rb +5 -5
- data/lib/redshift/target/c/library.rb +12 -1
- data/lib/redshift/util/histogram.rb +1 -1
- data/lib/redshift/util/irb-shell.rb +81 -0
- data/lib/redshift/util/isaac.rb +22 -0
- data/lib/redshift/util/modular.rb +48 -0
- data/lib/redshift/util/tkar-driver.rb +1 -2
- data/lib/redshift/util/tracer/var.rb +1 -1
- data/lib/redshift/world.rb +9 -3
- data/rakefile +9 -1
- data/test/test.rb +23 -9
- data/test/test_buffer.rb +1 -1
- data/test/test_flow_trans.rb +3 -20
- metadata +50 -46
- data/lib/redshift/mixins/irb-shell.rb +0 -151
data/.bnsignore
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# The list of files that should be ignored by Mr Bones.
|
2
|
+
# Lines that start with '#' are comments.
|
3
|
+
#
|
4
|
+
# A .gitignore file can be used instead by setting it as the ignore
|
5
|
+
# file in your Rakefile:
|
6
|
+
#
|
7
|
+
# PROJ.ignore_file = '.gitignore'
|
8
|
+
#
|
9
|
+
# For a project with a C extension, the following would be a good set of
|
10
|
+
# exclude patterns (uncomment them if you want to use them):
|
11
|
+
# *.[oa]
|
12
|
+
# *~
|
13
|
+
announcement.txt
|
14
|
+
coverage
|
15
|
+
doc
|
16
|
+
pkg
|
17
|
+
*.bck
|
18
|
+
misc
|
19
|
+
junk
|
20
|
+
tmp
|
21
|
+
*/tmp
|
22
|
+
*/*/tmp
|
23
|
+
*/junk
|
data/.gitignore
CHANGED
data/README
CHANGED
@@ -1,5 +1,23 @@
|
|
1
|
-
|
1
|
+
= RedShift
|
2
|
+
|
3
|
+
A framework for simulation of networks of hybrid automata, similar to SHIFT and Lambda-SHIFT. Includes ruby-based DSL for defining simulation components, and ruby/C code generation and runtime.
|
4
|
+
|
5
|
+
There's no documentation yet. Start with the examples.
|
6
|
+
|
7
|
+
== Requirements
|
8
|
+
|
9
|
+
RedShift needs ruby 1.8.x and a compatible C compiler. If you can build native gems, you're all set.
|
10
|
+
|
11
|
+
== Env vars
|
12
|
+
|
13
|
+
If you have a multicore system and are using the gnu toolchain, set
|
14
|
+
|
15
|
+
REDSHIFT_MAKE_ARGS='-j -l2'
|
16
|
+
|
17
|
+
or some variation. You'll find that rebuilds of your simulation code go faster.
|
18
|
+
|
19
|
+
----
|
20
|
+
|
21
|
+
Copyright (C) 2001-2010, Joel VanderWerf, vjoel@users.sourceforge.net
|
22
|
+
Distributed under the Ruby license. See www.ruby-lang.org.
|
2
23
|
|
3
|
-
ruby install.rb config
|
4
|
-
ruby install.rb setup
|
5
|
-
ruby install.rb install
|
data/RELEASE-NOTES
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
redshift 1.3.18
|
2
|
+
|
3
|
+
- added robots example
|
4
|
+
|
5
|
+
- redshift builds isaac and buffer code at install time, rather than run time
|
6
|
+
|
7
|
+
- added isaac extension and reverted tests to use it again
|
8
|
+
|
9
|
+
- use REDSHIFT_MAKE_ARGS
|
10
|
+
|
11
|
+
- added -j2 switch to test
|
12
|
+
|
13
|
+
- added modular.rb for modular use of redshift DSL, and an example
|
14
|
+
|
15
|
+
- some rdoc improvements
|
16
|
+
|
1
17
|
redshift 1.3.17
|
2
18
|
|
3
19
|
- added shell example with plotting and tk animation
|
data/examples/lotka-volterra.rb
CHANGED
@@ -24,7 +24,7 @@ require 'redshift/util/plot'
|
|
24
24
|
include Plot::PlotUtils
|
25
25
|
|
26
26
|
gnuplot do |plot|
|
27
|
-
plot.command %{set title "
|
27
|
+
plot.command %{set title "Lotka-Volterra"}
|
28
28
|
plot.command %{set xlabel "time"}
|
29
29
|
plot.add data, %{using 1:2 title "foxes" with lines}
|
30
30
|
plot.add data, %{using 1:3 title "rabbits" with lines}
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# This example shows how to define things in a module and include that in a
|
2
|
+
# Component or World class.
|
3
|
+
|
4
|
+
require 'redshift'
|
5
|
+
require 'redshift/util/modular'
|
6
|
+
|
7
|
+
module ThermoStuff
|
8
|
+
extend Modular
|
9
|
+
|
10
|
+
continuous :temp
|
11
|
+
|
12
|
+
state :Heat, :Off
|
13
|
+
|
14
|
+
flow :Heat do # Must quote constants, since they are defined later
|
15
|
+
diff "temp' = (68 - temp)/3"
|
16
|
+
end
|
17
|
+
|
18
|
+
flow :Off do
|
19
|
+
diff "temp' = (45 - temp)/10"
|
20
|
+
end
|
21
|
+
|
22
|
+
transition :Heat => :Off do
|
23
|
+
guard "temp > 68 - 0.1"
|
24
|
+
end
|
25
|
+
|
26
|
+
transition :Off => :Heat do
|
27
|
+
guard "temp < 66"
|
28
|
+
end
|
29
|
+
|
30
|
+
setup do
|
31
|
+
start :Off
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class Thermostat < RedShift::Component
|
36
|
+
include ThermoStuff
|
37
|
+
|
38
|
+
default do
|
39
|
+
self.temp = 35
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
world = RedShift::World.new
|
44
|
+
thermostat = world.create Thermostat
|
45
|
+
|
46
|
+
world.evolve 30 do |w|
|
47
|
+
printf "%6.2f: %7.3f %s\n", w.clock, thermostat.temp, thermostat.state
|
48
|
+
end
|
49
|
+
|
@@ -0,0 +1,49 @@
|
|
1
|
+
= Robots
|
2
|
+
|
3
|
+
An example covering some of the basic language features plus some techniques for interactive simulation and for animating simulation output.
|
4
|
+
|
5
|
+
To run the example:
|
6
|
+
|
7
|
+
ruby robots.rb
|
8
|
+
|
9
|
+
Press ^C to break into a command shell, and then ^D to continue the simulation run. The "help" command in the shell shows you the commands beyond the basic irb.
|
10
|
+
|
11
|
+
Use -h from the command line to see the switches.
|
12
|
+
|
13
|
+
To change the setup of the world, edit robots.rb. You can add and move robots and missles.
|
14
|
+
|
15
|
+
You can also move the objects interactively in the Tk visualization.
|
16
|
+
|
17
|
+
Currently, the robots move around and the missles track and hit them.
|
18
|
+
|
19
|
+
= To do
|
20
|
+
|
21
|
+
== robots physics
|
22
|
+
|
23
|
+
- collision detection and effect on robot health
|
24
|
+
|
25
|
+
- walls and obstacles
|
26
|
+
|
27
|
+
== robot control
|
28
|
+
|
29
|
+
- command language to evade attackers and launch missiles based on radar
|
30
|
+
|
31
|
+
- maybe similar to RoboTalk (from RoboWar)
|
32
|
+
|
33
|
+
- multirobot coordination using comm channels
|
34
|
+
|
35
|
+
== game mechanics
|
36
|
+
|
37
|
+
- robot shop
|
38
|
+
|
39
|
+
- tournaments
|
40
|
+
|
41
|
+
== dev tools
|
42
|
+
|
43
|
+
- debugger
|
44
|
+
|
45
|
+
- plotter
|
46
|
+
|
47
|
+
== visualization
|
48
|
+
|
49
|
+
- for each sensor, show nearest robot by drawing arrow
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# This file encapsulates some relatively uninteresting tweaks and additions
|
2
|
+
# to make the demo go smoothly.
|
3
|
+
|
4
|
+
require 'redshift'
|
5
|
+
|
6
|
+
class RobotComponent < RedShift::Component
|
7
|
+
attr_accessor :name # for display; can be anything
|
8
|
+
attr_reader :id # for keeping track of tk objects; must be uniq int
|
9
|
+
|
10
|
+
default do
|
11
|
+
@id = world.next_id
|
12
|
+
@name = id
|
13
|
+
end
|
14
|
+
|
15
|
+
def tk_delete
|
16
|
+
"delete #{id}"
|
17
|
+
end
|
18
|
+
|
19
|
+
state :Done
|
20
|
+
|
21
|
+
transition Done => Exit do
|
22
|
+
action do
|
23
|
+
world.exited << self
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
require 'shell-world'
|
29
|
+
require 'robot'
|
30
|
+
|
31
|
+
class RobotWorld < RedShift::World
|
32
|
+
include ShellWorld
|
33
|
+
|
34
|
+
def show_status
|
35
|
+
grep(Robot) {|r| r.show_status}
|
36
|
+
nil
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
class Explosion < RobotComponent
|
2
|
+
continuous :size, :max_size, :x, :y, :vx, :vy
|
3
|
+
continuous :t, :prev_t
|
4
|
+
|
5
|
+
setup do
|
6
|
+
self.t = self.prev_t = 0
|
7
|
+
end
|
8
|
+
|
9
|
+
state :Expanding
|
10
|
+
start Expanding
|
11
|
+
|
12
|
+
flow Expanding do
|
13
|
+
diff " x' = vx "
|
14
|
+
diff " y' = vy "
|
15
|
+
diff " size' = 10 * sqrt(size) "
|
16
|
+
diff " t' = 1 "
|
17
|
+
end
|
18
|
+
|
19
|
+
transition Expanding => Done do
|
20
|
+
guard " size > max_size "
|
21
|
+
end
|
22
|
+
|
23
|
+
transition Expanding => Expanding do
|
24
|
+
guard " t > prev_t "
|
25
|
+
action do
|
26
|
+
world.grep(Robot) do |r|
|
27
|
+
if (x - r.x)**2 + (y - r.y)**2 < size**2
|
28
|
+
r.messages <<
|
29
|
+
Robot::InflictDamage.new(10*(max_size - size)*(t - prev_t))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
reset :prev_t => "t"
|
34
|
+
end
|
35
|
+
|
36
|
+
def tk_add
|
37
|
+
"add explosion #{id} - 0 #{x} #{y} 0 #{size} #{-size}"
|
38
|
+
end
|
39
|
+
|
40
|
+
# emit string representing current state
|
41
|
+
def tk_update
|
42
|
+
fade = ("%x" % ((size / max_size) * 256)) * 2
|
43
|
+
"moveto #{id} #{x} #{y}\n" +
|
44
|
+
"param #{id} 0 #{size}\n" +
|
45
|
+
"param #{id} 1 #{-size}\n" +
|
46
|
+
"param #{id} 2 0xFF#{fade}"
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
class Missile < RobotComponent
|
2
|
+
require 'explosion'
|
3
|
+
|
4
|
+
link :target => Robot
|
5
|
+
|
6
|
+
continuous :x, :y, :vx, :vy, :v, :heading, :power
|
7
|
+
constant :turn_rate
|
8
|
+
|
9
|
+
state :Seeking
|
10
|
+
start Seeking
|
11
|
+
|
12
|
+
default do
|
13
|
+
self.power = 40
|
14
|
+
self.v = 20
|
15
|
+
self.turn_rate = 60 * Math::PI / 180 # convert from degrees per sec
|
16
|
+
end
|
17
|
+
|
18
|
+
flow Seeking do
|
19
|
+
alg " vx = v * cos(heading) "
|
20
|
+
alg " vy = v * sin(heading) "
|
21
|
+
|
22
|
+
diff " x' = vx "
|
23
|
+
diff " y' = vy "
|
24
|
+
|
25
|
+
diff "power' = -1"
|
26
|
+
|
27
|
+
alg " distance =
|
28
|
+
sqrt(
|
29
|
+
pow(x - target.x, 2) +
|
30
|
+
pow(y - target.y, 2) ) "
|
31
|
+
|
32
|
+
alg " angle =
|
33
|
+
atan2(target.y - y,
|
34
|
+
target.x - x) "
|
35
|
+
|
36
|
+
alg " error = fmod(heading - angle, 2*#{Math::PI}) "
|
37
|
+
diff " heading' = turn_rate * (
|
38
|
+
error < #{-Math::PI/2} ?
|
39
|
+
#{-Math::PI/2} :
|
40
|
+
(error > #{Math::PI/2} ?
|
41
|
+
#{Math::PI/2} : -error)) "
|
42
|
+
end
|
43
|
+
|
44
|
+
transition Seeking => Done do
|
45
|
+
guard "power < 0"
|
46
|
+
end
|
47
|
+
|
48
|
+
transition Seeking => Done do
|
49
|
+
guard "distance < 1"
|
50
|
+
action do
|
51
|
+
unless target.state == Exit
|
52
|
+
create Explosion do |e|
|
53
|
+
e.size = 1
|
54
|
+
e.max_size = power
|
55
|
+
e.x = x
|
56
|
+
e.y = y
|
57
|
+
e.vx = vx
|
58
|
+
e.vy = vy
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# return string that adds an instance of the shape with label t.name,
|
65
|
+
# position based on (x, y), and rotation based on heading
|
66
|
+
def tk_add
|
67
|
+
"add missile #{id} - 11 #{x} #{y} #{heading}"
|
68
|
+
end
|
69
|
+
|
70
|
+
# emit string representing current state
|
71
|
+
def tk_update
|
72
|
+
"moveto #{id} #{x} #{y}\n" +
|
73
|
+
"rot #{id} #{heading}"
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'tracker'
|
2
|
+
|
3
|
+
class Radar < RobotComponent
|
4
|
+
constant :period => 0.200
|
5
|
+
continuous :t
|
6
|
+
continuous :distance
|
7
|
+
continuous :angle
|
8
|
+
attr_accessor :host_robot, :nearest_robot
|
9
|
+
link :nearest_tracker => Tracker
|
10
|
+
|
11
|
+
attr_accessor :trackers
|
12
|
+
default do
|
13
|
+
self.trackers = []
|
14
|
+
end
|
15
|
+
|
16
|
+
# add +robots+ to the list of objects tracked by this radar
|
17
|
+
def track_robots *robots
|
18
|
+
robots.flatten.each do |robot|
|
19
|
+
tracker = create Tracker
|
20
|
+
tracker.host = host_robot
|
21
|
+
tracker.target = robot
|
22
|
+
trackers << tracker
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
state :Sleep, :Measure
|
27
|
+
start Sleep
|
28
|
+
|
29
|
+
flow Sleep do
|
30
|
+
diff " t' = -1 "
|
31
|
+
end
|
32
|
+
|
33
|
+
flow Sleep, Measure do
|
34
|
+
alg " angle_deg = fmod(angle * #{180/Math::PI}, 360)"
|
35
|
+
end
|
36
|
+
|
37
|
+
transition Sleep => Measure do
|
38
|
+
guard " t <= 0 "
|
39
|
+
action do
|
40
|
+
scan_for_nearest_robot
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
transition Measure => Sleep do
|
45
|
+
reset :t => "period"
|
46
|
+
end
|
47
|
+
|
48
|
+
def scan_for_nearest_robot
|
49
|
+
self.nearest_tracker = trackers.min_by {|tracker| tracker.distance}
|
50
|
+
if nearest_tracker
|
51
|
+
self.nearest_robot = nearest_tracker.target ## how to delay this?
|
52
|
+
self.distance = nearest_tracker.distance
|
53
|
+
self.angle = nearest_tracker.angle
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
class Robot < RobotComponent
|
2
|
+
require 'radar'
|
3
|
+
require 'explosion'
|
4
|
+
require 'missile'
|
5
|
+
|
6
|
+
continuous :x, :y, :vx, :vy, :v, :heading, :power, :health
|
7
|
+
link :radar => :Radar
|
8
|
+
|
9
|
+
InflictDamage = Struct.new(:value)
|
10
|
+
queue :messages
|
11
|
+
|
12
|
+
default do
|
13
|
+
self.radar = create Radar
|
14
|
+
radar.host_robot = self
|
15
|
+
|
16
|
+
self.power = 100
|
17
|
+
self.health = 100
|
18
|
+
end
|
19
|
+
|
20
|
+
state :Stopped, :Rolling, :Exploding
|
21
|
+
start :Rolling
|
22
|
+
|
23
|
+
flow Stopped, Rolling do
|
24
|
+
alg " vx = v * cos(heading) "
|
25
|
+
alg " vy = v * sin(heading) "
|
26
|
+
|
27
|
+
alg " heading_deg = fmod(heading * #{180/Math::PI}, 360)"
|
28
|
+
|
29
|
+
diff " x' = vx "
|
30
|
+
diff " y' = vy "
|
31
|
+
|
32
|
+
# a bit of friction, i.e. deceleration proportional to velocity
|
33
|
+
diff " v' = -0.01 * v"
|
34
|
+
end
|
35
|
+
|
36
|
+
flow Rolling do
|
37
|
+
# it takes power to maintain speed
|
38
|
+
diff " power' = -1 "
|
39
|
+
end
|
40
|
+
|
41
|
+
transition Rolling => Stopped do
|
42
|
+
guard " power <= 0 "
|
43
|
+
end
|
44
|
+
|
45
|
+
transition Rolling => Exploding, Stopped => Exploding do
|
46
|
+
guard " health <= 0 "
|
47
|
+
action do
|
48
|
+
create Explosion do |e|
|
49
|
+
e.size = 1
|
50
|
+
e.max_size = 60
|
51
|
+
e.x = x
|
52
|
+
e.y = y
|
53
|
+
e.vx = vx
|
54
|
+
e.vy = vy
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
transition Exploding => Done
|
60
|
+
|
61
|
+
transition Rolling => Rolling, Stopped => Stopped do
|
62
|
+
wait :messages => InflictDamage
|
63
|
+
action do
|
64
|
+
m = messages.pop
|
65
|
+
case m
|
66
|
+
when RedShift::SimultaneousQueueEntries
|
67
|
+
dmg_msgs, other = m.partition {|n| InflictDamage === n}
|
68
|
+
messages.unpop other
|
69
|
+
dmg_msgs.each do |n|
|
70
|
+
self.health -= n.value
|
71
|
+
end
|
72
|
+
else
|
73
|
+
self.health -= m.value
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# return string that adds an instance of the shape with label t.name,
|
79
|
+
# position based on (x, y), and rotation based on heading
|
80
|
+
def tk_add
|
81
|
+
"add robot #{id} - 10 #{x} #{y} #{heading}\n" +
|
82
|
+
"param #{id} 0 #{name}\n" +
|
83
|
+
"param #{id} 1 359\n" +
|
84
|
+
"param #{id} 2 359"
|
85
|
+
end
|
86
|
+
|
87
|
+
# emit string representing current state
|
88
|
+
def tk_update
|
89
|
+
e = (power / 100.0) * 359.9
|
90
|
+
h = (health / 100.0) * 359.9
|
91
|
+
"moveto #{id} #{x} #{y}\n" +
|
92
|
+
"rot #{id} #{heading}\n" +
|
93
|
+
"param #{id} 1 #{e}\n" +
|
94
|
+
"param #{id} 2 #{h}"
|
95
|
+
end
|
96
|
+
|
97
|
+
def show_status
|
98
|
+
printf \
|
99
|
+
"%5.3f sec: closest blip is %s, at %8.3f meters, bearing %3d degrees\n",
|
100
|
+
world.clock,
|
101
|
+
radar.nearest_robot ? radar.nearest_robot.name : "none",
|
102
|
+
radar.distance,
|
103
|
+
(radar.angle * 180/Math::PI).round.abs
|
104
|
+
end
|
105
|
+
end
|