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 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
- It's for describing live coordinated events among people with different roles and across places.
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 through Groundcrew are actually CEML programs which are then executed on the server, resulting in a test message-, IM-, or mobile application-based coordination.
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 a bit like a cross between tcl and ruby. Every line starts with a command keyword, some arguments, and possibly a string after a colon. The terminal string parameter can be indented on the following line.
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.2.1
1
+ 0.3.0
@@ -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
- gather 2 agents within 1 block
3
- takes 45m
2
+ await 2 players within 1 block
4
3
 
5
- tell agents:
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
- after 30m
11
- ask agents re where:
9
+ give players 30m
10
+
11
+ ask players re breakfast_location:
12
12
  Where did you hide your breakfast?
13
- tell agents:
14
- Go find your breakfast at |otherguy.where|. Send back any reports.
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
- ask photographer re clothing: What are you wearing? (so the others can recognize you?)
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
- tell photographer:
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
- tell interviewers:
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
- tell reporters:
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/engine'
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
- p = ScriptsParser.new
20
- p.root = what
21
- result = p.parse(string)
22
- raise "parse failed: \n#{p.failure_reason}" unless result
23
- case what
24
- when :scripts
25
- raise "no scripts found" unless result.elements and !result.elements.empty?
26
- result = result.elements
27
- result.each{ |s| s.validate! }
28
- when :script
29
- result.validate!
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 Dummy
5
- def method_missing(meth, *args, &blk)
6
- # puts "#{meth}: #{args.to_s.inspect}"
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, delg = Dummy.new)
13
+ def initialize(script_text, id = rand(36**10).to_s(36))
19
14
  @script = CEML.parse(:script, script_text)
20
- @delg = delg
21
- @parts = {}
22
- @seq = {}
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
- parts[id] = obj.merge :roles => Set.new(roles)
24
+ players[id] = obj.merge :roles => Set.new(roles)
28
25
  end
29
26
 
30
27
  def run
31
- :loop while parts.keys.any? do |@current_id|
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
- @delg.send(*seq[pc] + [@current_id])
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
- parts.each do |key, thing|
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
- @delg.send :did_report
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::Engine.new(script)
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.parts[id]
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.parts[id]
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.parts[id][:received] = str
51
+ @e.players[id][:received] = str
52
52
  @e.run
53
- @e.parts[id][:received] = nil
53
+ @e.players[id][:received] = nil
54
54
  end
55
55
  end
@@ -1,9 +1,9 @@
1
1
  require 'ceml'
2
2
  require 'test/helper'
3
3
 
4
- class TestEngine < Test::Unit::TestCase
4
+ class TestIncident < Test::Unit::TestCase
5
5
 
6
- def test_engine
6
+ def test_incident
7
7
  play COMPLIMENT_SCRIPT
8
8
  player :joe, :organizer, :agent
9
9
  player :bill, :agent
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 2
8
- - 1
9
- version: 0.2.1
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-10-07 00:00:00 -07:00
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/engine.rb
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/test_engine.rb
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/test_engine.rb
106
+ - test/test_incident.rb
106
107
  - test/test_instructions.rb
107
108
  - test/test_scripts.rb