Hi everyone, I'm learning python to get a multiplayer roleplaying game up and running.
I didn't see any simple examples of multiplayer games on the web so I thought I'd post mine here. I choose Rock, Paper, Scissors as a first game to experiment with as the game logic/options are easy to implement and understand. Initially, I tried to get the socketserver.TCPServer example in the Python Docs to work, but couldn't get my game variables into the handle method of the class MyTCPHandler. And I didn't know how else to do it. I ended up creating my own server & client out of simple sockets based on the simple echo server & client examples in the Python Docs. I also wanted to send chat messages OR game variables back & forth, but I couldn't figure out how to do the OR, so the basic idea in this implementation is to send the dictionary 'game' back and forth. game dict contains all the gaming variables as well as any chat messages. The program processes results depending on whether the game is starting, there's a chat message, or there's a game move. Finally, in testing, I ran the server in IDLE, but I had to load a command prompt and switch to c:\python30; then type 'python rpsmulti.py' for the client every time. Anyone know how to test server/client code strictly in IDLE? Anyway, try it out and let me know how you would improve it. John R. # NAME: rpsmulti.py # DESCRIPTION: rock, paper, scissors game multiplayer game # AUTHOR: John Robinson # DATE: 1/3/09 # VERSION: Python 3.0 # TO DO: # .server_address instead of HOST, PORT? import socket from random import choice from pickle import dumps, loads from pprint import pprint HOST, PORT = "localhost", 9999 # defined for now BUFFSIZE = 1024 # for socket.send & recv commands BACKLOG = 2 # number of clients supported by server SECONDS = 3 # seconds until socket.timeout (not implemented) # moves dict to translate 'rps' choice MOVES = {'r':'Rock', 'p':'Paper', 's':'Scissors'} # outcome dict stores result for all possible scenarios OUTCOME = {('p','r'): 'win', ('r','s'): 'win', ('s','p'): 'win', ('p','p'): 'draw', ('r','r'): 'draw', ('s','s'): 'draw', ('r','p'): 'lose', ('s','r'): 'lose', ('p','s'): 'lose'} def main_menu(game): """ initialize game dict variables & opening screen of the game """ game['total'] = 0 # total number of games played game['won'] = 0 # total games won by player game['lost'] = 0 # total games lost by player game['drew'] = 0 # total games drew by player game['move'] = '' # player's move (rps) game['message'] = '' # player's chat message game['sentmessage'] = '' # player's previous message game['start'] = True # setting up the game boolean print("\tROCK PAPER SCISSORS\n") if game['name']=='': # if returning to menu don't display the following game['name'] = get_name() print("Welcome "+game['name']+". Remember...") print("Rock smashes scissors! Paper covers Rock! Scissors cuts paper!\n") print("1. Play single player game") print("2. Start two player game") print("3. Join two player game") print("4. Quit") print() c = safe_input("Your choice? ", "1234") c = int(c) if c==1: one_player(game) elif c==2: start_two_player(game) elif c==3: two_player_join(game) else: print('Play again soon.') def safe_input(prompt, values='abcdefghijklmnopqrstuvwxyz'): """ gives prompt, checks first char of input, assures it meets given values default is anything goes """ while True: i = input(prompt) try: c = i[0].lower() except IndexError: # the only possible error?! if c=='': print("Try again.") else: if c not in values: # some other character print("Try again.") else: # looks good. continue. break return i def get_name(): """ returns input as name """ while True: name = input("What is your name? ") check = input(name + ". Correct (y/n)? ") if check[0] in 'yY': break return name def get_result(player, opponent): """ reports opponent's choice; checks player and opponent dicts ['move'] against OUTCOME dict; reports result returns player dict with updated values """ print(opponent['name'], 'chose %s.' % (MOVES[opponent['move']])) # check lookout dict (OUTCOME dictionary) result = OUTCOME[(player['move'], opponent['move'])] # update game variables player['total'] += 1 if result=='win': print('%s beats %s. You win.' % (MOVES[player['move']], MOVES [opponent['move']])) player['won'] += 1 elif result=='draw': print('%s - %s: no one wins. You draw.' % (MOVES[player ['move']], MOVES[opponent['move']])) player['drew'] += 1 else: print('%s loses to %s. You lose.' % (MOVES[player['move']], MOVES[opponent['move']])) player['lost'] += 1 return player def one_player(game): """ implements one player game with minimal opponent dict """ print("\nType (R)ock, (P)aper or (S)cissors to play. (q)uit to return to \ main menu.") opponent = {} opponent['name'] = 'Computer' # gaming loop while True: # gets player's choice game['move'] = safe_input('1, 2, 3, GO! ','rpsq') if game['move']=='q': break # computer chooses via random.choice opponent['move'] = choice('rps') # check game outcome dict game = get_result(game, opponent) # exit loop print('\nYou won %s, lost %s, drew %s (%s total)\n' % \ (game['won'],game['lost'],game['drew'],game['total'])) main_menu(game) def start_two_player(game): """ starts tcp server and implements two player game game dict = player 1""" # Create a socket (SOCK_STREAM means a TCP socket) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind((HOST, PORT)) sock.listen(BACKLOG) serverip, serverport = sock.getsockname() print("Running at %s, %s" % (serverip, serverport)) print("\nType (R)ock, (P)aper or (S)cissors to play. (q)uit to \ return to main menu.") print("Waiting for player...") client, address = sock.accept() clientip, clientport = address # server/game loop while True: try: P2game = loads(client.recv(BUFFSIZE)) # receive other game variables except EOFError: # if available print(P2game['name'], "left the game.") break client.send(dumps(game)) # send our variables # it's either the start... if P2game['start']: print(P2game['name'],"logged on at", clientip, clientport) game['start'] = False # or there's a message if P2game['message']!='' and P2game['message']!=game ['sentmessage']: print(P2game['name']+': '+P2game['message']) game['sentmessage'] = P2game['message'] # to avoid many print calls game['move'] = '' # message always takes priority # or there's a move if game['move']=='': game['move'] = safe_input('1, 2, 3, GO! ') if game['move']=='q': break elif game['move'] not in 'rps': game['message'] = game['move'] game['move'] = '' # only check result if P2game also made a move if P2game['move']!='': # check game outcome dict game=get_result(game, P2game) game['move']='' # exit loop client.close() print('\nYou won %s, lost %s, drew %s (%s total)\n' % \ (game['won'],game['lost'],game['drew'],game['total'])) main_menu(game) def two_player_join(game): """ joins a tcp server two player game game dict = player 2""" # Create a socket (SOCK_STREAM means a TCP socket) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((HOST, PORT)) print("\nType (R)ock, (P)aper or (S)cissors to play. (q)uit to \ return to main menu.") # client/game loop while True: sock.send(dumps(game)) try: P1game = loads(sock.recv(BUFFSIZE)) except EOFError: print(P1game['name'], "left the game.") break if P1game['start']: print("You're connected to "+P1game['name']+"'s game.") game['start'] = False if P1game['message']!='' and P1game['message']!=game ['sentmessage']: print(P1game['name']+': '+P1game['message']) game['sentmessage'] = P1game['message'] game['move'] = '' if game['move']=='': game['move'] = safe_input('1, 2, 3, GO! ') if game['move']=='q': break elif game['move'] not in 'rps': game['message'] = game['move'] game['move'] = '' if P1game['move']!='': # check game outcome dict game=get_result(game, P1game) game['move']='' # exit loop sock.close() print('\nYou won %s, lost %s, drew %s (%s total)\n' % \ (game['won'],game['lost'],game['drew'],game['total'])) main_menu(game) if __name__=='__main__': game = {} # initialize game dict to store all game variables game['name'] = '' # player's name initially main_menu(game) -- http://mail.python.org/mailman/listinfo/python-list