#!/usr/bin/env python
#-*- encoding: utf-8 -*-
"""
This module implement a simple analyser for input messages (read from clients or server).
It respect the semantics about the communications (this document is available here `specification_slides <../../specification_slides.pdf>`_).
Basically, it is a collection of mini parsers.
It also provides the function *try_unpickling*, to load datas from binary files (saved with ParseMessageOut.try_pickling).
"""
__author__='Lilian BESSON (mailto:lilian.besson[AT]normale.fr)' # Automatically update with update__date__.sh, a Naereen Corp. (c) bash script.
__version__='1.7a'
__date__='mar. 19/02/2013 at 01h:04m:42s ' #: The date of the file, automatically update with update__date__.sh, a Naereen Corp. (c) bash script.
#: This can be erased by the module Constants to modify the global verbosity of this module
PRINT_ALL_PARSEIN = True
#1###############
# Usual Modules #
import pprint #: Use pprint to print the data which are pickling with pickle.
try:
import cPickle as pickle
except:
import pickle
# A try of pretty-printing / parsing
# using printf and scanf
# usefull to learn how to send datas using strings messages
import scanf
#2#################
# Project Modules #
import ANSIColors # ANSIColors.py : just some colors definition.
from Constants import * # Constants.py : all the constants
# About Pickling
#################
[docs]def try_unpickling(info="(no info about the unpickling was given)", verb=True, verb2=False, fn=filename_pickling):
""" Load datas from the file *fn*.
This file **have** to be present, and **have** to be created by ParseMessageOut.try_pickling.
The goal of *info* is to say what variables will be set equal to the unpicked datas (see the examples below).
If *verb* is here, this function print a log saying from which file datas are being read.
If *verb2* is here, this function will also **print all unpicked datas** (using the pprint.pprint pretty-printer).
Caution ! If the datas are quite huge (and you are allow to do so), the will print a huge message.
This function doesn't handle exception, so be prudent with it (in particular, the file *fn* have to be there).
Example:
>>> from ParseMessageOut import try_pickling
>>> a, b, c = 0, 2.0, u"ok !" # Make a tuple.
>>> try_pickling((a,b,c)) # Save the tuple, in the default file (.datas_saved_by_picked_python.pkl).
>>> aa, bb, cc = try_unpickling(info="aa, bb, cc") # Load the tuple from the default file, and save it to (aa, bb, cc).
Warning:
.. warning::
All datas cannot be picked and upicked.
Fore more details, see directly the documentation of the **pickle** module.
"""
if verb:
ANSIColors.printc('\t/pickle/ <blue>reading<white> from <u>%s<U> to read datas,\n\t/pickle/ and load them into the variable(s) %s...' % (fn, info))
try:
pkl_file = open(fn, 'rb')
data = pickle.load(pkl_file)
if verb2:
pprint.pprint(data)
pkl_file.close()
except Exception as e:
if verb:
ANSIColors.printc('\t/pickle/ <red>failed<white> when reading <u>%s<U>\n\t/pickle/ Cause : %s.' % (fn, e))
raise e
if verb:
ANSIColors.printc('\t/pickle/ <green>succed<white> when reading <u>%s<U>.' % fn)
return data
[docs]def try_parse(message, pattern, verb=False):
""" A simple wrapping around scanf.sscanf.
This will try to parse the *message*, according to the scanf *pattern*.
If verb, print useful and colored log information."""
if PRINT_ALL_PARSEIN:
ANSIColors.printc("<magenta>/PRINT_ALL_PARSEIN/ message=<neg>'%s'<Neg>. pattern=<neg>'%s'<Neg>.<white>" % (message, pattern))
if verb:
ANSIColors.printc('\t/scanf/ <blue>Trying to parse<white> the message [%s] with the pattern [%s]...' % (message, pattern))
try:
result = scanf.sscanf(message, pattern)
except Exception as e:
if verb:
ANSIColors.printc('\t/scanf/ <red>Fail<white> ! scanf.sscanf raised the exception : %s.' % e)
raise e
if verb:
ANSIColors.printc('\t/scanf/ <green>Success<white> ! scanf.sscanf returned the value(s) : %s.' % str(result))
return result
[docs]def number_of_kill(s):
""" Sent message to manually kill a connected player.
For BombermanServer.py."""
result = try_parse(s, "\\kill=%i")
return result[0]
[docs]def newplayer_of_str(s):
""" Sent message by a new player when he wants to change his pseudo.
Contains the pseudo of the player.
"""
s=s.replace('(','').replace(')','')
result = try_parse(s, "NEW_PLAYER%s")
return result[0]
[docs]def newcolor_of_str(s):
""" Sent message by a new player when he wants to change his color.
Contains the color of the player.
.. warning::
The color is checked to be correct, i.e. one of the simple color from `ANSIColors.simpleColorList <ANSIColors.html#simpleColorList>`_.
This list is ['green', 'red', 'blue', 'black', 'cyan', 'magenta', 'yellow', 'white'].
"""
result = try_parse(s, "color=%s")
if not(result[0] in ANSIColors.simpleColorList):
raise Exception("The color %s is not a valid one (from the list ['green', 'red', 'blue', 'black', 'cyan', 'magenta', 'yellow', 'white'])." % result[0])
return result[0]
[docs]def update_players(board, list_players, player, message, verb=True):
""" Move the players in the list *list_players* according to the order in *message*.
This is for the first communication (message GAME_START), and **only for this**.
The order was packed with ParseMessageOut.str_of_players.
"""
if verb: print "update_players: receiving : %s" % message
N = len(list_players)
if verb: print "N players, N=%i. Players=%s." % (N, str(list_players))
move_done = 0 #: Number of expected moves.
#: result = try_parse(message, ";%s,%i,%i"*N, verb=verb)#FIXME
#: result = try_parse(message, ";%s,%i,%i"*N+"%s", verb=verb)
message = message.replace(',', ' #')
# WARNING: this is not like in our formal semantics,
# but it's ok anyway.
# A try:
if message[-1]!='\n':
ANSIColors.printc("\n\n<warning> I found <neg> no '\\n' caracter at the end<Neg> at message=%s. !!! Not fine ?" % message )
result = try_parse(message, ";%s#%i#%i"*N, verb=True)
#: result = try_parse(message+"XXX", ";%s,%i,%i"*N+"%s", verb=True)
else:
ANSIColors.printc("\n\n<warning> I found <neg> one (or more) '\\n' caracter at the end<Neg> at message=%s. !!! Not fine ?" % message )
result = try_parse(message[0: -1], ";%s #%i #%i"*N, verb=True)
#: result = try_parse(message[0: -1]+"XXX", ";%s,%i,%i"*N+"%s", verb=True)
#######################################################################
for l in range(N):
if verb: ANSIColors.printc("\t/update_players/ For the player [%s], looking for an order about id=%i ..." % (list_players[l], list_players[l].id))
x, y = result[l*3 + 1], result[l*3 + 2]
board[list_players[l].x, list_players[l].y].players.remove(list_players[l])
# Update attributes.
list_players[l].id = l # by increasing order.
list_players[l].pseudo = result[l*3]
if list_players[l].pseudo == player.pseudo:
################ FIXME ##################################
ANSIColors.printc("\n\n<INFO> <neg> <warning> I'm trying to set the <u>id<U><Neg><white> of you, client [%s], to %i." % ( str(player), l ))
ANSIColors.printc("<INFO> <warning> <red><u> This is a bug in our protocol <U><Neg><white> <magenta><neg>***maybe***<Neg><white> the parsed *id* wasn't yourse, if an other is using the *same* pseudo as yourse...\n")
################ FIXME ##################################
player.move(x,y)
player.id = l
list_players[l] = player #FIXME
board[x, y].players.append(list_players[l])
list_players[l].move(x, y)
if verb:
ANSIColors.printc("\t/update_players/ <green>Found ! <yellow>id=%i => x=%i,y=%i<white>. Moving the player !" % (list_players[l].id, x, y))
move_done += 1
if (move_done < N) and verb:
ANSIColors.printc("\t/update_players/ <WARNING> the parser 'ParseMessageIn.update_players' found only %i moves (and %i was expected)..." % (move_done, N))
return (board, list_players, player)
[docs]def board_and_player_of_str(board, pl, player, message, verb=True):
"""board_and_player_of_str(board, pl, player, message, verb=True)-> board, pl, player
Update the board and the list of players with the map sent in the string *message*.
The order was packed with ParseMessageOut.str_of_board_and_player.
"""
# Now parse the message.
message=message.replace(')','').replace('(','')
if verb: print "board_and_player_of_str: receiving : %s" % message
result = try_parse(message, "GAME_START%s", verb=verb)
message = message[len("GAME_START"):]
# Continue parsing.
for i,j,spot in board:
s = message[0] #: 1,2,3
if (i<board.lx-1)or(j<board.ly-1):
message = message[2:]
else:
message = message[1:] # no final ','.
board[i,j].destr = (s == '1')
board[i,j].wall = (s in ['1', '2'])
#: if verb: print "i=%i, j=%i => s=%s. Now this spot is %s." % (i,j,s, str(board[i, j]))
if verb: print board
if verb: print "board_and_player_of_str: now i look at : %s. I'm starting......" % message
board, pl, player = update_players(board, pl, player, message, verb=verb)
return (board, pl, player)
[docs]class GameOver(Exception):
""" Exception Class for handling the end of the game (win or lose).
.. warning::
Even if his name seems to says otherwise, this exception give the **winner** of the game.
In fact, the name was choose to fit with the message *GAME_OVER*, already fixer in the formal semantics.
This message, sent by the server, imply the **end of the game**, and give the *id* of the player who is winning the game.
Attributes:
* player -- the player which is which has wan.
* msg -- the message in case of victory.
"""
[docs] def __init__(self, player, msg="This is the default message. The winner is "):
""" Construction of a GameOver exception.
"""
self.player = player #: the player which has wan.
self.msg = msg #: the message annoucing the death, which have to be printed to the player.
[docs] def __str__(self):
"""__str__(self) -> str
A pretty string for the exception. (with colors if self.player supports color)."""
return "%s %s." % (self.msg, self.player)
[docs]def gameover_of_str(s):
"""gameover_of_str(s) -> s2, id
Received message from the server to announce the winner of the game, by refering to his [id].
For the player who have the same id : he is the *winner*, and for the other : they have *lost*.
[s2] refers to the end of the message [s] (untouched by the data's extraction).
The order was packed with ParseMessageOut.str_of_gameover.
"""
s=s.replace(')','').replace('(','')
result = try_parse(s, "GAME_OVER%i")
s = s[ len( "GAME_OVER%i" % (result[0]) ) :]
return s, result[0]
[docs]def posplantbomb_of_str(s):
"""posplantbomb_of_str(s) -> s2, i, j
Received message from the server to announce that a bomb have been planted in the spot ([i], [j]).
[s2] refers to the end of the message [s] (untouched by the data's extraction).
The order was packed with ParseMessageOut.str_of_posplantbomb.
"""
s=s.replace(')','').replace('(','')
result = try_parse(s, "PLANT_BOMB%i,%i")
s = s[ len( "PLANT_BOMB%i,%i" % (result[0], result[1]) ) :]
return s, result[0], result[1]
[docs]def blowbomb_of_str(s):
"""blowbomb_of_str(s) -> s2, i, j, radius
Received message from the server to announce that a bomb have blowed up
in the spot (i,j), with the radius [radius].
[s2] refers to the end of the message [s] (untouched by the data's extraction).
The order was packed with ParseMessageOut.str_of_blowbomb.
"""
s=s.replace(')','').replace('(','')
result = try_parse(s, "BLOW_BOMB%i,%i,%i")
s = s[ len( "BLOW_BOMB%i,%i,%i" % (result[0], result[1], result[2]) ) :]
return s, result[0], result[1], result[2]
[docs]def moveplayer_of_str(s):
"""moveplayer_of_str(s) -> s2, id, i, j
Received message from the server to announce that a player [id] have moved to [i], [j].
[s2] refers to the end of the message [s] (untouched by the data's extraction).
The order was packed with ParseMessageOut.str_of_moveplayer.
"""
s=s.replace(')','').replace('(','')
result = try_parse(s, "MOVE_PLAYER%i,%i,%i")
s = s[ len( "MOVE_PLAYER%i,%i,%i" % (result[0], result[1], result[2]) ) :]
return s, result[0], result[1], result[2]
# END