Source code for BombermanServer

#!/usr/bin/env python
# -*- encoding: utf-8 -*-

"""
 This program is the **server** for our *Bomberman multiplayer game*.

Example
-------
  This show how the **textual mode** *looks like during the game* :

.. image:: images/exempletextual_server.png
   :scale: 100 %
   :align: center

How to ?
========

 The program accept some *command line* options.

 The simpliest way to use it is::
  **$ ./BombermanServer.py --server bomberman.crans.org --port 12885**
 This will launch the *server* on **bomberman.crans.org** (assuming that your machine is *opened* on the Internet with this domain name), on the *port* **12885** (it **have** to be an open port, maybe you will have to change something in your *firewall*).
   On *Windows*, a window will surely pop to ask you confirmation for this (with a message like "Python is trying to use the port 12885, are you sure ?").

PyRlwrap
---------

 The client *have* to be launched from the **command line**, and during the game the *stdin* (*i.e.* the keyboard) is listened.
  So you can send messages yo your server, directly from the command line.
  The script `PyRlwrap.py <PyRlwrap.html>`_ provide a **readline wrapper** for this command line interface. It brings:
   * shortcut, "a la Nano" (^A: begin line, ^E: end line etc);
   * history (Up & Down show previous message).

 You can launch the client with *PyRlwrap.py* like this (-vv is to *increase* verbosity):
  **$ ./PyRlwrap.py ./BombermanServer.py --server 138.231.134.230 --port 9312 -vv**

Options for textual mod
-----------------------

 The following options can change how the *textual mod* looks like :
  * --noUTF : disabled *UTF* caracters for the board.
    And, **warning** for pseudos, if one begins with a *non-ascii* caracter, the *id* of the player is used (an integer, between 0 and the number of player -1).
  * --noANSI or --ANSI : force to disable or enable the colors.
    Normally, you don't have to use them, because the `ANSIColors module <ANSIColors.html>`_ can detect *cleverly* if colors are supported.
    (Note: this have been tested with **Linux**, **Cygwin**, but not with **Mac OS X**)

Saving and restoring
--------------------

 This is an experimental functionnality, but it should work.
  You can try to **launch again the server on his last state**, with the --load option.
  By default, the *loaded file* is *savegame.ess*, but you can specifify yourse with the --file option.

 And the save file can also be changed with the --save option.
  But this functionnality *have* to be enabled with changin USE_PICKLING to True in `ConfigServer.py <ConfigServer.html>`_.

About
=====

Getting some help
-----------------

 This program uses a *high-level* **command line** parser : `ParseCommandArgs <ParseCommandArgs.html>`_, based on *argparse* from the standard distribution.

 Therefore, the *help* for this program can be obtained simply with ::
  **$ ./BombermanClient.py --help**

 The *help* embed some colors, and it can be read through a pipe :
  **$ ./BombermanClient.py -h | less -r**

About:
======

Algorithm:
----------

 The server follows an algorithm in 3 main parts.

 1. First he **waits for clients**, and **register** them.
    Then, he create the game variables, **send the map** to the clients; and start his 2nb part.

 2. He **listen for order** from clients, **check** them, and if they are ok, **apply** them to his local version of the game.
    Then he make evolved his own game (blow the bomb *etc*).
    And he **send back order** to the clients, saying what have been *modified*.
    And the loop 2 begin again.

 3. And when only one player is on the game, he is declared as the winner.

Choices:
--------

 * For handling many clients (*i.e.* listening for many incoming orders), **select.select** is used.
    Both for the step 1 and 2.
 * The **syntax of messages** is :
    * explained in the `specification_slides.pdf <../../specification_slides.pdf>`_,
    * or in the module `ParseMessageIn <ParseMessageIn.html>`_,
    * or in the module `ParseMessageOut <ParseMessageOut.html>`_;
 * The **specification** of the game protocole is also detailed in the slides (and mainly it explains why **we use TCP**).

Saving and restoring:
=====================

 The server can **save and restore** his state.

How ?
-----

 It still very experimental, and quite limited : socket connections **cannot** be save to a file and restore later !
 But, by now, the board can be save during the game, and them restore after when launching a new server, to begin with this map.

 1. That mean, with the option *--load -f savegame.ess* you can try to **load** a previous map save in *savegame.ess*

 2. And with the option *--save savegame2.ess*, you can try to **save** the current map to *savegame2.ess* during the game.

 .. warning::
    This optionnal functionnality have to be **enabled**, by puting *USE_PICKLING* to 1 in `ConfigServer <ConfigServer.html>`_.

Example:
--------

 The server (left up corner) picked up the last map and will start the game with it and not a randomly generated one.

.. image:: images/exemple_saverestore.png
   :scale: 76 %
   :align: center

Warning
=======
.. warning::
   This script is not yet fully concluded.
   So, it might end badly on some untests behaviour.
   I ran many tests, but I can't ensure everything is all right...

TODOs ?:
=======
 * ? implement and describe the bonus system.
 * ? conclude the parser (add --bonus to activate bonus (not implemented yet)).
"""

__author__='Lilian BESSON'	# Automatically update with update__date__.sh, a Naereen Corp. (c) bash script.
__email__='lilian.besson[AT]normale.fr'
__version__='1.2a'	#: Version of this module
__date__='mar. 19/02/2013 at 22h:48m:15s '	#: The date of the file, automatically update with update__date__.sh, a Naereen Corp. (c) bash script.
try:
	import os, sys, time
	__date__ = time.strftime("%a %d/%m/%Y at %Hh:%Mm:%Ss", time.localtime(os.lstat(sys.argv[0]).st_mtime))
	del os, sys, time
finally:	pass
#1###############
# Usual Modules #
import socket, sys, select, os, copy, random, time
import thread

#2#################
# Project Modules #
import ANSIColors	# ANSIColors.py : just some colors definition.
from Constants import *	# Constants for the game
import Matrix		# Matrix.py : simple module to manipulate matrix, for the board.
import Player		# Player.py : implements the simple player system. (actions, representation etc)
import Board		# Board.py : two classes Board.Board and Board.State.
import ParseMessageOut	# ParseMessageOut.py : pretty printing of data types, for exchange on the net.
import ParseMessageIn	# ParseMessageIn.py : parsing of data types, for exchange on the net.
from AffichPygame import *	# Brings pygame ! Mainly for music, keyboard reaction and graphical window.

try:
	from ConfigServer import *
	if verb and verb2:
		print "The file %s have been loaded as a configuration file for the server." % 'ConfigServer.py'
		print "The server will use the profile named '%s'." % profile_name
	assert( CLOCK_FREQUENCY > 1.0 )
	assert( CLOCK_FREQUENCY < 80.0)
