ceml 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +3 -3
- data/VERSION +1 -1
- data/beginners-guide.md +214 -0
- data/examples/breakfast-exchange.ceml +8 -7
- data/examples/citizen-investigation.ceml +7 -6
- data/lib/ceml.rb +26 -14
- data/lib/ceml/{engine.rb → incident.rb} +16 -22
- data/lib/ceml/script.rb +43 -0
- data/test/helper.rb +5 -5
- data/test/{test_engine.rb → test_incident.rb} +2 -2
- metadata +8 -7
data/README.markdown
CHANGED
@@ -3,9 +3,9 @@ Introducing CEML
|
|
3
3
|
|
4
4
|
CEML is the world's first programming language custom-built for bringing people together.
|
5
5
|
|
6
|
-
|
6
|
+
Short scripts define the structure for live coordinated events among people with different roles and across locations.
|
7
7
|
|
8
|
-
All assignments and reassignments given
|
8
|
+
All assignments and reassignments given in Groundcrew are CEML programs that are submitted via our API and executed on the server. Scripts can result in a text message, IM, twitter, and mobile application-based coordination.
|
9
9
|
|
10
10
|
A sample program
|
11
11
|
----------------
|
@@ -76,7 +76,7 @@ Also helps the user or the system pick which agents to involve, based on their s
|
|
76
76
|
Reference - Syntax
|
77
77
|
------------------
|
78
78
|
|
79
|
-
The syntax is simple and
|
79
|
+
The syntax is simple and inherits from tcl and ruby. Every line starts with a command keyword, some arguments, and possibly a string after a colon. Like RFC822 headers, a terminal string parameter can fall either directly after a colon or on a series of indented line.
|
80
80
|
|
81
81
|
Copyright
|
82
82
|
------------------
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.0
|
data/beginners-guide.md
ADDED
@@ -0,0 +1,214 @@
|
|
1
|
+
Beginners Guide to CEML
|
2
|
+
=======================
|
3
|
+
|
4
|
+
Thanks for taking an interest in CEML, the Coordinated Event Modeling Language. Using CEML you'll be able to get people to do whatever you want, whether it's evacuating a submarine, finding a gym buddy, helping people nearby, or just throwing a great party.
|
5
|
+
|
6
|
+
A CEML _script_ is a recipe for action. Just like a recipe, it has three parts: a title, a list of ingredients, and then a section that says what to do with the ingredients. Here's an example:
|
7
|
+
|
8
|
+
"Trade favorite colors"
|
9
|
+
gather 1 answerer and 1 listener within 50 feet
|
10
|
+
ask answerer re favorite_color:
|
11
|
+
What's your favorite color?
|
12
|
+
tell listener:
|
13
|
+
Someone nearby likes the color |answerer.favorite_color|. Find them.
|
14
|
+
|
15
|
+
In the example, the first line is the title, which is written in double quotes. The second line is the ingredients and how they are obtained. In CEML, the ingredients are usually people (although sometimes they can be places, things, or notes). The remaining lines are the instructions.
|
16
|
+
|
17
|
+
A good way to learn how flexible CEML is is to write the same script a different way. Then you can see which parts can change and which parts have to stay the same:
|
18
|
+
|
19
|
+
"Trade favorite colors"
|
20
|
+
gather a, b within 50ft
|
21
|
+
ask a re x: What's your favorite color?
|
22
|
+
tell b: Someone nearby likes the color |a.x|. Find them.
|
23
|
+
|
24
|
+
Whether you write a script compactly with short names for roles and answers or long names, it runs exactly the same.
|
25
|
+
|
26
|
+
Commands
|
27
|
+
--------
|
28
|
+
|
29
|
+
The above script only uses three CEML commands—*gather*, *ask*, and *tell*—but there aren't many more commands to learn. Only four more, actually: besides *gather*, the other ingredients commands are *await* and *nab*. Besides *ask* and *tell*, the other instructions commands are *assign* and *certify*.
|
30
|
+
|
31
|
+
As a sneak preview, here's an example that uses the other commands:
|
32
|
+
|
33
|
+
"Emergency Medicine"
|
34
|
+
await 1 concerned patient
|
35
|
+
nab a doctor within 5 miles
|
36
|
+
ask patient re problem: What's wrong?
|
37
|
+
assign doctor to patient: Attend to patient's |problem|.
|
38
|
+
certify doctor as responsive
|
39
|
+
|
40
|
+
Texts
|
41
|
+
-----
|
42
|
+
|
43
|
+
Commands like *ask*, *tell*, and *assign* contain texts which are delivered to the player who is the subject of the command. These texts appears after a colon, and can appear on the same or the following lines. If they appears on the following lines, they must be indented, as in the very first example above or the one following.
|
44
|
+
|
45
|
+
"Signups"
|
46
|
+
await 1 new signup
|
47
|
+
tell signup:
|
48
|
+
We are so glad
|
49
|
+
that you have
|
50
|
+
signed up.
|
51
|
+
|
52
|
+
Texts may also contain hyperlinks, which if the player has a smartphone with a web browser, will be openable.
|
53
|
+
|
54
|
+
"Hostility"
|
55
|
+
gather enemy, reporter within 1 block
|
56
|
+
ask reporter re clothing: What are you wearing?
|
57
|
+
assign enemy: Find someone wearing |clothing| and harass them.
|
58
|
+
assign reporter:
|
59
|
+
Someone will harass you. Notice how it feels.
|
60
|
+
Then fill out this google spreadsheet form
|
61
|
+
to describe your experience.
|
62
|
+
http://spreadsheet.google.com/foo
|
63
|
+
tell both: thanks so much for participating!
|
64
|
+
|
65
|
+
Roles
|
66
|
+
-----
|
67
|
+
|
68
|
+
In the examples so far, the words like 'enemy', 'reporter', 'answerer', 'listener', 'a', 'b', 'signup', 'patient', and 'doctor' are *role names*. Most of the time, a role name can be any word you like. They are just a placeholder to connect *casting commands* like *gather* and *await*, with *coordination commands* like *tell*.
|
69
|
+
|
70
|
+
Squads on Groundcrew, however, can define special meanings for certain roles. So on a particular squad, a 'doctor' might mean someone who's been tagged/certified with the tag 'doctor', and a patient might mean anyone else.
|
71
|
+
|
72
|
+
Some role names are always special. For instance, if you use the role name 'organizer', it always means someone who's an official organizer for that squad. And if you use the role names 'both', 'all', 'each', or 'everyone', it means that the instruction applies to everyone regardless of how they were *cast*.
|
73
|
+
|
74
|
+
"Get Help"
|
75
|
+
await concerned user
|
76
|
+
nab organizer
|
77
|
+
tell organizer: someone's not doing well.
|
78
|
+
|
79
|
+
When there is only one kind of role in a script, it is possible to omit the role name in instructional commands.
|
80
|
+
|
81
|
+
await 1 tired user
|
82
|
+
ask re cause: why are you tired?
|
83
|
+
tell: i hope you feel better
|
84
|
+
|
85
|
+
Social Information
|
86
|
+
------------------
|
87
|
+
|
88
|
+
Texts (see above) may contain *social information* that's inserted from answers by another player. To do this, we use a section surrounded by vertical bars (|). In the text that's sent to the player, this section will be replaced by the other player's answer.
|
89
|
+
|
90
|
+
"Empire"
|
91
|
+
gather emperor and 2-5 servants
|
92
|
+
ask emperor re tasks: What should your servants do today?
|
93
|
+
assign servants: The emperor has asked you to do |tasks|. Do them now.
|
94
|
+
|
95
|
+
In situations where there may be confusion about which answer you mean, you can specify a particular role by using a dot (.) in between the role and the answer names.
|
96
|
+
|
97
|
+
"Cheese Monte"
|
98
|
+
gather a,b,c
|
99
|
+
ask each re cheese: What kind of cheese do you have?
|
100
|
+
tell a: Someone near you has |c.cheese|. Take it!
|
101
|
+
tell b: Someone near you has |a.cheese|. Take it!
|
102
|
+
tell c: Someone near you has |b.cheese|. Take it!
|
103
|
+
|
104
|
+
Assign
|
105
|
+
------
|
106
|
+
|
107
|
+
It is probably clear what *ask* and *tell* do, but we haven't exactly covered *assign*. *Assign* gives a task which is expected to last a certain duration. While a *tell* command for a particular player completes immediately and goes on to the next command, an *assign* will wait for the player to say they've completed the task, and collect photo and textual reports as they do it. There are different representations for this depending on whether the player is connected to the script via text messaging, the iphone, or the mobile web.
|
108
|
+
|
109
|
+
A special form of *assign* will additionally direct the player to a person or place where they are supposed to go.
|
110
|
+
|
111
|
+
"Streetcorner Observation"
|
112
|
+
gather player, streetcorner within 5 blocks
|
113
|
+
assign player to streetcorner:
|
114
|
+
Go there and report what you observe.
|
115
|
+
|
116
|
+
On iPhone or mobile web, the player will get a little map to direct them. There are several special *role names* which are used to indicate places rather than people. These include "streetcorner", "park", "field", and "landmark".
|
117
|
+
|
118
|
+
Choosing Players: Await, Gather, and Nab.
|
119
|
+
----------------------------------------------
|
120
|
+
|
121
|
+
So what is really the difference between *await*, *gather*, and *nab*, you may be asking? Or perhaps you have already figured it out.
|
122
|
+
|
123
|
+
*Await* defines a kind of trigger—as soon as the conditions awaited for are met, the script will run.
|
124
|
+
|
125
|
+
await 2-5 level1 players within 50ft
|
126
|
+
assign: shout out "woo-hoo!"
|
127
|
+
certify as level2 not level1
|
128
|
+
|
129
|
+
Unless there's an await statement in your script, it will have to be executed manually by an organizer.
|
130
|
+
|
131
|
+
*Gather* issues invitations. It will keep issuing invitations and processing people's replies to them until it has the requisite number of players for its roles.
|
132
|
+
|
133
|
+
"Pick-up basketball"
|
134
|
+
gather 4-8 basketball players within 4 blocks
|
135
|
+
|
136
|
+
*Nab* just involves people, straight up, without asking their permission or issuing them an invitation.
|
137
|
+
|
138
|
+
By default, the people that triggered an *await* script are nabbed, not gathered. That means they will never receive an invitation. If you want to make sure they accept, you need an *await* line and a *gather* line for the same role name.
|
139
|
+
|
140
|
+
"A study about vomitting"
|
141
|
+
await 5 sick users
|
142
|
+
gather 2-5 users
|
143
|
+
ask re feeling: how do you feel?
|
144
|
+
|
145
|
+
Qualifying players
|
146
|
+
------------------
|
147
|
+
|
148
|
+
You may have noticed that some of the *await*, *gather*, and *nab* statements have adjectives or qualifiers before the role name. For instance, the word "sick" in "sick users" above is such a qualifier. So is "basketball", "level1", "tired", "concerned", and "new".
|
149
|
+
|
150
|
+
Some of these have special meanings: the qualifier *new* is automatically applied to new signups. So if your script has an *await* command that looks for someone new, it will automatically run on signup.
|
151
|
+
|
152
|
+
await 3 new signups
|
153
|
+
assign:
|
154
|
+
You have all just signed up!
|
155
|
+
Take a photo of something you
|
156
|
+
love and share with one another.
|
157
|
+
|
158
|
+
Another qualifier with a special meaning is *concerned*. This is applied to players who, because of their text messaging or their tweeting or their interaction with the iphone or web apps, seem like are confused or have an urgent question or issue.
|
159
|
+
|
160
|
+
The other qualifiers don't mean anythings special, but they select only players who have been certified or tagged with that particular word. So "gather 5-20 level1 users" will only invite users who have been tagged or certified as level1.
|
161
|
+
|
162
|
+
Certifying
|
163
|
+
----------
|
164
|
+
|
165
|
+
When a player has successfully completed their role in your script, you can add or change the tags associated with that player using the *certify* command.
|
166
|
+
|
167
|
+
await 3 new signups
|
168
|
+
assign:
|
169
|
+
You have all just signed up!
|
170
|
+
Take a photo of something you
|
171
|
+
love and share with one another.
|
172
|
+
certify as photo_sharing
|
173
|
+
|
174
|
+
Making Connections between Scripts
|
175
|
+
----------------------------------
|
176
|
+
|
177
|
+
*Certify* and qualifications can be used to link scripts together. The certification at the end of the first script can trigger the *await* statement in the next script. Here's an example:
|
178
|
+
|
179
|
+
await 20 photo_sharing users
|
180
|
+
ask re opinion:
|
181
|
+
How did you like sharing photos just now?
|
182
|
+
certify as signed_up not photo_sharing
|
183
|
+
|
184
|
+
Congratulations
|
185
|
+
---------------
|
186
|
+
|
187
|
+
You now know the basics of CEML and can start brainstorming your own scripts. If you don't have a squad already, contact us at Groundcrew and we'll get you set up.
|
188
|
+
|
189
|
+
We will also be creating a google group for CEML developers soon. Write info@groundcrew.us to make sure you're on it.
|
190
|
+
|
191
|
+
----
|
192
|
+
|
193
|
+
FAQ
|
194
|
+
----------------
|
195
|
+
|
196
|
+
### Do I need a title for every script?
|
197
|
+
|
198
|
+
You may have noticed that some of the script examples given in this guide do not have titles. Titles can be omitted under certain conditions. The main issue is that if a script has a *gather* statement then it MUST have a title. This is because the invitations that are issued contain the script title. Otherwise, titles are still often good to have: if the script is run manually it must have a title so that organizers can select it, and when users are running one of our smartphone clients like the iPhone app or the HTML5 webpage, they will see the title if there is one. The title will also be associated with any long-lasting media and reports that come from running the script.
|
199
|
+
|
200
|
+
### What if I want to share documents or notes in a script?
|
201
|
+
|
202
|
+
The February or March 2011 version of CEML will support passing and curating notes and documents between scripts. This will allow for a mix of flashmob and knowledge work-style coordination. Syntax is not finalized, but something like:
|
203
|
+
|
204
|
+
await designer
|
205
|
+
assign:
|
206
|
+
sketch out a design for something you
|
207
|
+
want to build and take a photo
|
208
|
+
document as designer_photo
|
209
|
+
certify designer_photo as unreviewed
|
210
|
+
|
211
|
+
await 6 reviewers and unreviewed designer_photo document
|
212
|
+
ask reviewers re opinion:
|
213
|
+
What do you think of this idea? |designer_photo|
|
214
|
+
certify designer_photo as reviewed not unreviewed
|
@@ -1,15 +1,16 @@
|
|
1
1
|
"a breakfast exchange"
|
2
|
-
|
3
|
-
takes 45m
|
2
|
+
await 2 players within 1 block
|
4
3
|
|
5
|
-
|
4
|
+
assign players:
|
6
5
|
Okay, you need to make breakfast for a stranger and hide it
|
7
6
|
somewhere public in town. Do it fast, cause in 30m I will ask
|
8
7
|
you where you hid it and tell you where to find yours.
|
9
8
|
|
10
|
-
|
11
|
-
|
9
|
+
give players 30m
|
10
|
+
|
11
|
+
ask players re breakfast_location:
|
12
12
|
Where did you hide your breakfast?
|
13
|
-
|
14
|
-
|
13
|
+
|
14
|
+
tell players:
|
15
|
+
Go find your breakfast at |each_other.breakfast_location|. Send back any reports.
|
15
16
|
Thanks for playing!
|
@@ -1,24 +1,25 @@
|
|
1
1
|
"a citizen investigation"
|
2
2
|
takes 20m
|
3
|
-
involves 1 photographer, 1-2 interviewers, 1-3 reporters, 1 topic, 1 location
|
4
|
-
|
5
3
|
offers connection inclusion democracy adventure
|
6
4
|
|
7
|
-
|
5
|
+
involves 1 photographer, 1-2 interviewers, 1-3 reporters, 1 topic, 1 location within 5 mi
|
6
|
+
|
7
|
+
ask photographer re clothing:
|
8
|
+
What are you wearing? (so the others can recognize you?)
|
8
9
|
|
9
|
-
|
10
|
+
assign photographer:
|
10
11
|
go to |location|. the other members of your team will find you.
|
11
12
|
research |topic| by taking photos and sending them back to this number.
|
12
13
|
do this for |duration|
|
13
14
|
|
14
15
|
|
15
|
-
|
16
|
+
assign interviewers:
|
16
17
|
go to |location| and find someone wearing |clothing|. that's your photographer.
|
17
18
|
your assignment is to interview people to research |topic|
|
18
19
|
do this for |duration|
|
19
20
|
|
20
21
|
|
21
|
-
|
22
|
+
assign reporters:
|
22
23
|
go to |location| and find someone wearing |clothing|. that's your photographer.
|
23
24
|
your assignment is to to research |topic| and send back textual reports
|
24
25
|
do this for |duration|. good luck!
|
data/lib/ceml.rb
CHANGED
@@ -9,27 +9,39 @@ require 'ceml/tt/casting'
|
|
9
9
|
require 'ceml/tt/instructions'
|
10
10
|
require 'ceml/tt/scripts'
|
11
11
|
|
12
|
-
require 'ceml/
|
12
|
+
require 'ceml/incident'
|
13
|
+
|
14
|
+
module CEML
|
15
|
+
extend self
|
16
|
+
attr_accessor :delegate
|
17
|
+
@delegate = Class.new{ def method_missing(meth, *args, &blk);end }.new
|
18
|
+
# puts "#{meth}: #{args.to_s.inspect}"
|
19
|
+
end
|
20
|
+
|
21
|
+
class CEML::ScriptsParser
|
22
|
+
def parse_with_root(string, root)
|
23
|
+
self.root = root
|
24
|
+
parse(string)
|
25
|
+
end
|
26
|
+
end
|
13
27
|
|
14
28
|
module CEML
|
15
29
|
def parse(what, string)
|
16
30
|
result = nil
|
17
31
|
string.gsub!(/\n +/, ' ')
|
18
32
|
string << "\n"
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
33
|
+
ScriptsParser.new.tap do |parser|
|
34
|
+
result = parser.parse_with_root(string, what)
|
35
|
+
raise "parse failed: \n#{parser.failure_reason}" unless result
|
36
|
+
case what
|
37
|
+
when :scripts
|
38
|
+
raise "no scripts found" unless result.elements and !result.elements.empty?
|
39
|
+
result = result.elements
|
40
|
+
result.each{ |s| s.validate! }
|
41
|
+
when :script
|
42
|
+
result.validate!
|
43
|
+
end
|
30
44
|
end
|
31
45
|
result
|
32
46
|
end
|
33
|
-
|
34
|
-
extend self
|
35
47
|
end
|
@@ -1,42 +1,40 @@
|
|
1
1
|
require 'set'
|
2
2
|
|
3
3
|
module CEML
|
4
|
-
class
|
5
|
-
|
6
|
-
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
|
-
class Engine
|
11
|
-
attr_reader :script, :parts
|
12
|
-
def this; @parts[@current_id]; end
|
4
|
+
class Incident
|
5
|
+
attr_reader :script
|
6
|
+
def this; players[@current_id]; end
|
13
7
|
def roles; this[:roles] ||= Set.new; end
|
14
8
|
def got; this[:received]; end
|
15
9
|
def recognized; this[:recognized]; end
|
16
10
|
def pc; this[:pc] ||= 0; end
|
11
|
+
def qs_answers; this[:qs_answers] ||= Hash.new; end
|
17
12
|
|
18
|
-
def initialize(script_text,
|
13
|
+
def initialize(script_text, id = rand(36**10).to_s(36))
|
19
14
|
@script = CEML.parse(:script, script_text)
|
20
|
-
@
|
21
|
-
|
22
|
-
|
15
|
+
@id = id
|
16
|
+
end
|
17
|
+
|
18
|
+
def players
|
19
|
+
@players ||= CEML.delegate.players(@id) || {}
|
23
20
|
end
|
24
21
|
|
25
22
|
def add(id, *roles)
|
26
23
|
obj = Hash === roles[-1] ? roles.pop : {}
|
27
|
-
|
24
|
+
players[id] = obj.merge :roles => Set.new(roles)
|
28
25
|
end
|
29
26
|
|
30
27
|
def run
|
31
|
-
:loop while
|
28
|
+
:loop while players.keys.any? do |@current_id|
|
32
29
|
# puts "trying: #{@current_id}: #{seq[pc]}"
|
33
30
|
next unless seq[pc] and send(*seq[pc])
|
34
|
-
|
31
|
+
CEML.delegate.send(*seq[pc] + [@iid, @current_id])
|
35
32
|
this[:pc]+=1
|
36
33
|
end
|
37
34
|
end
|
38
35
|
|
39
36
|
def seq
|
37
|
+
@seq ||= {}
|
40
38
|
@seq[roles] ||= begin
|
41
39
|
bytecode = [[:start]]
|
42
40
|
instrs = script.instructions_for(roles)
|
@@ -67,14 +65,10 @@ module CEML
|
|
67
65
|
this.merge! params
|
68
66
|
end
|
69
67
|
|
70
|
-
def qs_answers
|
71
|
-
this[:qs_answers] ||= Hash.new
|
72
|
-
end
|
73
|
-
|
74
68
|
def expand(role, var)
|
75
69
|
role = nil if role == 'otherguy'
|
76
70
|
role = role.to_sym if role
|
77
|
-
|
71
|
+
players.each do |key, thing|
|
78
72
|
next if key == @current_id
|
79
73
|
next if role and not thing[:roles].include? role
|
80
74
|
value = (thing[:qs_answers]||{})[var] and return value
|
@@ -121,7 +115,7 @@ module CEML
|
|
121
115
|
say :ok
|
122
116
|
true
|
123
117
|
else
|
124
|
-
|
118
|
+
CEML.delegate.send :did_report
|
125
119
|
false
|
126
120
|
end
|
127
121
|
end
|
data/lib/ceml/script.rb
CHANGED
@@ -30,6 +30,10 @@ module CEML
|
|
30
30
|
casting_statement.empty? ? nil : casting_statement
|
31
31
|
end
|
32
32
|
|
33
|
+
def max_to_invite
|
34
|
+
dramatis_personae ? dramatis_personae.max : 0
|
35
|
+
end
|
36
|
+
|
33
37
|
alias_method :dp, :dramatis_personae
|
34
38
|
|
35
39
|
def simple?
|
@@ -99,5 +103,44 @@ module CEML
|
|
99
103
|
def concludes_immediately?
|
100
104
|
!title and instructions.asks([:agents]).empty?
|
101
105
|
end
|
106
|
+
|
107
|
+
def color(odds)
|
108
|
+
return :red if odds < 0.1
|
109
|
+
return :yellow if odds < 0.4
|
110
|
+
return :green
|
111
|
+
end
|
112
|
+
|
113
|
+
def likelihood(yesprob, needed, psize)
|
114
|
+
return 1 if needed <= 0
|
115
|
+
return 0 if psize < needed
|
116
|
+
return (needed..psize).inject(0) do |memo, yes_count|
|
117
|
+
memo + begin
|
118
|
+
no_count = psize - yes_count
|
119
|
+
ways_it_could_happen = psize.choose(yes_count)
|
120
|
+
prob_of_each = (yesprob ** yes_count) * ((1 - yesprob) ** no_count)
|
121
|
+
ways_it_could_happen * prob_of_each
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def availabilities(potential_count, committed_count = 0)
|
127
|
+
return {} unless script.dramatis_personae
|
128
|
+
min = script.dramatis_personae.min
|
129
|
+
needed = min - committed_count
|
130
|
+
possible = potential_count + committed_count
|
131
|
+
yesprob = 0.7 # just make this up for now
|
132
|
+
odds = likelihood(yesprob, needed, potential_count)
|
133
|
+
|
134
|
+
{
|
135
|
+
:odds => odds, :color => color(odds),
|
136
|
+
:availability_counts => {
|
137
|
+
:total => possible,
|
138
|
+
:unknown => potential_count
|
139
|
+
},
|
140
|
+
:estimated_size => yesprob * potential_count + committed_count,
|
141
|
+
:needed => min
|
142
|
+
}
|
143
|
+
end
|
144
|
+
|
102
145
|
end
|
103
146
|
end
|
data/test/helper.rb
CHANGED
@@ -26,7 +26,7 @@ ENDOFSCRIPT
|
|
26
26
|
|
27
27
|
class Test::Unit::TestCase
|
28
28
|
def play script
|
29
|
-
@e = CEML::
|
29
|
+
@e = CEML::Incident.new(script)
|
30
30
|
end
|
31
31
|
|
32
32
|
def player id, *roles
|
@@ -35,21 +35,21 @@ class Test::Unit::TestCase
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def asked id, rx
|
38
|
-
p = @e.
|
38
|
+
p = @e.players[id]
|
39
39
|
assert_equal :ask, p[:said]
|
40
40
|
assert_match rx, p[:q]
|
41
41
|
p.delete :said
|
42
42
|
end
|
43
43
|
|
44
44
|
def told id, rx
|
45
|
-
p = @e.
|
45
|
+
p = @e.players[id]
|
46
46
|
assert_match rx, p[:msg]
|
47
47
|
p.delete :said
|
48
48
|
end
|
49
49
|
|
50
50
|
def says id, str
|
51
|
-
@e.
|
51
|
+
@e.players[id][:received] = str
|
52
52
|
@e.run
|
53
|
-
@e.
|
53
|
+
@e.players[id][:received] = nil
|
54
54
|
end
|
55
55
|
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
7
|
+
- 3
|
8
|
+
- 0
|
9
|
+
version: 0.3.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Joe Edelman
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-
|
17
|
+
date: 2010-12-24 00:00:00 -08:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -46,6 +46,7 @@ files:
|
|
46
46
|
- README.markdown
|
47
47
|
- Rakefile
|
48
48
|
- VERSION
|
49
|
+
- beginners-guide.md
|
49
50
|
- editors/CEML.tmbundle/Syntaxes/ceml.tmLanguage
|
50
51
|
- editors/CEML.tmbundle/info.plist
|
51
52
|
- examples/breakfast-exchange.ceml
|
@@ -53,7 +54,7 @@ files:
|
|
53
54
|
- examples/high-fives.ceml
|
54
55
|
- lib/ceml.rb
|
55
56
|
- lib/ceml/casting.rb
|
56
|
-
- lib/ceml/
|
57
|
+
- lib/ceml/incident.rb
|
57
58
|
- lib/ceml/instructions.rb
|
58
59
|
- lib/ceml/script.rb
|
59
60
|
- lib/ceml/tt/casting.rb
|
@@ -66,7 +67,7 @@ files:
|
|
66
67
|
- lib/ceml/tt/scripts.treetop
|
67
68
|
- test/helper.rb
|
68
69
|
- test/test_casting.rb
|
69
|
-
- test/
|
70
|
+
- test/test_incident.rb
|
70
71
|
- test/test_instructions.rb
|
71
72
|
- test/test_scripts.rb
|
72
73
|
has_rdoc: true
|
@@ -102,6 +103,6 @@ summary: a language for coordinating real world events
|
|
102
103
|
test_files:
|
103
104
|
- test/helper.rb
|
104
105
|
- test/test_casting.rb
|
105
|
-
- test/
|
106
|
+
- test/test_incident.rb
|
106
107
|
- test/test_instructions.rb
|
107
108
|
- test/test_scripts.rb
|