ceml 0.7.13 → 0.8.0
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/Makefile +1 -1
 - data/VERSION +1 -1
 - data/ceml.gemspec +62 -50
 - data/guide/guide.html +69 -14
 - data/guide/guide.md +74 -15
 - data/guide/guide.pdf +0 -0
 - data/lib/ceml/driver.rb +0 -181
 - data/lib/ceml/lang/basic_instruction.rb +49 -0
 - data/lib/ceml/{casting_statement.rb → lang/casting_statement.rb} +19 -9
 - data/lib/ceml/{instruction_statements.rb → lang/instruction_statements.rb} +5 -16
 - data/lib/ceml/{script.rb → lang/script.rb} +53 -43
 - data/lib/ceml/lang/tt/casting.rb +432 -0
 - data/lib/ceml/lang/tt/casting.treetop +29 -0
 - data/lib/ceml/lang/tt/instructions.rb +1130 -0
 - data/lib/ceml/lang/tt/instructions.treetop +86 -0
 - data/lib/ceml/lang/tt/lexer.rb +1804 -0
 - data/lib/ceml/{tt → lang/tt}/lexer.treetop +70 -7
 - data/lib/ceml/lang/tt/scripts.rb +647 -0
 - data/lib/ceml/{tt → lang/tt}/scripts.treetop +2 -2
 - data/lib/ceml/lang.rb +10 -0
 - data/lib/ceml/models/audition.rb +65 -0
 - data/lib/ceml/models/bundle.rb +64 -0
 - data/lib/ceml/models/cast.rb +108 -0
 - data/lib/ceml/models/castable.rb +81 -0
 - data/lib/ceml/{incident.rb → models/incident.rb} +63 -15
 - data/lib/ceml/models/incident_model.rb +100 -0
 - data/lib/ceml/models/incident_role_slot.rb +16 -0
 - data/lib/ceml/models/player.rb +80 -0
 - data/lib/ceml/models/queue.rb +12 -0
 - data/lib/ceml/models/waiting_room.rb +40 -0
 - data/lib/ceml/models.rb +16 -0
 - data/lib/ceml/processor.rb +162 -0
 - data/lib/ceml.rb +7 -14
 - data/test/askchain.ceml +6 -0
 - data/test/compliment.ceml +4 -0
 - data/test/dialogues/accept.ceml +24 -0
 - data/test/dialogues/basic_seed.ceml +26 -0
 - data/test/dialogues/jordan.ceml +121 -0
 - data/test/helper.rb +44 -39
 - data/test/jane.ceml +48 -0
 - data/test/{test_casting.rb → lang/test_casting.rb} +5 -0
 - data/test/lang/test_instructions.rb +42 -0
 - data/test/{test_scripts.rb → lang/test_scripts.rb} +3 -2
 - data/test/sync.ceml +6 -0
 - data/test/test_castable.rb +20 -0
 - data/test/test_dialogues.rb +58 -0
 - data/test/test_incident.rb +64 -127
 - metadata +54 -30
 - data/.gitignore +0 -23
 - data/lib/ceml/confluence.rb +0 -63
 - data/lib/ceml/role.rb +0 -61
 - data/lib/ceml/tt/casting.treetop +0 -65
 - data/lib/ceml/tt/instructions.treetop +0 -91
 - data/test/test_instructions.rb +0 -27
 - 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 
     | 
| 
       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 
     | 
    
         
            -
                       
     | 
| 
       39 
     | 
    
         
            -
                       
     | 
| 
      
 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 
     | 
    
         
            -
                       
     | 
| 
      
 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 
     | 
    
         
            -
                         
     | 
| 
      
 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 
     | 
    
         
            -
                   
     | 
| 
       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  
     | 
| 
      
 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 
     | 
| 
      
 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,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
         
     | 
    
        data/lib/ceml/models.rb
    ADDED
    
    | 
         @@ -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 
     | 
    
         
            +
             
     |