ceml 0.7.13 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/Makefile +1 -1
  2. data/VERSION +1 -1
  3. data/ceml.gemspec +62 -50
  4. data/guide/guide.html +69 -14
  5. data/guide/guide.md +74 -15
  6. data/guide/guide.pdf +0 -0
  7. data/lib/ceml/driver.rb +0 -181
  8. data/lib/ceml/lang/basic_instruction.rb +49 -0
  9. data/lib/ceml/{casting_statement.rb → lang/casting_statement.rb} +19 -9
  10. data/lib/ceml/{instruction_statements.rb → lang/instruction_statements.rb} +5 -16
  11. data/lib/ceml/{script.rb → lang/script.rb} +53 -43
  12. data/lib/ceml/lang/tt/casting.rb +432 -0
  13. data/lib/ceml/lang/tt/casting.treetop +29 -0
  14. data/lib/ceml/lang/tt/instructions.rb +1130 -0
  15. data/lib/ceml/lang/tt/instructions.treetop +86 -0
  16. data/lib/ceml/lang/tt/lexer.rb +1804 -0
  17. data/lib/ceml/{tt → lang/tt}/lexer.treetop +70 -7
  18. data/lib/ceml/lang/tt/scripts.rb +647 -0
  19. data/lib/ceml/{tt → lang/tt}/scripts.treetop +2 -2
  20. data/lib/ceml/lang.rb +10 -0
  21. data/lib/ceml/models/audition.rb +65 -0
  22. data/lib/ceml/models/bundle.rb +64 -0
  23. data/lib/ceml/models/cast.rb +108 -0
  24. data/lib/ceml/models/castable.rb +81 -0
  25. data/lib/ceml/{incident.rb → models/incident.rb} +63 -15
  26. data/lib/ceml/models/incident_model.rb +100 -0
  27. data/lib/ceml/models/incident_role_slot.rb +16 -0
  28. data/lib/ceml/models/player.rb +80 -0
  29. data/lib/ceml/models/queue.rb +12 -0
  30. data/lib/ceml/models/waiting_room.rb +40 -0
  31. data/lib/ceml/models.rb +16 -0
  32. data/lib/ceml/processor.rb +162 -0
  33. data/lib/ceml.rb +7 -14
  34. data/test/askchain.ceml +6 -0
  35. data/test/compliment.ceml +4 -0
  36. data/test/dialogues/accept.ceml +24 -0
  37. data/test/dialogues/basic_seed.ceml +26 -0
  38. data/test/dialogues/jordan.ceml +121 -0
  39. data/test/helper.rb +44 -39
  40. data/test/jane.ceml +48 -0
  41. data/test/{test_casting.rb → lang/test_casting.rb} +5 -0
  42. data/test/lang/test_instructions.rb +42 -0
  43. data/test/{test_scripts.rb → lang/test_scripts.rb} +3 -2
  44. data/test/sync.ceml +6 -0
  45. data/test/test_castable.rb +20 -0
  46. data/test/test_dialogues.rb +58 -0
  47. data/test/test_incident.rb +64 -127
  48. metadata +54 -30
  49. data/.gitignore +0 -23
  50. data/lib/ceml/confluence.rb +0 -63
  51. data/lib/ceml/role.rb +0 -61
  52. data/lib/ceml/tt/casting.treetop +0 -65
  53. data/lib/ceml/tt/instructions.treetop +0 -91
  54. data/test/test_instructions.rb +0 -27
  55. data/test/test_release.rb +0 -78
