(Modifications to the Multithreaded Tic-Tac-Toe Program) The programs of Fig. 20.6 and Fig. 20.7 implemented a multithreaded, client/server version of the game Tic-Tac-Toe. Our goal in developing this game was to demonstrate a multithreaded server that could process multiple connec- tions from clients at the same time. The server in the example is really a mediator between the two clients—it
makes sure that each move is valid and that each client moves in the proper order. The server does not determine who won or lost or if there was a draw. Modify the TicTacToeServer class to test for a win, loss or draw on each move in the game. The server should send to each client a message that indicates the result of the game when the game is over.
What will be an ideal response?
```
# Class TicTacToeServer maintains a game of Tic-Tac-Toe
# for two clients, each managed by a Player thread.
import socket
import threading
import time
class Player( threading.Thread ):
"""Thread to manage each Tic-Tac-Toe client individually"""
def __init__( self, connection, server, number ):
"""Initialize thread and setup variables"""
threading.Thread.__init__( self )
if number == 0:
self.mark = "X"
else:
self.mark = "O"
self.connection = connection
self.server = server
self.number = number
def otherPlayerMoved( self, location ):
"""Notify client of opponent’s last move"""
self.connection.send( "Opponent moved." )
self.connection.send( str( location ) )
def otherPlayerEnded( self ):
"""Notify client that opponent ended game"""
self.connection.send( "Game over. Winner: %s." % \
self.server.getWinner() )
self.connection.send( "" )
def run( self ):
"""Play the game"""
self.server.display( "Player %s connected." % self.mark )
self.connection.send( self.mark )
self.connection.send( "%s connected." % self.mark )
# wait for another player to arrive
if self.mark == "X":
self.connection.send( "Waiting for another player..." )
self.server.gameBeginEvent.wait()
self.connection.send(
"Other player connected. Your move." )
else:
self.server.gameBeginEvent.wait() # wait for server
self.connection.send( "Waiting for first move..." )
# play game
while not self.server.gameOver():
location = self.connection.recv( 2 )
if not location or location == "EG":
break
if self.server.validMove( int( location ), self.number ):
self.server.display( "loc: " + location )
self.connection.send( "Valid move." )
if self.server.gameOver():
self.otherPlayerEnded()
else:
self.connection.send( "Invalid move, try again." )
self.connection.close()
self.server.display( "Game over." )
self.server.display( "Connection closed." )
class TicTacToeServer:
"""Server that maintains a game of Tic-Tac-Toe for two clients"""
def __init__( self ):
"""Initialize variables and setup server"""
HOST = ""
PORT = 5000
self.board = []
self.currentPlayer = 0
self.turnCondition = threading.Condition()
self.gameBeginEvent = threading.Event()
self.winner = "" # no winner yet
for i in range( 9 ):
self.board.append( None )
# setup server socket
self.server = socket.socket( socket.AF_INET,
socket.SOCK_STREAM
self.server.bind( ( HOST, PORT ) )
self.display( "Server awaiting connections..." )
def execute( self ):
"""Play the game--create and start both Player threads"""
self.players = []
for i in range( 2 ):
self.server.listen( 2 )
connection, address = self.server.accept()
self.players.append( Player( connection, self, i ) )
self.players[ -1 ].start()
# players are suspended until player O connects
# resume players now
self.gameBeginEvent.set()
def display( self, message ):
"""Display a message on the server"""
print message
def validMove( self, location, player ):
"""Determine if a move is valid--if so, make move"""
# only one move can be made at a time
self.turnCondition.acquire()
while player != self.currentPlayer:
self.turnCondition.wait()
if not self.isOccupied( location ):
if self.currentPlayer == 0:
self.board[ location ] = "X"
else:
self.board[ location ] = "O"
self.currentPlayer = ( self.currentPlayer + 1 ) % 2
self.players[ self.currentPlayer ].otherPlayerMoved(
location )
if self.gameOver():
self.players[ self.currentPlayer ].otherPlayerEnded()
self.turnCondition.notify()
self.turnCondition.release()
return 1
else:
self.turnCondition.notify()
self.turnCondition.release()
return 0
def isOccupied( self, location ):
"""Determine if a space is occupied"""
return self.board[ location ] # an empty space is None
def gameOver( self ):
"""Determine if the game is over"""
# test horizontally
for x in range( 0, 9, 3 ):
if self.board[ x ] == self.board[ x + 1 ] == \
self.board[ x + 2 ] and self.isOccupied( x ):
self.winner = self.board[ x ]
return 1
# test vertically
for y in range( 0, 3 ):
if self.board[ y ] == self.board[ y + 3 ] == \
self.board[ y + 6 ] and self.isOccupied( y ):
self.winner = self.board[ y ]
return 1
# test diagonals
if self.board[ 0 ] == self.board[ 4 ] == \
self.board[ 8 ] and self.isOccupied( 0 ):
self.winner = self.board[ 0 ]
return 1
if self.board[ 2 ] == self.board[ 4 ] == \
self.board[ 6 ] and self.isOccupied( 2 ):
self.winner = self.board[ 2 ]
return 1
# if board is full at this point, a tie has occurred
for location in range( 0, 9 ):
if not self.isOccupied( location ):
return 0 # game not over
self.winner = "TIE"
return 1 # game over
def getWinner( self ):
"""Return winner"""
return self.winner
def main():
TicTacToeServer().execute()
if __name__ == "__main__":
main()
```
```
# Client for Tic-Tac-Toe program.
import socket
import threading
from Tkinter import *
import Pmw
class TicTacToeClient( Frame, threading.Thread ):
"""Client that plays a game of Tic-Tac-Toe"""
def __init__( self ):
"""Create GUI and play game"""
threading.Thread.__init__( self )
# initialize GUI
Frame.__init__( self )
Pmw.initialise()
self.pack( expand = YES, fill = BOTH )
self.master.title( "Tic-Tac-Toe Client" )
self.master.geometry( "250x325" )
self.id = Label( self, anchor = W )
self.id.grid( columnspan = 3, sticky = W+E+N+S )
self.board = []
self.messageSet = 0
# create and add all buttons to the board
for i in range( 9 ):
newButton = Button( self, font = "Courier 20 bold",
height = 1, width = 1, relief = GROOVE,
name = str( i ) )
newButton.bind( "
self.board.append( newButton )
current = 0
# display buttons in 3x3 grid beginning with grid's row one
for i in range( 1, 4 ):
for j in range( 3 ):
self.board[ current ].grid( row = i, column = j,
sticky = W+E+N+S )
current += 1
# area for server messages
self.display = Pmw.ScrolledText( self, text_height = 10,
text_width = 35, vscrollmode = "static" )
self.display.grid( row = 4, columnspan = 3 )
self.start() # run thread
def run( self ):
"""Control thread to allow continuous updated display"""
# setup connection to server
HOST = "127.0.0.1"
PORT = 5000
self.connection = socket.socket( socket.AF_INET,
socket.SOCK_STREAM )
self.connection.connect( ( HOST, PORT ) )
# first get player’s mark ( X or O )
self.myMark = self.connection.recv( 1 )
self.id.config( text = 'You are player "%s"' %
self.myMark )
self.myTurn = 0
# receive messages sent to client
while 1:
try:
message = self.connection.recv( 34 )
except:
break
if not message:
break
self.processMessage( message )
self.connection.close()
self.display.insert( END, "Connection closed.\n" )
self.display.yview( END )
def processMessage( self, message ):
"""Interpret server message to perform necessary actions"""
if message == "Valid move.":
self.display.insert( END, "Valid move, please wait.\n" )
self.display.yview( END )
self.board[ self.currentSquare ].config(
text = self.myMark, bg = "white" )
elif message == "Invalid move, try again.":
self.display.insert( END, message + "\n" )
self.display.yview( END )
self.myTurn = 1
elif message == "Opponent moved.":
location = int( self.connection.recv( 2 ) )
if self.myMark == "X":
self.board[ location ].config( text = "O",
bg = "gray" )
else:
self.board[ location ].config( text = "X",
bg = "gray" )
self.display.insert( END, message + " Your turn.\n" )
self.display.yview( END )
self.myTurn = 1
elif message.find( "Game over." ) != -1:
self.connection.send( "EG" )
self.display.insert( END, message + "\n" )
self.myTurn = 1
elif message == "Other player connected. Your move.":
self.display.insert( END, message + "\n" )
self.display.yview( END )
self.myTurn = 1
else:
self.display.insert( END, message + "\n" )
self.display.yview( END )
def sendClickedSquare( self, event ):
"""Send attempted move to server"""
if self.myTurn:
name = event.widget.winfo_name()
self.currentSquare = int( name )
try:
self.connection.send( name )
except:
pass
self.myTurn = 0
def main():
TicTacToeClient().mainloop()
if __name__ == "__main__":
main()
```
You might also like to view...
8. How can you assign the value "toaster" to a c-string name str of size 10?
a. str="toaster; b. str=toaster; c. strcpy(str,"toaster"); d. str.strcpy("toaster");
A single record in a form can be printed to .pdf (Adobe Acrobat) format
Indicate whether the statement is true or false
You can place a chart on a separate chart sheet in a workbook or on the same worksheet as the data.
Answer the following statement true (T) or false (F)
Primary Major Horizontal is an example of a type of _____________________.
Fill in the blank(s) with the appropriate word(s).