finally:
	if verb and verb2:
		print "[ERROR] I failed when I tried to load the file %s as a configuration file for the server." % 'ConfigServer.py'

###############################################################################
##### First net functions #####


[docs]def create_bind_socket(server = (SERVEUR_INIT, PORT_INIT)): """create_bind_socket(server = (SERVEUR_INIT, PORT_INIT)) -> msocket Create a **server** socket and bind it to the *port* and the addresse *host*. Make also this socket listening (with socket.listen). """ msocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) msocket.bind(server) msocket.listen(64) return(msocket)
[docs]def create_data_socket(listen_socket, NB_CLIENT = 0, verb = True): """create_data_socket(listen_socket, NB_CLIENT = 0) -> data_socket, data_address, NB_CLIENT Create a **client** (data) socket from the **server** socket *listen_socket*. Update the integer [NB_CLIENT] by one. Print useful and colorful informations about this connection, and then return a 3-tuple : * the created socket, * the origin address of the listen_socket (addr. of the server (so, not very useful)), * and the current number of clients. """ # This step can fail. data_socket, data_address = listen_socket.accept() NB_CLIENT += 1 if verb: ANSIColors.printc(""" /new_socket/ A client connects to <green>addr:%s with port:%i<white>, """ % data_address) ANSIColors.printc("""/new_socket/ This client is the <black>%ith<white> to connects. """ % NB_CLIENT) return(data_socket, data_address, NB_CLIENT) ############################################################################### ##### Help for the User interface ##### # The help message, which can be printed to the user when invoking *'\\?'*
help_User = """ <green><u>Welcome in Bomberman server<U><white>, v%s. <blue>Wrote by %s, last version dated of %s.<white> Wrote in Python 2.7.3, using ANSIColors for colors in output messages. Reference page for this software is there : <u>https://bitbucket.org/vcohen/projet_reseau<U>. <neg>You are a Bomberman server<Neg>, which accepts client using **TCP sockets**. The server is mainly 3 steps : 1. Create a map, then wait for users to connect to the server (and collect informations to them : their name and their favorite color). [1. is ok] When there is just enough player to begin the game, launch it ! 2. As an "infinite" loop, wait for actions from the players, then *check* them, then apply them to the local version of the game, then broadcast the action, using strings and the socket.send function. [2. is not yet ok] 3. Conclude when a player wins the game, broadcast the name of the winner, and close. [3. is ok] """ % (__version__, __author__, __date__) # For the commands (with function *action_on_readstr*) help_commands = """The following <green>commands are implemented<white> : <neg>\\h, \\help, \\?<Neg> : print a usefull help message, <neg>\\whoami<Neg> : print the informations about you (address, port), <neg>\\out<Neg> : logout (act like Ctrl+D), <neg>\\list<Neg> : list of all currently connected players, <neg>\\kill=NUM<Neg> : close manually the client number NUM (in order of connection, not by there port).<white> """
[docs]def action_on_readstr(readstr, list_clients, dict_clients, player_clients, server, lx, ly, nb): """action_on_readstr(readstr, list_clients, dict_clients, player_clients, server, lx, ly, nb) -> <bool> Reaction to special message readed from the keyboard ! The goal is to allow the *administrator* of the server to act through the command line. Return *True* is the key did something, *False* if the *readstr* wasn't a command. """ if readstr in ['\\h\n', '\\help\n', '\\?\n']: ANSIColors.printc("/whoami/ You are a Bomberman Server, listening for player over the net interface : <neg>%s<Neg>." % str_of_InfoServer(server)) ANSIColors.printc('/\\help/ <blue>Help : \\h or \\help or \\?<white>:') ANSIColors.printc(help_User) ANSIColors.printc(help_commands) return True if readstr in ['\\out\n']: for s in player_clients: s.close() ANSIColors.printc(""" /\\out/ <warning> <red>Server is closing<white>, because <yellow>server wants to, with the \\out command !<white> (this is a very extrem way to quit : no cleaning and no message to your clients...)""") os._exit(1) if readstr[:5] in ['\\kill']: ANSIColors.printc("\\kill=NUM : close manually the client number NUM. Trying to determin NUM...") try: indice_client = ParseMessageIn.number_of_kill(readstr) assert( (indice_client > 0) and (indice_client < nb)) ANSIColors.printc("/\\kill=/ I understood this command with NUM=%i." % indice_client) # Informing about this logging out ANSIColors.printc('/\\kill=/ Client <green>%s:%i<white> <red><u>killed manually<U><white> !' % dict_clients[indice_client]) ANSIColors.printc('/\\kill=/ He was the player : %s (repr. = %s).\n' % (str(player_clients[indice_client]), repr(player_clients[indice_client]) )) for client in list_clients: client.send(ANSIColors.sprint('/\\kill=/ Client %s <red><u>killed manually<U><white> by the server !\n\t/K/ He was the player : %s (repr. = %s).\n' % ( dict_clients[indice_client], str(player_clients[indice_client]), repr(player_clients[indice_client]) ) )) # Removing the client/player from the data lists dict_clients.pop(indice_client) list_clients.pop(indice_client) player_clients.pop(indice_client) # Done ! except Exception as e: ANSIColors.printc(" <red><u>Fail because %s<U><white>" % e) return True if readstr in ['\\whoami\n', '\\who am I\n']: ANSIColors.printc("/\\whoami/ You are a Bomberman Server, listening for player over the net interface : <neg>%s<Neg>." % str_of_InfoServer(server)) ANSIColors.printc("/\\whoami/ You will wait for %i clients, then launch a game with a board of dim %ix%i." % (nb, lx, ly)) return True ########### if readstr in ['\\list\n', '\\clients\n']: ANSIColors.printc("/\\list/ There is currently <neg>%i connected clients<Neg>." % len(list_clients)) for i in range(len(player_clients)): ANSIColors.printc("This is the client number = <neg>%i<Neg>. He is connected from <u>%s<U>, and he his the player %s (repr. %s)" % (1+i, str(dict_clients[i]), player_clients[i], repr(player_clients[i]))) return True return False ############################################################################### #### First phasis for the game : initialization ####
[docs]def run_phasis1(lx = LX_CST, ly = LY_CST, nb = NB_PLAYER, server = (SERVEUR_INIT, PORT_INIT), board = None): """run_phasis1(lx = LX_CST, ly = LY_CST, nb = NB_PLAYER, server = (SERVEUR_INIT, PORT_INIT)) -> (nbmax, lx, ly, pl, board, Mi, Mj) Creating all Game variables. The [server] variable refers to the address (and the port) of the server being builted. This is the 1st step of the server algorithm : creating the game, wait for client, register them, and when there are the correct number, go to step 2 (i.e. this function returns and new connection are no longer accepted). Returns (nbmax, lx, ly, pl, board, Mi, Mj) : * nbmax: the max number of player for the map, * lx, ly: the dimension of the map, * pl: the list of all the player connected with the server, * board: the map, * Mi, Mj: two integer lists, long as pl, of players' initial positions.""" # Initialing the server. ANSIColors.printc('/phasis 1/ <blue>Bomberman Server is initializing at <green><u>%s:%i<U><white>...' % server) try: socket_server = create_bind_socket(server) #: Now the server is open to listen from the network. except Exception as e: ANSIColors.writec('\t<ERROR> <red>Bomberman Server <u>have failed<U> when trying to initialized to connection...<white>\n', file=sys.stderr) ANSIColors.writec('\t<ERROR> Exception catched : <u>%s<U><white>\n' % str(e), file=sys.stderr) os._exit(2) # Hard quit : a server not connected to the network is nothing. ANSIColors.printc('/phasis 1/ <green>Bomberman Server <u>seems<U> to listen correctly on the network : <neg>%s:%i<Neg>...<white>' % server) ANSIColors.printc("/help/ Enter <blue>\\?<white> for <u>more help<U>.") ############################################################################### # Pseudos and colors for players pseudos, colors = pseudos_colors(nb) # Start position. if not board: Mi, Mj = start_position(TYPE_MAP, lx, ly, nb) else: Mi, Mj = [], [] for tmp_pl in board.players(): Mi.append(tmp_pl.x) Mj.append(tmp_pl.y) for i, j, tmpspot in board: board[i, j].players = copy.copy([]) board[i, j].explosion = False board[i, j].bomb = None ANSIColors.printc(""" /load/ Starts positions have been recover from the loaded map... /load/Mi=<neg>%s<Neg>,\t Mj=<neg>%s<Neg>. """ % ( str(Mi), str(Mj) )) ANSIColors.printc("/load/ Now the current board is :\n%s" % str(board)) ############################################################################### ANSIColors.printc("\n/initGame/ The board will be constructed with those parameters : TYPE_MAP=%s, lx=%i,ly=%i,nb=%i." % (str(TYPE_MAP), lx, ly, nb)) # This one is always like this. nbmax = nb if nbmax>nbmax_Max or 0>nbmax: ANSIColors.printc("<warning> <red>Nb of players ( = len(players)) have to be strictly in [|1;%i|] !<white>" % nbmax_Max) #: List of connected players player_clients = [] #: List of associated sockets list_clients = [] #: List of associated list of addresses dict_clients = [] # The number of client that have been found (i.e. length of the last 3 lists) NB_CLIENT = 0 if USE_DATABASE: #: GOAL: allow some user and block others. try: import shelve database = shelve.open(filename_database, writeback = True, flag = 'c') except Exception as e: ANSIColors.printc("""/database/ <red>Error when opening the database<white> (<neg>%s<Neg>).\t/database/ Maybe the module isn't correctly installed or maybe the file is already there but with restrictive IO rights ?""" % filename_database) #: Logging this operations ANSIColors.printc(""" /waiting room/ <neg>Bomberman Server is launching step #1<Neg> : <u><magenta>waiting room.<white><U> /waiting room/ * It act like a basic IRC server, waiting for new clients. /waiting room/ * As soon as <green>%i clients<white> will be available, after they will all send their pseudos [and their colors], the game will begin. /waiting room/ * You can send messages to your clients, and they can send messages to you (if they are well designed). """ % nbmax) # Informing that the database is well opened. if USE_DATABASE: ANSIColors.printc("""/database/ <green>The database<white> (<neg>%s<Neg>) is well initialized !""" % filename_database) # Going in the main loop. keep = True ############################################################################### try: while (len(player_clients) < nb) or keep: keep = True # Wait for new client ! As soon as the [nb]th client will be identified, # and a pseudo/color will be stored for each of them, this will end. # And only do this !! list_listen = list_clients + [socket_server, sys.stdin] list_in, useless1, useless2 = select.select(list_listen, [], []) # Here is the trick with *select.select*. for socket_in in list_in: # If the administrator is using the keyboard to send message to the clients, # for example to simplify communications if(socket_in is sys.stdin): readstr = sys.stdin.readline() if action_on_readstr(readstr, list_clients, dict_clients, player_clients, server, lx, ly, nb): continue NB_CLIENT = len(dict_clients) # update in case administrator used the command \\kill. if len(readstr) == 0: ANSIColors.printc('/EOF ?/ Server sends <u>an EOF signal<U> : <red>server is closing<white> ...') raise KeyboardInterrupt ANSIColors.printc('/out/ Server <blue>%s:%i <green>sends a message<white>.' % server) origin = 'Server[%s:%i]' % (server) print_on_all(readstr, list_clients, origin) # If it's not from the administrator. # /!\ in this context, that **don't** mean necessarily a new player (because it is **also** a chat room) else: file_in = socket_in.makefile() ############################################################################### # It is a new client ! if(socket_in is socket_server): # The creation of the socket update NB_CLIENT automatically ! new_client, address_client, NB_CLIENT = create_data_socket(socket_server, NB_CLIENT) # Make a new Player.PlayerServer to representing this client. new_player = Player.PlayerServer(pseudo = pseudos[NB_CLIENT-1], color = colors[NB_CLIENT-1]) # Here is CREATED the Player.PlayerServer instance, but not well modified # to have the good pseudo/color. # A good client *have* to send those informations, just after his connection. ####################### new_player.id = NB_CLIENT-1 # His pseudo and color are initialized with the default values. ANSIColors.printc('/new/ A new Player.PlayerServer have been created : <neg>%s<Neg>.' % new_player) if USE_DATABASE: if not database.has_key(address_client[0]): ANSIColors.printc('/database/ This player is <yellow>unknown in the database<white> (<neg>%s<Neg>).' % filename_database) try: database[address_client[0]] = (new_player.pseudo, new_player.color) ANSIColors.printc('/database/ This player is <green>now stored<white> on the database (<neg>%s<Neg>), with the mapping <u>[%s] \\mapsto [%s]<U>.' % (filename_database, address_client[0], str(database[address_client[0]]))) except Exception as e: ANSIColors.printc("""/database/ <red>Error (%s) when registering<white> this new player on the database (<neg>%s<Neg>).""" % (e, filename_database)) # If you want to inform all the already connected client that a new one have been found ? if INFORM_CLIENTS: print_on_all(ANSIColors.sprint(""" /new_client/ A new client connects to <green>addr:%s with port:%i<white>, /new_client/ This client is the <blue>%ith<white> to be connected to the <u>same<U> Bomberman Server as yourse, /new_client/ This client is currently represented as the player %s (repr %s). """ % (address_client[0], address_client[1], NB_CLIENT, str(new_player), repr(new_player))), list_clients, 'Server[%s:%i]' % (server)) # Add the new client to the local datas. ANSIColors.printc('/adding/ This new player is added to list_clients; dict_clients; player_clients.') list_clients.append(new_client) dict_clients.append(address_client) player_clients.append(new_player) ANSIColors.printc('/repr./ This client is currently represented as the player %s (repr %s).\n' % (str(new_player), repr(new_player))) ############################################################################### # One of the already connected client is sending a message else: # Get the variable representing the player address_client = socket_in.getpeername() indice_client = dict_clients.index(address_client) # WARNING here. player = player_clients[indice_client] # Inform who is being listened ANSIColors.printc(""" <u>/listen/ <neg>Listening<Neg> to client <blue>%s<white>, number <red>%s<white> :<U> """ % (str(address_client), str(1+indice_client))) ANSIColors.printc('/repr./ In the game, this client is the player : %s (repr. <neg>%s<Neg>)' % (str(player), repr(player))) # Read a message from one of the available client readstr = file_in.readline() if len(readstr)>0: ANSIColors.printc('/reading/ Client <blue>%s:%i <green>sends a message<white> :' % address_client) ANSIColors.printc('/reading/ This is his message : <blue>%s<green>...' % readstr) if INFORM_CLIENTS: list_clients.remove(socket_in) origin = 'Client[%s:%i]' % address_client print_on_all(readstr, list_clients, origin) list_clients.append(socket_in) ANSIColors.printc('/reading/ His message have been printed on <green>all %i other client\'s stdout<white>.' % (len(list_clients) - 1)) # Analyze the message from the client, and if it has the good form, change the attributes of the player. try: # message pseudo = %s player.pseudo = ParseMessageIn.newplayer_of_str(readstr[:len(readstr)-1])[:32] keep = False # that mean : if it is the last client, then ok I can stop now. # and *color*s are just an add-on, so they are *not* mandatory. ANSIColors.printc('/scanf/ <green>Success<white> ! The player <blue>%s<white> have changed his <u>pseudo<U> to %s (and now, he is %s).' % (origin, player.pseudo, player)) if USE_DATABASE: database[address_client[0]] = (player.pseudo, player.color) ANSIColors.printc('/database/ This player have changed <green>in the database<white> (<neg>%s<Neg>), with the mapping <u>[%s] ~~~~» [%s]<U>.' % (filename_database, address_client[0], str(database[address_client[0]]))) except Exception as e1: #: try: # message color = %s #: player.color = ParseMessageIn.newcolor_of_str(readstr[:len(readstr)-1])[:32] #: ANSIColors.printc('/scanf/ <green>Success<white> ! The player <blue>%s<white> have changed his <u>color<U> to %s (and now, he is %s).' % (origin, player.color, player)) #: if USE_DATABASE: #: database[address_client[0]] = (player.pseudo, player.color) #: ANSIColors.printc('/database/ This player have changed <green>in the database<white> (<neg>%s<Neg>), with the mapping <u>[%s] \\mapsto [%s]<U>.' % (filename_database, address_client[0], str(database[address_client[0]]))) #: except Exception as e2: if verb2: ANSIColors.printc('/scanf/ <red>Fail<white> : the message was not a command. (e1 = %s)') ##'; e2 = %s)' % (e1, e2)) # Maybe the value of the player have been changed. ANSIColors.printc(""" /repr/ This client is currently represented as the player %s (repr %s). """ % (str(player), repr(player))) else: # nothing readed from the client : he logged out or exited ANSIColors.printc('/logged out/ Client <green>%s:%i<white> <red><u>logged out<U><white> !' % address_client) # Removing the client/player from the data lists NB_CLIENT = NB_CLIENT - 1 dict_clients.remove(address_client) list_clients.remove(socket_in) player_clients.remove(player) # Informing about this logging out ANSIColors.printc(""" /client out/ He <red>was<white> the player : %s (repr. = <neg>%s<Neg>). """ % (str(player), repr(player) )) # If you want to inform all the already connected client that he quit ? if INFORM_CLIENTS: print_on_all('/client out/ Client %s logged out !\n\t/5/ He was the player : %s (repr. = %s).' % ( ("%s:%i" % address_client), str(player), repr(player) ), list_clients, origin = 'Server[%s:%i]' % (server) ) except Exception as e: if USE_DATABASE: database.close() ANSIColors.writec('/database/ <blue>The database have been closed<white> !') ANSIColors.writec('\a\n\t/END Phase 1/ <red>Server is closing<white>, because <red>%s<white> !' % e) ############################################################################### # Here : the while loop is concluded, the except might not have been executed. # So, the first step is almost done : # * we have the informations about the map, # * we have the correct number of players, each corresponding to a client # == > we just have to create the map, and return all those datas. ANSIColors.printc('\n\t/./ <red>Phasis #1 is done<white>, now, the map is being constructed !') ANSIColors.printc('/./ <red> The current list of player is <white>:\n\t/./ %s\n' % (str(player_clients)) ) # Build the *final* list of player. nb = len(player_clients) for i in range(nb): ANSIColors.printc("/n/ Add a player : his number #%i. He is %s (repr. %s)." % (i, player_clients[i], repr(player_clients[i]))) # Convert players to state spl = list() for i in range(nb): spl.append(Board.State(wall = False, players = [player_clients[i]])) if not board: # Initialize the board board = Board.Board(Board.empty, lx, ly) # Change how to print boxes of the board (|-+/\\ or nonANSI) board.mat.box = Matrix.boxnoASCII if Board.UTFSupported else Matrix.boxASCII for i, j, spot in board: #: ok. newspot = copy.copy( Board.empty ) if (i>0) and (j>0) and (i<lx-1) and (j<ly-1) and (PROBA_UMUR > random.random()): # no umur over the walls. newspot = copy.copy( Board.umur ) else: newspot = copy.copy( random.choice( [Board.dmur, Board.empty] ) ) if ((i%2 == 1) and (j%2 == 1)) and (0.3*PROBA_UMUR <= random.random()): # Idea from Lucas Hosseini. newspot = copy.copy( Board.umur ) newspot.players = copy.copy([]) board[i,j] = copy.copy(newspot) else: ANSIColors.printc("/pickle/ <INFO> <neg><u><green>The board was already here !<reset><white>") for i in range(nb): player_clients[i].move(Mi[i], Mj[i]) board[Mi[i], Mj[i]] = spl[i] # do not use copy.copy here : spl[i] are pointers to player_clients[k] for deltax in [-1, 1]: for deltay in [-1, 1]: if (Mi[i]+deltax >= 0) and (Mj[i]+deltay >= 0) and (Mi[i]+deltax <= lx-1) and (Mj[i]+deltay <= ly-1): board[Mi[i]+deltax, Mj[i]+deltay].destr = False board[Mi[i]+deltax, Mj[i]+deltay].wall = False # Send everything to all the clients. msg = ParseMessageOut.str_of_board_and_player(board, player_clients) ANSIColors.printc("/send/ The map has been created, and I'll send it to all the players.\n\t/send/ %s" % msg) print_on_all(message = msg, list_clients = list_clients, origin = 'Server[%s:%i]' % (server)) print_clear("\n" + str(board)) # Return all those created values. # To continue the game -> direction Phasis#2. return (nb, lx, ly, player_clients, board, Mi, Mj, socket_server, list_clients, dict_clients) ############################################################################### #### Using Pickle ####
[docs]def save_current_game(variables_to_save, info = "variables_to_save", fn=filename_pickling): """ Save all variables content the list *variables_to_save*, in a .pkl file. The game can be restored then, by setting all variables equals to their previous values (**of course** this only work if the .pkl file is still there).""" return ParseMessageOut.try_pickling(variables_to_save, info, fn=fn) ############################################################################### #### Mail Loop for the game ####
[docs]def run_phasis2(nb, lx, ly, player_clients, board, Mi, Mj, socket_server, server, list_clients, dict_clients): """ run_phasis2(nb, lx, ly, player_clients, board, Mi, Mj, socket_server, server, list_clients, dict_clients) -> Player.PlayerServer instance, for the winner. Mail Loop for the game.""" NB_CLIENT = len(player_clients) # have to be ==nb assert(NB_CLIENT == nb) ANSIColors.printc("""\n\n /phasis 2/ <green>Bomberman Server <u>is still listening<U> correctly on the network : <neg>%s:%i<Neg>...<white> /phasis 2/ <neg><green>Bomberman Server is launching step #2 .<Neg><white> """ % server) print_help() # Initialization of the clock frequency. clock = pygame.time.Clock() ANSIColors.printc("/pygame/ <INFO> <green>The game will make %i loops each seconds<white> (this control is made with clock.tick())" % CLOCK_FREQUENCY) # Print the parameters for the game. ANSIColors.printc(""" /parameters/ The game is launching with those parameters : nb, lx, ly, Mi, Mj = %s. /parameters/ List of players : %s. /parameters/ Constants for the game : BREAK_ON_WALL, NB_BOMB_MAX_ALLOW, force_default, pv_Init = %s. /parameters/ And then the board : """ % (str( (nb, lx, ly, Mi, Mj) ), str(player_clients), str( (BREAK_ON_WALL, NB_BOMB_MAX_ALLOW, force_default, pv_Init)) ) ) print_clear("\n" + str(board)) ############################################################################### # To count the nb of turns, and the time. nb_turn = 1 time_before = time.time() time_before2 = time.time() ############################################################################### #: Start parallelization NB_THREADS = 3 thread.start_new_thread( toggle_explosion, (board, player_clients, clock), \ {'MAKE_DESTROY':True, 'FORCE':True, \ 'print_on_all':print_on_all, \ 'list_clients':list_clients, 'origin':("[%s:%i]" % server), \ 'num_thread':NB_THREADS} ) ############################################################################### if USE_PICKLING: save_current_game((nb, lx, ly, board), "nb, lx, ly, board", fn=savegame) # Going in the main loop. keep = True try: while keep and (len(player_clients)>1): #: To ensure that the printing is not too quick. clock.tick(CLOCK_FREQUENCY) time_now = time.time() if ((time_now - time_before2) > 1): # (5.0/CLOCK_FREQUENCY) ): print_clear("\n" + str(board) + "\n PVs for players :\n") print_pvs_player(player_clients) time_before2 = time_now if USE_PICKLING: save_current_game((nb, lx, ly, board), "nb, lx, ly, board", fn=savegame) keep = True # To be sure we are not keeping an already dead player alive... ############################################################### for tmp_pl in player_clients: if tmp_pl.pv <= 0: ANSIColors.printc('/dead/ Player <neg>%s:%i<white> <red><u>is dead : it is not right that I found him here...<U><white>' % ( str(tmp_pl), tmp_pl.id )) try: ANSIColors.printc('/dead/ Player <neg>%s:%i<white> <red><u>is dead : I\'m deleting him from my variables...., at index %i for the lists, and on the spot %i,%i for the matrix...<U><white>' % ( str(tmp_pl), tmp_pl.id, tmp_pl.id, tmp_pl.x, tmp_pl.y )) indice_client = player_clients.index(tmp_pl) dict_clients.remove( dict_clients[indice_client] ) list_clients.remove( list_clients[indice_client] ) player_clients.remove(tmp_pl) board[tmp_pl.x, tmp_pl.y].players.remove(tmp_pl) except: ANSIColors.printc("/dead/ <warning> <neg> I failed badly when I tried to remove this player <neg>%s<Neg>. Cause=%s !" % (str(tmp_pl), str(sys.exc_info()[1]))) ############################################################### if ((time_now - time_before) > TIME_EXPLOSION ): if verb2: ANSIColors.printc("<white>/time/ Time lapse between now and the last tic : <neg>%f<Neg>." % (time_now - time_before)) time_before = time_now nb_turn += 1 if verb: ANSIColors.printc("/t/ <blue>New turn !<white> Number of turn : <neg>%i<Neg>." % nb_turn) #: Listening... list_listen = list_clients + [socket_server, sys.stdin] list_in, useless1, useless2 = select.select(list_listen, [], []) # Here is the trick with *select.select*. for socket_in in list_in: ############################################################################### # If the administrator is using the keyboard to send message to the clients, # for example to simplify communications if(socket_in is sys.stdin): readstr = sys.stdin.readline() if action_on_readstr(readstr, list_clients, dict_clients, player_clients, server, lx, ly, nb): ANSIColors.printc(""" This is a list of new commands. You can test them ! * <neg>GAME_OVER(id)<Neg> -- To announce the <green><neg>winner<Neg> of the game<white> (id is between 0 and %i), * <neg>PLANT_BOMB(i,j)<Neg> -- To plant a <green>new bomb at position (i,j)<white> (i is between 0 and %i, j is between 0 and %i), * <neg>BLOW_BOMB(i,j,radius)<Neg> -- To make the bomb at positon (i,j) <green>blow<white> (radius is the New York distance of explosion, default is %i), * <neg>MOVE_PLAYER(id,i,j)<Neg> -- To <green>move the player number=id to the position (i,j)<white>. """ % (nb, lx, ly, force_default) ) continue NB_CLIENT = len(dict_clients) # update in case administrator used the command \\kill. if len(readstr) == 0: ANSIColors.printc('/9/ Server sends <u>an EOF signal<U> : <red>server is closing<white> ...') raise KeyboardInterrupt ANSIColors.printc('/1/ Server <blue>%s:%i <green>sends a message<white>.' % server) origin = 'Server[%s:%i]' % (server) print_on_all(readstr, list_clients, origin) # If it's not from the administrator. # In this context, that **don't** mean necessarily a new player (because it is **also** a chat room) elif not(socket_in is socket_server): # No new clients in second phasis. ############################################################################### file_in = socket_in.makefile() # One of the already connected client is sending a message # Get the variable representing the player try: address_client = socket_in.getpeername() indice_client = dict_clients.index(address_client) # WARNING here. player = player_clients[indice_client] except socket.error as eee3: ANSIColors.printc(""" <warning> <magenta><neg> I'm reading an already closed connection : this is bad !<Neg> I'm closing now... <warning> Maybe the last client is already disconnected !<white> """) list_clients.remove(socket_in) if not list_clients: ANSIColors.printc("<warning> <red>I have no client connected : ciao !<white> Exit code=6...") os._exit(6) continue # FIXME ? # Inform who is being listened ANSIColors.printc('\n\t/=/ Listening to client <blue>%s<white>, number <red>%s<white> :' % (str(address_client), str(1+indice_client))) ANSIColors.printc('/=/ In the game, this client is the player : %s' % str(player)) # Read a message from one of the available client readstr = file_in.readline() if (len(readstr)>0) or (player.pv <= 0): ANSIColors.printc('/1/ Client <blue>%s:%i <green>sends a message<white> :' % address_client) ANSIColors.printc("/in/ He send <neg>%s<Neg>. I am going to use it..." % readstr) origin = 'Server[%s:%i]' % server if INFORM_CLIENTS: # FIXME just be sure. list_clients.remove(socket_in) # WARNING here. print_on_all(readstr, list_clients, origin) list_clients.append(socket_in) # WARNING here. ANSIColors.printc('/1/ His message have been printed on <green>all %i other client\'s stdout<white>.' % (len(list_clients) - 1)) # readstr is now a message from the server. ############################################################################### # Update the current game with those orders. # the unpack functions are gameover_of_str, posplantbomb_of_str, blowbomb_of_str, moveplayer_of_str try: i, j = player.x, player.y Mi, Mj = i, j #: keep_parsing = True if verb2: ANSIColors.printc("\n/in/<INFO> Beginning parsing '<neg>%s<Neg>'." % readstr) #: while keep_parsing and readstr: # FIXME in the semantics, we said *one message, one '\n'*. if readstr: if readstr[ -1 ] == '\n': readstr = readstr[: len(readstr) - 1 ] # Delete the last '\n' ############################################################# if readstr[:10] == 'PLANT_BOMB': ANSIColors.printc("/in/<INFO> Trying to interpret [<neg>%s<Neg>] as a PLANT_BOMB message." % readstr) if board[i, j].bomb: if verb: ANSIColors.printc("/!/ <u><red>This is not allowed<U><black>, you cannot drop a second bomb <u>here<U>.<white>") elif player.nb_bomb >= NB_BOMB_MAX_ALLOW: if verb: ANSIColors.printc("/!/ <u><red>This is not allowed<U><black>, you cannot drop a new bomb on the board (max allow : %i).<white>" % NB_BOMB_MAX_ALLOW) else: board[i, j].bomb = player.drop() # default value. print_on_all( ParseMessageOut.str_of_posplantbomb(i, j) , list_clients, origin) if verb: ANSIColors.printc("/out/ <green> An order have been sent to <neg>plant<Neg> a new bomb (=%s) at the spot <neg>%i,%i<Neg>.<white>" % ( str(board[i,j].bomb), i, j )) ############################################################# if readstr[:9] == 'MOVE_LEFT': ANSIColors.printc("/in/<INFO> Trying to interpret [<neg>%s<Neg>] as a MOVE_LEFT message." % readstr) if (j>0) and board[i, j-1].is_free(): j = max(0,j-1) ############################################################# if readstr[:10] == 'MOVE_RIGHT': ANSIColors.printc("/in/<INFO> Trying to interpret [<neg>%s<Neg>] as a MOVE_RIGHT message." % readstr) if (j<ly-1) and board[i, j+1].is_free(): j = min(ly-1,j+1) ############################################################# if readstr[:7] == 'MOVE_UP': ANSIColors.printc("/in/<INFO> Trying to interpret [<neg>%s<Neg>] as a MOVE_UP message." % readstr) if (i>0) and board[i-1, j].is_free(): i = max(0,i-1) ############################################################# if readstr[:9] == 'MOVE_DOWN': ANSIColors.printc("/in/<INFO> Trying to interpret [<neg>%s<Neg>] as a MOVE_DOWN message." % readstr) if (i<lx-1) and board[i+1, j].is_free(): i = min(lx-1,i+1) ############################################################# else: #: keep_parsing = False if verb: ANSIColors.printc("""<question>\t/in/<INFO> I can't interpret [<neg>%s<Neg>] as any messages I can understand...""" % readstr) # Now log all this. if board[i,j].wall: if verb: ANSIColors.printc("/!/ <green>Player %s<white> can not move from (%i,%i) to (%i,%i) because there is a <u>wall<U> in the wanted spot." % (player, Mi, Mj, i, j)) continue elif board[i,j].bomb: if verb: ANSIColors.printc("/!/ <green>Player %s<white> can not move from (%i,%i) to (%i,%i) because there is a <u>bomb<U> in the wanted spot." % (player, Mi, Mj, i, j)) continue elif (i == Mi) and (j == Mj): if verb: ANSIColors.printc("/!/ <green>Player %s<white> can not move from (%i,%i) to (%i,%i) because there is the natural limitation of the board." % (player, Mi, Mj, i, j)) continue elif board[i,j].is_free(): ANSIColors.printc("/!/ <green>Player %s<white> is moving from (%i,%i) to (%i,%i)." % (player, Mi, Mj, i, j)) try: board[player.x, player.y].players.remove(player) player.move(i, j) board[player.x, player.y].players.append(player) print_on_all( ParseMessageOut.str_of_moveplayer(player) , list_clients, origin) if verb: ANSIColors.printc("/out/ <green> An order have been sent to <neg>plant<Neg> a new bomb (=%s) at the spot <neg>%i,%i<Neg>.<white>" % ( str(board[i,j].bomb), i, j )) continue except ValueError as useless1: ANSIColors.printc(""" <warning> <red>I just tried to remove the player %s (repr. %s) from the spot %i,%i. <warning> <red>This spot is %s (and contains this list of player %s). """ % ( str(player), repr(player), player.x, player.y, str(board[player.x, player.y]), str(board[player.x, player.y].players) )) #: keep_parsing = False else: if verb: ANSIColors.printc("/!/ <green>Player %s<white> can not move from (%i,%i) to (%i,%i)." % (player, Mi, Mj, i, j)) continue except: raise #FIXME be sure ? ############################################################################### else: # nothing readed from the client : he logged out or exited if player.pv <= 0: ANSIColors.printc('/dead/ Player <neg>%s<white> <red><u>died<U><white> !' % str(player)) else: ANSIColors.printc('/5/ Client <green>%s:%i<white> <red><u>logged out<U><white> !' % address_client) # Removing the client/player from the data lists NB_CLIENT = NB_CLIENT - 1 ANSIColors.xtitle(".: Phasis #2 of Bomberman Server %s:%i; playing with %i players) :." % (server[0], server[1], NB_CLIENT)) dict_clients.remove(address_client) list_clients.remove(socket_in) player_clients.remove(player) # Informing about this logging out ANSIColors.printc('/logged out/ He <red>was<white> the player : %s (repr. = <neg>%s<Neg>).\n' % (str(player), repr(player) )) # FIXME, to respect the formal semantics, it is better to delete it. # If you want to inform all the still connected client that he quit ? if INFORM_CLIENTS: print_on_all('/client out/ Client %s logged out !\n\t/5/ He was the player : %s (repr. = %s).' % ( ("%s:%i" % address_client), str(player), repr(player) ), list_clients, origin = 'Server[%s:%i]' % (server) ) ############################################################################### except: ANSIColors.writec(""" /END Phase 2/ <red>Server is closing<white>, because <red><neg>%s<Neg><white> !""" % str( sys.exc_info()[1] )) raise # Here : the while loop is concluded, the except might not have been executed. print "\n", board if not player_clients: ANSIColors.writec("""\n /END Phase 2/ <red>Server is closing<white>, because <red><neg>he have no valid client<Neg> to propose as a winner....<white> !""") sys.stderr.flush() sys.stdout.flush() os._exit(9) return player_clients[0], list_clients # Last player on top ! ############################################################################### ##### Last stuff : used the command line to know what to do #####
if __name__ == '__main__': import ParseCommandArgs #: This variable is the preprocessor, given to description and epilog by ParseCommandArgs, #: * erase: to print with no colors. #: * sprint: to print with colors. preprocessor = ANSIColors.sprint if ANSIColors.ANSISupported else ANSIColors.erase #:preprocessor = __builtin__.str, if you want to *see* the balises. #: Generate the parser, with another module. parser = ParseCommandArgs.parser_default(\ description = '<green>BombermanServer <red>module<reset> and <blue>script<reset>.',\ epilog = """\n\ <yellow>About: ======<reset> This is the server for a <neg>multiplayer Bomberman Game<Neg> (MPRI 1-21 projet, 2013). This project is hosted here <u>https://bitbucket.org/vcohen/projet_reseau<U>. The doc for this project can be find here <u>http://perso.crans.org/~besson/publis/Bomberman/_build/html/<U>.""", \ version = __version__, date = __date__, author = __author__, \ preprocessor = preprocessor) #: Description for the part with '--file' and '--generate' options. group = parser.add_argument_group('About network connection', preprocessor("""\ <b>This program <u>is a server<U>. So, it will listen on an *address* and on a *port* <reset> """)) #: Remember that action can be used to many things. group.add_argument("-s","--server", help = preprocessor("""The address of the server. <u>Examples<U>: * <yellow>'127.0.0.1'<reset> : to listen only on the local loop (usefull for test if you don't have a network connection), * <yellow>'0.0.0.0'<reset> : to listen on all interfaces (usefull to be an *open* server), * <yellow>'bomberman-server.crans.org'<reset> : to listen only on a fixed loop, if you have a fix DNS name. <green> This one is the best, to be a simple-to-remember server name !<reset>""")) #:, required = True) group.add_argument("-p","--port", type = int, help = "The port on which the server will listen for clients (ex. 9312, or 12882).\n\t This port *have* to be an open port on your machine.") #:, required = True) #: A group for handling save restore group = parser.add_argument_group('Saving and restoring previous sessions', preprocessor("""\ <b>This program <u>save his state<U> (the board, list of players etc) during the session<reset>, so if there is a problem, the last valid state of the game will be kept. And this program can of course restore such a state, to restart from where it was ! """)) group.add_argument('-l', '--load', help = preprocessor(""" If present, the server will try to restart a previously close session. Default is not."""), action = "store_true") group.add_argument('-f', '--file', help = preprocessor(""" Try to load the savegame from the file FILE if possible, launch a <red>new one<reset> if not.""")) group.add_argument('--save', help = preprocessor(""" If present, the server will try to save his current session to the file SAVE. Default is none. To enable this <red>experimental functionnality<white>, <neg>change USE_PICKLING to True in ConfigServer.py<Neg>.""")) #: A group for parametrize the map -x -y -N for lx, ly, nbmax #: group = parser.add_argument_group('Parametrize the map (by now, **do not use it**) :', preprocessor("""\ #:<b>The game <u>run on a map<U> (also called the board)<reset>, #: which is mainly parametrized by his length : lx,ly; and the max number of player in it. #: Currently, the map is very simple, and LX,LY should be 6,6; 8,8 or 10,10. #:""")) #: group.add_argument('-x', '--lx', type = int, help = preprocessor(""" Force the map to take length LX over x axis."""), default = LX_CST) #: group.add_argument('-y', '--ly', type = int, help = preprocessor(""" Force the map to take length LY over y axis."""), default = LY_CST) #: group.add_argument('-N', '--nbmax', type = int, help = preprocessor(""" Force the map to take less than NBMAX players."""), default = NB_PLAYER) #: The parser is done, #: Use it to extract the args from the command line. args = parser.parse_args() ############################################################################### ANSIColors.xtitle(".: Bomberman Server, v%s, made by %s :." % (__version__, __author__)) print_clear(".: Welcome in the Bomberman Server, v%s, made by %s Last modification %s :." % (__version__, __author__, __date__)) # Use those args. verb = (args.verbose > 1) or verb # Default = True verb2 = (args.verbose > 2) and verb2 # Default = False if verb2: print(" Processing command line args...") print("About verbosity: verb=%s, verb2=%s." % (verb, verb2)) # Print with ANSI escape code for colors if possible ANSIColors.ANSISupported = (not(args.noANSI) and ANSIColors.ANSISupported) or args.ANSI # Disable all escape codes for color to be generated if not(ANSIColors.ANSISupported): ColorOff() else: ColorOn() #: Defined in Constants. ANSIColors.printc("/!/ ANSI escape code for colors supports = <green>%s<white>." % ANSIColors.ANSISupported) # Print with non ASCII caracters for boxes if possible Board.UTFSupported = not(args.noUTF) and Board.UTFSupported ANSIColors.printc("/!/ UTF escape code for boxes supports = <green>%s<white>." % Board.UTFSupported) # Set the server and the port server = args.server if args.server else SERVEUR_INIT port = int(args.port) if args.port else PORT_INIT # Choose a file to save. if args.save: savegame = args.save else: savegame = filename_pickling # Set the parameter of the map try: lx = max(1, min(lx_Max, int(args.ly))) if args.lx else LX_CST ly = max(1, min(ly_Max, int(args.lx))) if args.ly else LY_CST nbmax = max(1, min(nbmax_Max, int(args.nbmax))) if args.nbmax else NB_PLAYER except Exception as e: lx, ly, nbmax = LX_CST, LY_CST, NB_PLAYER if verb2 and verb: ANSIColors.printc("/init/ <u><red>Failed to find the lx,ly,nbmax<reset><white> in command line args. Cause : <neg>%s<Neg>." % str(e)) ############################################################################### board = None #: If a save game is available, load it ! if args.load: ANSIColors.printc("/load/ <yellow>Trying to load<reset><white> \tthe game from a save file<reset><white>...") try: if args.file: nbmax, lx, ly, board = ParseMessageIn.try_unpickling("(nbmax, lx, ly, board)", fn = args.file) else: # use the default value ! nbmax, lx, ly, board = ParseMessageIn.try_unpickling("(nbmax, lx, ly, board)") for i, j, spottmp in board: board[i,j] = copy.copy(spottmp) board[i,j].players = copy.copy(spottmp.players) ANSIColors.printc("/load/ <green>Succeed to load<reset><white> \tthe game from a save file<reset><white>...") ANSIColors.printc(""" /load/ I picked up those parameters : <neg>lx=%i,ly=%i,nb=%i<Neg> /load/ And the following board: %s """ % ( lx, ly, nbmax, str(board) )) except Exception as e: ANSIColors.printc("/load/ <u><red>Failed to load<reset><white> \tthe game from a save file...\n\t/load/ Cause : %s" % e) else: ############################################################################### #: Otherwise, new parameters will be recreated. if verb2: e = "<yellow>-l neither --load found if the argument.<reset>" ANSIColors.printc("/load/ <u><red>Failed to load<reset><white> \tthe game from a save file...\n\t/load/ Cause : %s" % e) ############################################################################### try: ANSIColors.printc("/init/ <yellow>The game will run on the server : (%s:%i)<reset><white>" % (server, port)) if USE_NOTIFY: ANSIColors.notify("Phasis #1 of Bomberman Server is going to start (you are listening on %s:%i for %i clients)" % (server, port, nbmax), obj=".: Bomberman Server :.", icon="bomberman.gif" ) ANSIColors.xtitle(".: Phasis #1 of Bomberman Server %s:%i :." % (server, port)) nbmax, lx, ly, player_clients, board, Mi, Mj, socket_server, list_clients, dict_clients = run_phasis1(lx = lx, ly = ly, nb = nbmax, server = (server, port), board=board) # The game is initialized ############################################################################### #: Launch the game if USE_NOTIFY: ANSIColors.notify("Phasis #2 of Bomberman Server is going to start (you are still listening on %s:%i; playing with %i players)" % (server, port, nbmax), obj=".: Bomberman Server :.", icon="bomberman.gif") ANSIColors.xtitle(".: Phasis #2 of Bomberman Server %s:%i; playing with %i players) :." % (server, port, nbmax)) player_winner, list_clients = run_phasis2(nbmax, lx, ly, player_clients, board, Mi, Mj, socket_server, (server, port), list_clients, dict_clients) print_on_all( ParseMessageOut.str_of_gameover(player_winner), list_clients, origin = 'Server[%s:%i]' % (server, port)) ANSIColors.printc(""" /end of the game/ <INFO> I just sent the last message to all connected clients, to inform them that the winner is : <neg>%s<Neg> (repr. as : %s) """ % ( str(player_winner), repr(player_winner) )) ANSIColors.printc("<neg> Going asleep for 1.0 seconds...<Neg>") time.sleep(1.0) ############################################################################### except KeyboardInterrupt: ANSIColors.printc("""<warning> <red> The game is done<default>. <warning> I <yellow>guess<default> you closed it, probably with an EOF (<black>Ctrl+D<default>) or a SIGTERM signal (<black>Ctrl+C<default>). <warning> <green>Feel free to send any comment, suggestion or bug : <white> <u><neg>%s<reset>.""" % (__email__)) sys.stdout.flush() sys.stderr.flush() os._exit(1) except: ANSIColors.printc("<ERROR> <red> The game is done<default>. I received the last exception <neg>%s<Neg>." % str(sys.exc_info()[1])) sys.stdout.flush() sys.stderr.flush() raise ############################################################################### ANSIColors.printc("""<green><neg> The game is done<white>, and I didn't receive any unhandled exceptions in the end<reset> (good job !)... <neg>The second phasis seems to give me this player as a winner<neg> : %s. I'm quiting nicely now... <green>Feel free to send any comment, suggestion or bug : <white> <u><neg>%s<reset>.""" % ( player_winner, __email__ ) ) os._exit(0) ############################################################################### # DONE.