@@ -0,0 +1,64 @@
1
+ module CEML
2
+ class Bundle < Struct.new(:id)
3
+ include Redis::Objects
4
+ value :castables, :marshal => true
5
+
6
+ def clear_from_all_rooms(player_id)
7
+ Audition.new(player_id).clear_from_all_rooms
8
+ end
9
+
10
+ def find_castables(stanza_name = nil)
11
+ return castables.value unless stanza_name
12
+ return [castables.value.find{ |c| c.stanza_name == stanza_name }]
13
+ end
14
+
15
+ def all_rooms
16
+ find_castables.map(&:all_rooms).flatten.uniq
17
+ end
18
+
19
+ def reset
20
+ all_rooms.each(&:clear)
21
+ end
22
+
23
+ def rooms_for_player(player, stanza_name = nil)
24
+ roomnames = find_castables(stanza_name).map{ |c| c.waiting_rooms_for_player(player, stanza_name) }.flatten.uniq
25
+ roomnames.map{ |r| WaitingRoom.new(r) }
26
+ end
27
+
28
+ def join_running_incident?(player, stanza_name = nil)
29
+ rooms_for_player(player, stanza_name).each do |room|
30
+ if incident_id = room.audition_for_incidents(player)
31
+ return incident_id
32
+ end
33
+ end
34
+ false
35
+ end
36
+
37
+ def cast_player?(player, stanza_name = nil)
38
+ incident_id = gen_code
39
+ find_castables(stanza_name).each do |castable|
40
+ if cast = castable.cast_player?(incident_id, player, stanza_name)
41
+ i = IncidentModel.new(incident_id)
42
+ i.bytecode.value = castable.bytecode
43
+ i.add_castings(cast.castings)
44
+ return incident_id
45
+ end
46
+ end
47
+ false
48
+ end
49
+
50
+ def absorb?(player, stanza_name = nil)
51
+ x = join_running_incident?(player, stanza_name)
52
+ puts "X1: #{x.inspect}"
53
+ return x if x
54
+ x = cast_player?(player, stanza_name)
55
+ puts "X2: #{x.inspect}"
56
+ x
57
+ end
58
+
59
+ def register_in_rooms(player, stanza_name = nil)
60
+ Audition.new("#{player[:id]}").list_in_rooms(rooms_for_player(player, stanza_name)) ##{gen_code}:
61
+ end
62
+
63
+ end
64
+ end
@@ -0,0 +1,108 @@
1
+ module CEML
2
+ RoleSpec = Struct.new :name, :tagspec, :range
3
+
4
+ # this contains the logic of building and approving a valid cast
5
+ # for an await statement, starting with a seed first_guy
6
+ class Cast
7
+ extend Forwardable
8
+ attr_reader :castings
9
+ def_delegators :@castable, :type, :timewindow, :radius, :matching, :roles
10
+ def_delegators :@castings, :[]
11
+ def initialize(castable, first_guy)
12
+ @castable = castable
13
+ @castings = Hash.new{ |h,k| h[k] = Set.new }
14
+ cast first_guy
15
+ end
16
+
17
+ def room(role)
18
+ casted_count = @castings[role.name].size
19
+ [role.range.max - casted_count, 0].max
20
+ end
21
+
22
+ def affinity role, guy
23
+ casted_count = @castings[role.name].size
24
+ needed = [role.range.min - casted_count, 0].max
25
+ allowed = [role.range.max - casted_count, 0].max
26
+
27
+ return [-1, -1, -1 ] unless role.tagspec =~ guy and allowed > 0
28
+ [ role.tagspec.with.size, -needed, -allowed ]
29
+ end
30
+
31
+ def <=>(other)
32
+ castings <=> other.castings
33
+ end
34
+
35
+ def cast guy
36
+ return if included? guy or not self =~ guy
37
+ best_role = roles.max_by{ |role| affinity(role, guy) }
38
+ return if affinity(best_role, guy)[0] == -1
39
+ @castings[best_role.name] << guy
40
+ guy[:roles] = best_role.name.to_sym
41
+ end
42
+
43
+ def included? guy
44
+ folks.any?{ |fellow| fellow[:id] == guy[:id] }
45
+ end
46
+
47
+ def launchable?
48
+ case type
49
+ when :accept
50
+ roles.any?{ |role| castings[role.name].size >= 1 }
51
+ when :await
52
+ roles.all?{ |role| castings[role.name].size >= role.range.min }
53
+ end
54
+ end
55
+
56
+ def folks
57
+ castings.values.map(&:to_a).flatten
58
+ end
59
+
60
+ def player_ids
61
+ folks.map{ |p| p[:id] }
62
+ end
63
+
64
+ def star
65
+ folks.first
66
+ end
67
+
68
+ def closes_at
69
+ return nil unless star && timewindow
70
+ star[:ts] + timewindow
71
+ end
72
+
73
+ def matchings
74
+ return {} unless star
75
+ Hash[ *matching.map{ |k| [k, star[:matchables][k]] } ]
76
+ end
77
+
78
+ def circle
79
+ return nil unless star && radius
80
+ llr = star[:lat] && star[:lng] && [star[:lat], star[:lng], radius]
81
+ ll && Circle.new(*llr)
82
+ end
83
+
84
+ def tagspecs
85
+ roles.map(&:tagspec).uniq
86
+ end
87
+
88
+ def =~(c)
89
+ (!closes_at or closes_at < Time.unix) and
90
+ (!circle or circle.contains?(c[:lat], c[:lng])) and
91
+ (matchings.each{ |k,v| c[:matchables][k] == v }) and
92
+ (tagspecs.any?{ |ts| ts =~ c })
93
+ end
94
+ end
95
+
96
+ class Circle < Struct.new :lat, :lng, :radius
97
+ def center; Geokit::LatLng(lat, lng); end
98
+ def contains?(*ll)
99
+ center.distance_to(Geokit::LatLng(*ll), :meters) <= radius
100
+ end
101
+ end
102
+
103
+ class Tagspec < Struct.new :with, :without
104
+ def =~(c)
105
+ with.all?{ |t| (c[:tags]||[]).include?(t) } and without.all?{ |t| !(c[:tags]||[]).include?(t) }
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,81 @@
1
+ module CEML
2
+ class Castable < Struct.new :type, :stanza_name, :matching, :radius, :timewindow, :roles, :bytecode
3
+
4
+ def cast_player?(incident_id, player, seeded=false)
5
+ room_ids = hot_waiting_rooms_given_player(player, seeded)
6
+ hotties = Audition.from_rooms(room_ids)
7
+ # puts "hotties are... #{hotties.inspect} from rooms #{room_ids.inspect}"
8
+ hot_players = hotties.keys.map{ |id| Player.new(id).data.value } + [player]
9
+ # puts "casting from #{hot_players.inspect}"
10
+ if cast = cast_from(hot_players)
11
+ puts "...cast with cast #{cast.player_ids.inspect}"
12
+ audition_ids = (cast.player_ids & hotties.keys).map{ |id| hotties[id] }
13
+ puts "consuming #{audition_ids.inspect}"
14
+ if Audition.consume(audition_ids, room_ids)
15
+ # post audition signs in waiting rooms for remaining parts
16
+ with_open_roles(cast) do |idx, role, count|
17
+ waiting_rooms_to_watch(role, cast).each do |room|
18
+ room.list_job(incident_id, idx, role.name, count)
19
+ end
20
+ end
21
+ return cast
22
+ else
23
+ sleep 0.02
24
+ return cast_player?(incident_id, player)
25
+ end
26
+ end
27
+ return
28
+ end
29
+
30
+ def waiting_rooms_for_player(player, seeded=false)
31
+ result = []
32
+ result.concat([*player[:seeded]]) if player[:seeded]
33
+ result.concat player[:tags] if player[:tags] unless seeded
34
+ # result << 'generic'
35
+ result
36
+ end
37
+
38
+ def hot_waiting_rooms_given_player(player, seeded=false)
39
+ rooms = waiting_rooms_for_player(player, seeded)
40
+ roles.each{ |r| rooms.concat(["#{stanza_name}:#{r.name}", *r.tagspec.with]) }
41
+ rooms << "#{stanza_name}:*"
42
+ # rooms << 'generic'
43
+ rooms.uniq
44
+ end
45
+
46
+ def waiting_rooms_to_watch(role, cast)
47
+ # skip for now: radius, timewindow, matching, general
48
+ result = []
49
+ if stanza_name
50
+ result << "#{stanza_name}:#{role.name}"
51
+ result << "#{stanza_name}:*"
52
+ end
53
+ if !role.tagspec.with.empty?
54
+ result.concat role.tagspec.with
55
+ end
56
+ # result << 'generic'
57
+ result.map{ |id| WaitingRoom.new(id) }
58
+ end
59
+
60
+ def all_rooms
61
+ roles.map{ |r| waiting_rooms_to_watch(r, {}) }.flatten
62
+ end
63
+
64
+ def with_open_roles(cast)
65
+ roles.each_with_index do |role, i|
66
+ count = cast.room(role)
67
+ next unless count > 0
68
+ yield i, role, count
69
+ end
70
+ end
71
+
72
+ # an O(n*^2) alg for now. can do much better
73
+ def cast_from guys
74
+ # see if we can build a cast out of them and bid on the casts
75
+ possible_casts = guys.map{ |guy| Cast.new self, guy }.select(&:star)
76
+ guys.each{ |guy| possible_casts.each{ |cast| cast.cast guy }}
77
+ result = possible_casts.detect(&:launchable?)
78
+ result
79
+ end
80
+ end
81
+ end
@@ -21,29 +21,50 @@ module CEML
21
21
  end
