from pyparsing import * # Helper parser stuff nop = lambda s,l,t:[] Dropkeyword = lambda x: Keyword(x).setParseAction(nop) # Tokens for the parser # names include hyphen, letters, numbers underscore, hash, and parens name = Word('-_#()' + alphas + nums) number = Word(nums) # sequence of digits negnumber = Word('-',nums) # a negative sign followed by a sequence of digits bot = name loc = name bank_value = number # must be between 0 and 1000 inclusive loot = number # must be between 0 and 6000 inclusive world = number # must be between 0 and 200 inclusive distance = number # must be between 0 and the number of nodes inclusive certainty = number | negnumber # must be between -100 and 100 inclusive coordinate = number # must be betwen 0 and 1023 inclusive ptype = Keyword('cop-foot') | Keyword('cop-car') | Keyword('robber') edge_type = Keyword('car') | Keyword('foot') node_tag = (Keyword('hq') | Keyword('bank') | Keyword('robber-start') | Keyword('ordinary')) # Register: (C --> S) # Bots use this message to register with the server. register_msg = Dropkeyword('reg:') + bot + ptype # World Skeleton: (S --> C) # This message indicates initial information that the player receives. # # The player's name is repeated back to the player, in case the server changed # it. The server only changes the name when it is the same as some other # player's name. Following the player's name, the server reports the names of # all of the players in the game. # # In a node line, the tag bank indicates that the given node is a bank, hq # indicates that the given node is a headquarters, robber-start indicates that # the given node is the node on which the robber starts the game, and ordinary # indicates that the node is not one of those three types. There is always # exactly one headquarters and exactly one robber start position on any # world-skeleton message. # # The coordinates associated with the nodes indicate relative positions of the # nodes (in the usual computer-science coordinate system: (0,0) is at the top # left, and bigger coordinates move to the right and down, respectively) to # show where the nodes match up (approximately) to a real map of Hyde Park. The # coordinates do not play a role in game play, but may help you visualize the # game. # # The edg: lines show the source edge first and the target edge second. coplist = Group(Keyword('cop:') + bot + Keyword('cop:') + bot + Keyword('cop:') + bot + Keyword('cop:') + bot + Keyword('cop:') + bot ) coplist.setParseAction(lambda s,l,t: [t[0][1::2]]) world_skeleton_msg = ( Dropkeyword('wsk\\') + Dropkeyword('name:') + bot + Dropkeyword('robber:') + bot + coplist + Dropkeyword('nod\\') + Group( ZeroOrMore(Dropkeyword('nod:') + Group(loc + node_tag + coordinate + coordinate))) + Dropkeyword('nod/') + Dropkeyword('edg\\') + Group( ZeroOrMore(Dropkeyword('edg:') + Group(loc + loc + edge_type))) + Dropkeyword('edg/') + Dropkeyword('wsk/') ) # World (S --> C) # # This message contains all the data that is variable in the world (values at # banks, whether you found evidence or not, location/types of players). The # rbd: line contains the amount of loot the robber currently has. The bv # section describes the amount of money in the banks. The ev section describes # the evidence on the current node; it indicates the location and turn where # the robber was (there are never any ev: lines for the robber). The smell # line indicates if cops can sense the robber nearby. If the cops cannot sense # the robber, the distance will be zero (the distance is always zero for the # robber). Finally, the pl section describes the visible players, their types, # and their locations. # TODO Keir: please check changes and make sure I haven't broken things world_msg = ( Dropkeyword('wor\\') + Dropkeyword('wor:') + world + Dropkeyword('rbd:') + loot + # Dropkeyword('sc\\') + # Group( # ZeroOrMore(Dropkeyword('sc:') + Group(bot+bot) )) + # Dropkeyword('sc/') + # Dropkeyword('dc\\') + # Group( # ZeroOrMore(Dropkeyword('dc:') + bot)) + # Dropkeyword('dc/') + # Dropkeyword('fac\\') + # Group( # ZeroOrMore(Dropkeyword('fac:') + Group(bot+bot) )) + # Dropkeyword('fac/') + Dropkeyword('bv\\') + Group( ZeroOrMore(Dropkeyword('bv:') + Group(loc + bank_value))) + Dropkeyword('bv/') + Dropkeyword('ev\\') + Group( ZeroOrMore(Dropkeyword('ev:') + Group(loc + world))) + Dropkeyword('ev/') + Dropkeyword('smell:') + distance + Dropkeyword('pl\\') + Group( ZeroOrMore(Dropkeyword('pl:') + Group(bot + loc + ptype))) + Dropkeyword('pl/') + Dropkeyword('wor/') ) # Move (C --> S) # The players communicate their moves with a mov: line. The ptype must match # the player's current ptype unless the player is a cop and is located at the # cop headquarters in the current world. In that case, a cop may switch its # type from cop-car to cop-foot. move_msg = Dropkeyword('mov:') + loc + ptype # Game Over (S --> C) # This means the game has finished. game_over_msg = Dropkeyword('game-over') #FIXME assign an action to game_over_msg # Inform (C --> S) # Cop-Brains share information about player locations with this message. Each # line indicates where and when a player was (or is or will be), with a # certainty. Positive certainties indicate how sure the reporter is that the # information is true. Negative certainties should be interpreted as how # certain the reporter is that the information is false. inform_msg = ( Dropkeyword('inf\\') + Group( ZeroOrMore(Dropkeyword('inf:') + Group(bot + loc + ptype + world + certainty))) + Dropkeyword('inf/') ) # Suggest Plan (C --> S) # The plan message communicates suggested moves for each Cop-Bot. A plan may # contain any number of moves for any robot. plan_msg = ( Dropkeyword('plan\\') + Group( ZeroOrMore(Dropkeyword('plan:') + Group(bot + loc + ptype + world))) + Dropkeyword('plan/') ) # Voting (C --> S) # After each player has submitted a plan, they are sent to the cops using a # from-msg. The voting is done by ranking, so listing a player's name first # means that you think they have submitted the best plan and listing a player # last means you think they submitted the worst plan. A legal ballot must # contain each Cop-Bot's name, exactly once. vote_msg = ( Dropkeyword('vote\\') + Group( ZeroOrMore(Dropkeyword('vote:') + bot)) + Dropkeyword('vote/') ) # Vote Tally (S --> C) # After all players have voted, the server computes which is the elected plan # and reports the winner, if any. vote_res_msg = ( Dropkeyword('winner:') + bot) | Dropkeyword('nowinner:') # From: Messages from other cops (S --> C) # When a player sends an inform-msg or a plan-msg to the server, the server # makes sure it is grammatically correct, and then forwards it to the other # clients. # These message are unlike other messages since they contain repeated elements # that are longer than a single line. Each of the repeated messages is prefixed # by a from: token, indicating who originally sent the message, and then the # message they sent (with the spaces and newlines normalized). # Unlike the other repeated elements in the protocol spec, the repeated # elements below may contain more than 1000 lines, but each repeated element # from the inform-msg or plan-msg non-terminals can only appear at most 1000 # times. Overall, this means that entire message may be as long as 5000 lines # roughly (plus a few more structuring lines), since the server only sends out # one encapsulated message per Cop-Bot. from_msg_inform = ( Dropkeyword('from\\') + Group( ZeroOrMore(Dropkeyword('from:') + Group(bot + inform_msg))) + Dropkeyword('from/') ) from_msg_plan = ( Dropkeyword('from/') + Group( ZeroOrMore(Dropkeyword('from:') + Group(bot + plan_msg))) + Dropkeyword('from/') ) if __name__ == '__main__': text = 'reg: hello robber \n' name = 'hello' print bot.parseString(name) print ptype.parseString('cop-foot') x = register_msg.parseString(text) print x wsm = ''' wsk\ name: bob robber: bob2 cop: angus cop: clay cop: shirky cop: zany cop: barney nod\\ nod: my-location-44 hq 3 2 nod/ edg\\ edg: 44-bloor-spadina 343-my-house foot edg: 288-robert- yonge-irwin car edg/ wsk/''' wsu = ''' wor\\ wor: 33 rbd: 333 bv\\ bv: 44-abab 300 bv: 444-33-mom 4040 bv/ ev\\ ev: ausou 33 ev: my-33-bloor 3555 ev/ smell: 40 pl\\ pl: mcgruff pp-43 cop-foot pl: roeee bloor-spadina robber pl/ wor/ ''' print world_skeleton_msg.parseString(wsm) print world_msg.parseString(wsu)