simple turn-based multiplayer game via TCP server/client

G

greywine

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)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

No members online now.

Forum statistics

Threads
473,756
Messages
2,569,534
Members
45,007
Latest member
OrderFitnessKetoCapsules

Latest Threads

Top