22
22
 
23
23
  def rolematch(specified_roles)
24
- expanded = roles.map{ |r| r == :agent ? [:agent, :agents] : r }.flatten.concat([:both, :all, :everyone])
25
- not (expanded & specified_roles).empty?
24
+ expanded = roles.map{ |r| r == :agent ? [:agent, :agents] : r }.flatten.concat([:both, :all, :everyone, :them, :either])
25
+ not (expanded & [*specified_roles]).empty?
26
26
  end
27
27
 
28
+ def log state
29
+ p = @this
30
+ instr = seq[pc]
31
+ guyroles = roles.to_a - [:everyone, :players, :them, :all, :either, :each, :agents, :both]
32
+ instr ||= []
33
+
34
+ puts "[#{id}] #{p[:id]}(#{guyroles}) ##{pc} #{state} -- #{instr[1]}/#{instr[0]} -- #{instr[2].inspect}"
35
+ end
36
+
37
+
28
38
  def run(players, &blk)
29
39
  @players = players
30
40
  @callback = blk
31
41
 
32
42
  loop do
33
- players = @players.select{ |p| !p[:released] }
34
- # puts "playing with #{players.map{|p|p[:id]}}"
43
+ players = @players
35
44
  advanced = false
36
45
  players.each do |p|
37
46
  @this = p
