In: Computer Science
PLEASE READ CAREFULLY!!!!
write a client.py and server.py file for tic-tac-toe IN PYTHON with the following restrictions (SO WRITE TWO FILES THAT PLAY PYTHON THROUGH A SOCKET)
Use a 5 x 5 grid (dimensions are subject to change, so use
constants for NUM_ROWS and NUM_COLS)
Use 'X' for player 1 and 'O' for player 2 (symbols and the number
of players is subject to change, so use constants)
Each player can make 1 move per turn before having to wait for the
next player to move. If an illegal move is made, an
error message is sent to the player by the server and the player
loses the current turn
SO AGAIN TO RECAP A TIC TAC TOE PROGRAM IN PYTHON THAT ALLOWS TWO PLAYERS TO PLAY TOGETHER, a client and a server file
Here is tic-tac-toe in python, please hit that like button or thumbs-up, it really motivates me>3
client.py
import socket
import threading
from tkinter import *
import Pmw
class TicTacToeClient(Frame, threading.Thread ):
def __init__(self):
threading.Thread.__init__( self )
# make 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 = []
# 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( "<Button-1>", self.sendClickedSquare )
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 ) )
self.myMark = self.connection.recv( 1 ).decode('ascii')
self.id.config( text = 'You are player "%s"' % self.myMark )
self.myTurn = 0
# receive messages sent to client
while 1:
#message = self.connection.recv( 34 ).decode('ascii')
length = int(self.connection.recv(2).decode('ascii'))
message = self.connection.recv(length).decode('ascii')
if not message:
break
self.processMessage( message )
self.connection.close()
self.display.insert( END, "Game over.\n" )
self.display.insert( END, "Connection closed.\n" )
self.display.yview( END )
def processMessage( self, message ):
"""Interpret server message to perform necessary actions"""
# valid move occurred
if message == "Valid move.":
self.display.insert( END, "Valid move, please wait.\n" )
self.display.yview( END )
# set mark
self.board[ self.currentSquare ].config(
text = self.myMark, bg = "white" )
# invalid move occurred
elif message == "Invalid move, try again.":
self.display.insert( END, message + "\n" )
self.display.yview( END )
self.myTurn = 1
# opponent moved
elif message == "Opponent moved.":
# get move location
location = int( self.connection.recv( 1 ).decode('ascii') )
# update board
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
# other player's turn
elif message == "Other player connected. Your move.":
self.display.insert( END, message + "\n" )
self.display.yview( END )
self.myTurn = 1
# simply display message
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 )
print(name, type(name))
# send location to server
self.connection.send( name.encode('ascii') )
self.myTurn = 0
def main():
TicTacToeClient().mainloop()
if __name__ == "__main__":
main()
server.py
import socket
import threading
class Player( threading.Thread ):
def __init__( self, connection, server, number ):
"""Initialize thread and setup variables"""
threading.Thread.__init__( self )
# specify player's mark
if number == 0:
self.mark = "X"
else:
self.mark = "O"
self.connection = connection
self.server = server
self.number = number
def otherPlayerMoved( self, location ):
self.connection.send( "15Opponent moved.".encode('ascii') )
self.connection.send( str( location ).encode('ascii') )
def run( self ):
"""Play the game"""
# send client message indicating its mark (X or O)
self.server.display( "Player %s connected." % self.mark )
self.connection.send( self.mark.encode('ascii') )
# wait for another player to arrive
if self.mark == "X":
self.connection.send( "29Waiting for another player...".encode('ascii') )
self.server.gameBeginEvent.wait()
self.connection.send(
"34Other player connected. Your move.".encode('ascii') )
else:
self.server.gameBeginEvent.wait() # wait for server
self.connection.send( "25Waiting for first move...".encode('ascii') )
# play game until over
while not self.server.gameOver():
# get more location from client
location = self.connection.recv(1).decode('ascii')
if not location:
break
# check for valid move
if self.server.validMove( int( location ), self.number ):
self.server.display( "loc: " + location )
self.connection.send( "11Valid move.".encode('ascii') )
else:
self.connection.send( "24Invalid move, try again.".encode('ascii') )
# close connection to client
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()
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 = []
# wait for and accept two client connections
for i in range( 2 ):
self.server.listen( 2 )
connection, address = self.server.accept()
# assign each client to a Player thread
self.players.append( Player( connection, self, i ) )
self.players[ -1 ].start()
self.server.close() # no more connections to wait for
# 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 not current player, must wait for turn
while player != self.currentPlayer:
self.turnCondition.wait()
# make move if location is not occupied
if not self.isOccupied( location ):
# set move on board
if self.currentPlayer == 0:
self.board[ location ] = "X"
else:
self.board[ location ] = "O"
# change current player
self.currentPlayer = ( self.currentPlayer + 1 ) % 2
self.players[ self.currentPlayer ].otherPlayerMoved(
location )
# tell waiting player to continue
self.turnCondition.notify()
self.turnCondition.release()
# valid move
return 1
# invalid move
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"""
# place code here testing for a game winner
# left as an exercise for the reader
return 0
def main():
TicTacToeServer().execute()
if __name__ == "__main__":
main()
I tried my best to include necessary comments in code to understand what's happening in program.
Again,Please do not forget to hit that like button>3(It will take only 1-2 seconds)
Thank you!!