38
- # puts "trying #{@this[:id]}"
39
- next unless instr = seq[pc]
47
+ instr = seq[pc]
48
+ unless instr = seq[pc]
49
+ log 'off schedule'
50
+ @players.delete(p)
51
+ cb :released
52
+ next
53
+ end
40
54
  instr = instr.dup
41
- if not rolematch(instr.shift)
55
+ rolespec = instr.shift
56
+ if not rolematch(rolespec)
57
+ log "skipping[#{rolespec}]"
42
58
  this[:pc]+=1
43
59
  advanced = true
44
60
  else
45
61
  instr << role_info if instr.first == :start #tmp hack
46
- next unless send(*instr)
62
+ if send(*instr)
63
+ log 'completed'
64
+ else
65
+ log 'blocked'
66
+ next
67
+ end
47
68
  cb(*instr)
48
69
  this[:pc]+=1
49
70
  advanced = true
@@ -67,17 +88,22 @@ module CEML
67
88
  end
68
89
  end
69
90
 
91
+ def players_with_role(role)
92
+ if role
93
+ @players.select{ |p| p[:roles].include? role }
94
+ else
95
+ @players.reject{ |p| p == this }
96
+ end
97
+ end
70
98
 
71
99
  def expand(role, var)
72
100
  case role
73
- when 'his', 'her', 'their', 'my', 'its'; return qs_answers[var]
101
+ when 'his', 'her', 'their', 'my', 'its', 'your'; return qs_answers[var]
74
102
  when 'world', 'game', 'exercise', 'group'; return (cb :world, var)
75
103
  when 'somebody', 'someone', 'buddy', 'teammate'; role = nil
76
104
  end
77
105
  role = role.to_sym if role
78
- @players.each do |p|
79
- next if p == this
80
- next if role and not p[:roles].include? role
106
+ players_with_role(role).each do |p|
81
107
  value = (p[:qs_answers]||{})[var] and return value
82
108
  end
83
109
  nil
@@ -102,6 +128,16 @@ module CEML
102
128
  cb :said, params.merge(:said => x)
103
129
  end
104
130
 
131
+ def seed x
132
+ x[:rolemap].each do |pair|
133
+ if rolematch(pair[:from].to_sym)
134
+ cb :seeded, :target => x[:target], :role => pair[:to]
135
+ break
136
+ end
137
+ end
138
+ true
139
+ end
140
+
105
141
  def start(x); true; end
106
142
  def finish; true; end
107
143
 
@@ -116,9 +152,9 @@ module CEML
116
152
  true
117
153
  end
118
154
 
119
- def sync
155
+ def sync q
120
156
  this[:synced] = pc
121
- return true if @players.all?{ |p| p[:synced] == pc }
157
+ return true if players_with_role(q[:role]).all?{ |p| p[:synced] == pc }
122
158
  end
123
159
 
124
160
  def ask_q q
@@ -141,6 +177,12 @@ module CEML
141
177
  true
142
178
  end
143
179
 
180
+ def pick q
181
+ choices = q[:value].split(/\s+\-\s+/)
182
+ qs_answers[q[:key]] ||= choices.sort_by{ rand }.first
183
+ true
184
+ end
185
+
144
186
  def send_msg a
145
187
  text = interpolate(a[:text]) or return false
146
188
  say :message, :msg => text
@@ -180,8 +222,14 @@ module CEML
180
222
 
181
223
  def release x
182
224
  return true if x[:cond] and not expectation(*x[:cond])
183
- this[:released] = x[:tag]
225
+ @players.delete(this)
184
226
  cb :released, x[:tag]
227
+ true
228
+ end
229
+
230
+ def replace x
231
+ return true if x[:cond] and not expectation(*x[:cond])
232
+ # TODO: implement replace
185
233
  false
186
234
  end
187
235
 
@@ -0,0 +1,100 @@
1
+ class Hash
2
+ def like(*syms)
3
+ syms.inject({}) { |h, k| h[k] = self[k] if self[k]; h }
4
+ end
5
+ end
6
+
7
+ module CEML
8
+ PLAYER_THREAD_FIELDS = [ :pc, :continue_at, :synced ]
9
+ # :received, :recognized, :last_answer, :last_answer_recognized
10
+
11
+ class IncidentModel < Struct.new(:id)
12
+ include Redis::Objects
13
+ lock :running
14
+ value :bytecode, :marshal => true
15
+ value :data, :marshal => true
16
+ hash_key :player_roles, :marshal => true
17
+
18
+ def add_castings(castings)
19
+ castings.each do |rolename, folks|
20
+ folks.each do |player|
21
+ Player.new(player[:id]).touch(id)
22
+ player_roles[player[:id]] = [rolename.to_sym]
23
+ puts "ADDED(#{id}) #{player[:id]} = #{rolename.to_sym}"
24
+ end
25
+ end
26
+ end
27
+
28
+ def release(player_id)
29
+ puts "Releasing player #{player_id} from incident #{id}"
30
+ Player.new(player_id).clear_incident(id)
31
+ player_roles.delete(player_id)
32
+ end
33
+
34
+ def expire
35
+ # TODO: remove casting calls from waiting rooms
36
+ bytecode.clear
37
+ data.clear
38
+ end
39
+
40
+ def self.run_latest(cb_obj)
41
+ t = CEML.clock - 1
42
+ ids = redis.zrangebyscore 'ceml_continue_at', 0, t
43
+ ids.each{ |id| self.new(id).run(cb_obj) }
44
+ redis.zremrangebyscore 'ceml_continue_at', 0, t
45
+ end
46
+
47
+ def run(cb_obj)
48
+ raise unless String === id
49
+ # puts "RUNNING(#{id})"
50
+ # running_lock.lock do
51
+ metadata, player_data = *data.value
52
+ metadata ||= { :id => id }
53
+ player_data ||= {}
54
+ puts "[#{id}] Player data loaded: #{player_data.inspect}"
55
+ players = []
56
+
57
+ puts "[#{id}] Player roles: #{player_roles.all.inspect}"
58
+ player_roles.each do |player_id, roles|
59
+ puts "#{id}: #{player_id.inspect} => #{roles.inspect}, #{player_data[player_id].inspect}"
60
+ player = { :id => player_id, :roles => Set.new(roles) }
61
+ player[:roles] << :agents << :players << :both << :all << :each << :everyone << :them << :either
62
+ player.merge! player_data[player_id] if player_data[player_id]
63
+ p = Player.new(player_id)
64
+ stored_player = p.data.value
65
+ msg = p.message.value
66
+ player.merge! msg if msg
67
+ player.merge! stored_player if stored_player
68
+ players << player
69
+ end
70
+
71
+ CEML::Incident.new(bytecode.value, id).run(players) do |player, meth, what|
72
+ case meth.to_sym when :released, :finish, :replace
73
+ release(player[:id])
74
+ end
75
+ meth = "player_#{meth}"
76
+ # cb_obj.log "[#{id}] #{meth}: #{player[:id]} #{what.inspect}"
77
+ if cb_obj.respond_to? meth
78
+ metadata.update :player => player, :players => players, :id => id
79
+ result = cb_obj.send(meth, metadata, what)
80
+ metadata.delete :player
81
+ metadata.delete :players
82
+ result
83
+ end
84
+ end
85
+
86
+ players.each do |p|
87
+ Player.new(p[:id]).message.value = p.like(:received, :recognized, :situation)
88
+ player_data[p[:id]] = p.like *PLAYER_THREAD_FIELDS
89
+ end
90
+ puts "Player data saving: #{player_data.inspect}"
91
+ data.value = [metadata, player_data]
92
+
93
+ if next_run = players.map{ |p| p[:continue_at] }.compact.min
94
+ redis.zadd 'ceml_continue_at', next_run, id
95
+ end
96
+ # end
97
+ end
98
+
99
+ end
100
+ end
@@ -0,0 +1,16 @@
1
+ module CEML
2
+
3
+ class IncidentRoleSlot < Struct.new(:incident_id, :role, :max)
4
+ def id; "#{incident_id}:#{role}"; end
5
+ include Redis::Objects
6
+ counter :casted
7
+
8
+ def reserve_spot!
9
+ casted.incr{ |val| val <= max }
10
+ end
11
+
12
+ def full?
13
+ casted.value >= max
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,80 @@
1
+ module CEML
2
+ class Player < Struct.new(:id)
3
+ include Redis::Objects
4
+ lock :updating
5
+ value :data, :marshal => true
6
+ value :message, :marshal => true
7
+ sorted_set :current_incidents
8
+
9
+ def touch(incident_id)
10
+ current_incidents[incident_id] = Time.now.to_i
11
+ end
12
+
13
+ def reset
14
+ Audition.new(id).clear_from_all_rooms
15
+ data.clear
16
+ message.clear
17
+ current_incidents.clear
18
+ end
19
+
20
+ def top_incident_id
21
+ current_incidents.last
22
+ end
23
+
24
+ def top_incident
25
+ if iid = top_incident_id
26
+ IncidentModel.new(iid)
27
+ end
28
+ end
29
+
30
+ def clear_incident(id)
31
+ current_incidents.delete(id)
32
+ end
33
+
34
+ def self.update bundle_id, player, cb_obj, &blk
35
+ player[:bundle_id] = player[:squad_id] = bundle_id
36
+ new(player[:id].to_s).update player, cb_obj, &blk
37
+ end
38
+
39
+ def clear_answers
40
+ updating_lock.lock do
41
+ value = data.value || {}
42
+ value.delete(:qs_answers)
43
+ value.delete(:received)
44
+ data.value = value
45
+ end
46
+ end
47
+
48
+ MSG_PARAMS = [:received, :recognized, :situation]
49
+
50
+ def split player
51
+ new_message = player.like(*MSG_PARAMS)
52
+ MSG_PARAMS.each{ |p| player.delete(p) }
53
+ return new_message, player
54
+ end
55
+
56
+ def merge_new_player_data player
57
+ updating_lock.lock do
58
+ old_value = data.value || {}
59
+ new_value = old_value.merge player
60
+ new_value[:qs_answers] = (old_value[:qs_answers]||{}).merge(player[:qs_answers] || {})
61
+ # puts "SAVING DATA: #{new_value.inspect}"
62
+ data.value = new_value
63
+ end
64
+ end
65
+
66
+ def update player, cb_obj
67
+ player = player.dup
68
+ # puts "UPDATING player id #{id} with #{player.inspect}"
69
+ new_message, player = split(player)
70
+ merge_new_player_data(player)
71
+ cmd = new_message[:recognized]
72
+ if cmd and cb_obj.recognize_override(cmd, new_message, player, self)
73
+ message.delete
74
+ else
75
+ message.value = new_message
76
+ yield player if block_given?
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,12 @@
1
+ module CEML
2
+ class Queue
3
+ def id; 'ceml_q'; end
4
+ include Redis::Objects
5
+ list :calls, :marshal => true
6
+
7
+ def run(klass)
8
+ p = klass.new
9
+ while call = calls.shift; p.send(*call); end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,40 @@
1
+ module CEML
2
+
3
+ class WaitingRoom < Struct.new(:id)
4
+ include Redis::Objects
5
+ set :waiting_auditions
6
+ set :waiting_incident_roles
7
+
8
+ def clear
9
+ waiting_auditions.each do |audition_id|
10
+ Audition.new(audition_id).clear_from_all_rooms(id)
11
+ end
12
+ waiting_incident_roles.clear
13
+ end
14
+
15
+ def audition_for_incidents(player)
16
+ # puts "auditioning #{player[:id]} for incidents in room #{id}"
17
+ waiting_incident_roles.members.each do |key|
18
+ incident_id, idx, role, count = *key.split(':')
19
+ # puts "checking against #{incident_id}: #{role}"
20
+ count = count.to_i
21
+ role_slot = IncidentRoleSlot.new(incident_id, role, count)
22
+ next unless role_slot.reserve_spot!
23
+ waiting_incident_roles.delete(key) if role_slot.full?
24
+ IncidentModel.new(role_slot.incident_id).add_castings({ role_slot.role => [ player ] })
25
+ return role_slot.incident_id
26
+ end
27
+ return false
28
+ end
29
+
30
+ def list_job(incident_id, idx, rolename, count)
31
+ waiting_incident_roles << [incident_id, idx, rolename, count].join(':')
32
+ end
33
+
34
+ def add(audition_id)
35
+ puts "adding #{audition_id} to waiting room #{id.inspect}"
36
+ waiting_auditions << audition_id
37
+ end
38
+ end
39
+
40
+ end
@@ -0,0 +1,16 @@
1
+ require 'redis'
2
+ require 'redis/objects'
3
+
4
+ Redis::Objects.redis = Redis.new
5
+
6
+ require 'ceml/models/cast'
7
+ require 'ceml/models/castable'
8
+ require 'ceml/models/incident'
9
+ require 'ceml/models/audition'
10
+ require 'ceml/models/incident_model'
11
+ require 'ceml/models/incident_role_slot'
12
+ require 'ceml/models/player'
13
+ require 'ceml/models/waiting_room'
14
+ require 'ceml/models/queue'
15
+ require 'ceml/models/bundle'
16
+