<repo_name>bthomp4/481_TTT_Server<file_sep>/TTT_Game.py
# Game class for the tic tac toe game
class Game(object):
    # when  constructing a Game object there is an optional paramter for player_first
    # if player_first is 1, the player makes the first move, the player is "X"
    # if player_first is 0 (default), the computer makes the first move, the computer is "X"
    def __init__(self, player_first = 0):
        # the board 2-d list holds the game board
        self.board = [[" ", " ", " "],[" ", " ", " "],[" ", " ", " "]]
        # indicates if the marker to be used by the player and computer
        self.player_first = player_first
        # if move_count gets to 9 without a winner the game is a draw
        self.move_count = 0
        # set the marker for the player and computer accordingly
        if self.player_first:
            self.player_marker = "X"
            self.comp_marker = "O"
        else:
            self.player_marker = "O"
            self.comp_marker = "X"
        
    # when invoked AI_move claims the first available space for the computer
    # returns True to indicate game over
    # return False to indicate continue game	
    def AI_move(self):
        for row in range(len(self.board)): 
            for col in range(len(self.board[row])):
                if self.board[row][col] == " ":
                    return self.place_move(self.comp_marker, (row, col))
    # when invoked player_move claims the space indicated by coordinates
    # returns True to indicate game over
    # returns False to indicate continue game
    def player_move(self, coordinates):
        return self.place_move(self.player_marker, coordinates)
            
    # when invoked place_move changes the board to the indicated marker at the indicated coordinates
    # return True to indicate game over
    # return False to indicate continue game
    def place_move(self, marker, coordinates):
        self.board[coordinates[0]][coordinates[1]] = marker
        return self.check_board(coordinates)

    # when invoked print_board prints the information stored in board as a formated tic tac toe board
    def print_board(self):
        '''
        count = 0;
        for line in self.board:
            for pos in range(len(line)):
                print(line[pos], end='')
                if pos == 2:
                    print()
                else:
                    print("|", end='')
            if count < 2:
                print("_|_|_")
            else:
                print(" | |")
            count += 1
        '''
        print(self.board_to_str(), end='')

    # when invoked board_to_str converts the board into a string and returns the string object
    def board_to_str(self):
        ret_str = ""
        count = 0;
        for line in self.board:
            for pos in range(len(line)):
                ret_str += line[pos]
                if pos == 2:
                    ret_str += "\n"
                else:
                    ret_str += "|"
            if count < 2:
                ret_str += "_|_|_\n"
            else:
                ret_str += " | | \n"
            count += 1
        return ret_str

    # when invoked check_coords checks to see if the coordinates are valid for the board
    # returns True if the coordinates are valid
    # returns False if the coordinates are not valid
    def check_coords(self, coordinates):
        return self.board[coordinates[0]][coordinates[1]] == " "

    # when invoked check_board checks the board to see if there is a winner or stalemate
    # checks possible states based on the most recent move
    # if there is a winner an appropriate message is printed
    # return 1 if there is a winner 
    # return -1 if there is a stalemate
    # return 0 if there is not a winner
    def check_board(self, coordinates):
        # increment the move count
        self.move_count += 1
        # flag to indicate win condition
        win_flag = 0
        # check the row of the most recent move
        prev_pos = self.board[coordinates[0]][0]
        for i in range(len(self.board[coordinates[0]])-1):
            curr_pos = self.board[coordinates[0]][i+1]
            if curr_pos != prev_pos:
                win_flag = 0    
                break
            else:
                win_flag = 1
                prev_pos = curr_pos
        # check to see if the entire row matched
        if win_flag:
            # there was a winner
            self.print_winner(curr_pos)
            return 1

        # the row did not have a winner check the column   
        prev_pos = self.board[0][coordinates[1]]
        for i in range(len(self.board[coordinates[1]])-1):
            curr_pos = self.board[i+1][coordinates[1]]
            if curr_pos != prev_pos:
                win_flag = 0
                break
            else:
                win_flag = 1
                prev_pos = curr_pos
        # check to seee if the entire column matched
        if win_flag:
            # there was a winner
            self.print_winner(curr_pos)
            return 1
        # the column did not have a winner check to see if the coordinates match a diagonal
        if coordinates == (0,0) or coordinates == (1,1) or coordinates == (2,2):
            # check to seee if the entire diagonal matched
            if self.board[0][0] == self.board[1][1] and self.board[1][1] == self.board[2][2]:
                # there was a winner
                self.print_winner(curr_pos)
                return 1
        elif coordinates == (2,0) or coordinates == (1,1) or coordinates == (0,2):
            # check to seee if the entire diagonal matched
            if self.board[2][0] == self.board[1][1] and self.board[1][1] == self.board[0][2]:
                # there was a winner
                self.print_winner(curr_pos)
                return 1 
        # there was not a winner check for a draw
        for row in self.board:
            for col in row:
                if col == " ":
                    # there is still a blank space 
                    return 0
        # a blank space was not found there was a draw
        print("Stalemate! It could have been worse!")
        return -1
            
    
    def print_winner(self, marker):
        # check to see if the winning marker belongs to the computer or player
        if self.player_marker == marker:
            # the player won
            print("Congrats! You won!")
        else:
            # the computer won
            print("You lost! Better luck next time!")
'''
# main to test the class
def main():
    print("Lets play Tic Tac Toe!")
    input_flag = 1
    while (input_flag):
        player_first = input("Would you like to go first? Y/n: ")
        if isinstance(player_first, str):
            player_first.upper()
            if player_first == "Y" or player_first == "N":
                input_flag = 0
            else:
                print("Please make a valid selection!")
        else:
            print("Please make a valid selection!")
    if player_first == "Y":
        game = Game(1)
        moves = 0
    else:
        game = Game()
        game.AI_move()
        moves = 1
    game_flag = 1
    while(game_flag):
        game.print_board()
        input_flag = 1
        while (input_flag):
            coor_raw = input("Enter the coordinates for your move seperated by a space (e.g. \"0 2\"): ")
            try:
                coordinates_raw = coor_raw.split()
                coordinates = (int(coordinates_raw[0]), int(coordinates_raw[1]))
                if coordinates[0] >= 0 and coordinates[0] <= 2 and coordinates[1] >= 0 and coordinates[1] <= 2:
                    input_flag = 0
                else:
                    # invalid input
                    print("Please enter valid coordinates between 0 and 2!")
            except TypeError: 
                print("TypeError: Please enter valid coordinates between 0 and 2!")
        if game.player_move(coordinates):
            game_flag = 0
        moves += 1
        if moves == 9:
            break
        if game.AI_move():
            game_flag = 0
        moves += 1
        if moves == 9:
            game_flag = 0
    game.print_board()
main() 
'''
<file_sep>/TTT_Server.py
# the following code is from "Computer Networking a Top-Down Approach"
# by <NAME> Ross
# this code will be modified for the project's funstionality
'''
from socket import *
serverPort = 12000
serverSocket = socket(AF_INET, SOCK_DGRAM)
serverSocket.bind(('',serverPort))
print("The server is ready to recieve")
while True:
    message, clientAddress = serverSocket.recvfrom(2048)
    modifiedMessage = message.decode().upper()
    serverSocket.sendto(modifiedMessage.encode(), clientAddress)
'''

from socket import *
from TTT_Game import Game

import signal
import sys

server_port = 12000
server_socket = socket(AF_INET, SOCK_DGRAM)
server_socket.bind(('', server_port))


client_dict = dict()
get_coor_str = 'CRDR,Please enter the coordinates for your move sperated by a space (e.g. \"0 2\"): '
print('The server is ready to recieve')
invalid_coor_str = 'INVC,Those coordinates are invalid!\nPlease enter valid coordinates: '
discnt_client_str = 'DCNT,See you again soon!'
discnt_server_str = 'DCNT,Server Disconnected!'
win_str = 'EOG,Congrats! You won!'
loss_str = 'EOG,Sorry you lost! Better luck next time!'
stale_str = 'EOG,Stalemate! Try harder next time!'
err_str = 'ERR,Error message not recieved correctly!'
bad_connect_str = 'BCNT,You are an unregistered client!'
def signal_handler(signal, frame):
    print('You pressed Ctrl+C')
    # send a disconnect message to all clients
    for key in client_dict: 
        response = discnt_server_str
        server_socket.sendto(response.encode(), key)
    server_socket.close()
    sys.exit(0)

signal.signal(signal.SIGINT, signal_handler)

while True:
    message, current_address = server_socket.recvfrom(2048)
    # decode the message
    dec_msg = message.decode().split(',')
    ''' debug print message id'''
    print(dec_msg[0])
    if current_address not in client_dict:
        if dec_msg[0] == 'INIT':
            # the message corresponds to a new connect
            if dec_msg[1] == '1':
                # the player wants to start
                game = Game(1)
            else:
                # the computer starts
                game = Game()
                # have the computer make the first move
                game.AI_move()
            client_dict[current_address] = game
            response = get_coor_str
            board_flag = 1    
        else:
             # the message came from an un-registered client and it was not a new connect message
             response = bad_connect_str
             board_flag = 0   
    else:
        board_flag = 1
        # check the message type
        if dec_msg[0] == 'MOVE':
            # the message corresponds to a player move
            # check to see if the move is valid
            coor_raw = dec_msg[1].split()
            coordinates = (int(coor_raw[0]), int(coor_raw[1]))
            if client_dict[current_address].check_coords(coordinates):
                # the coordinates are valid place the move on the board
                game_over = client_dict[current_address].player_move(coordinates)
                # check to see if the game is over
                if game_over:
                    # the game is over check to see if win or stalemate
                    if game_over > 0:
                        # the client won
                        response = win_str
                    else:
                        # there was a stalemate
                        response = stale_str
                else:
                    # the game is not over yet have the AI make a move
                    game_over = client_dict[current_address].AI_move()
                    if game_over:
                        # the game is over check to see if win or stalemate
                        if game_over > 0:
                            # the server won
                            response = loss_str
                        else:
                            # there was a stalemate
                            response = stale_str
                    else:
                        # the game is not over prompt for a new coordinate
                        response = get_coor_str
            else:
                # the coordinates are not valid 
                response = invalid_coor_str
        elif dec_msg[0] == 'DCNT':
            # The client is discontinuing the game
            # remove the client from the client_dict
            client_dict.pop(current_address)
            response = discnt_client_str
            board_flag = 0
        else:
            # the message does not take the correct form
            response = err_str
    
    # the response has been calculated 
    # check to see if the client is still connected
    if board_flag:
        # append the game board to the message
        response = response + ',' + client_dict[current_address].board_to_str()
    # send the response back to the client
    server_socket.sendto(response.encode(), current_address)
<file_sep>/TTT_Client.py
# the following code is from "Computer Networking a Top-Down Approach"
# by Kurose and Ross
# this code will be modified for the project's functionality
'''
from socket import *
serverName = '10.0.2.15'
serverPort = 12000
clientSocket = socket(AF_INET, SOCK_DGRAM)
message = raw_input('Input lowecase sentence:')
clientSocket.sendto(message.encode(), (serverName, serverPort))
modifiedMessage, serverAddress = clientSocket.recvfrom(2048)
print(modifiedMessage.decode())
clientSocket.close()
'''
from socket import *
import argparse
import signal
import sys

server_port = 12000
client_socket = socket(AF_INET, SOCK_DGRAM)


parser = argparse.ArgumentParser(description='Play Tic Tac Toe with a remote server!')
parser.add_argument('-c', help='indicates the client will go first', action='store_true')
parser.add_argument('-s', dest='server_name', help='specifies the IP of the server, this is required', required=True)
args = parser.parse_args()


game_flag = 1

init_user_str = 'INIT,1'
init_server_str = 'INIT,0'
move_str = 'MOVE'
disconnect_str = 'DCNT'

if args.c:
    # the user wants to go first
    message = init_user_str
else:
    # the server will go first
    message = init_server_str

def signal_handler(signal, frame):
    print('You pressed Ctrl+C')
    message = disconnect_str
    client_socket.sendto(message.encode(), (args.server_name, server_port)) 
    client_socket.close()
    sys.exit(0)

signal.signal(signal.SIGINT, signal_handler)

# send the message to initilize the game
client_socket.sendto(message.encode(), (args.server_name, server_port))

error_flag = 0
while game_flag:
    prev_msg = message
    response, server_address = client_socket.recvfrom(2048)
    # decode the response
    dec_rsp = response.decode().split(',')
    # check the response type
    if dec_rsp[0] == 'CRDR' or dec_rsp[0] == 'INVC':
        # the response indicates the server wants coordinates from the user
        # print the current board 
        print(dec_rsp[2])
        # get the coordinates using the prompt string provided by the server
        input_flag = 1
        while input_flag:
            coor_str_raw = input(dec_rsp[1])
            coordinates = coor_str_raw.split()
            if len(coordinates) < 2:
                print("Please use a space!")
            elif coordinates[0].isdigit and coordinates[1].isdigit():
                # both coordinates are numbers
                # check to see if the numbers fall into the correct range
                if int(coordinates[0]) >= 0 and int(coordinates[0]) < 3 and int(coordinates[1]) >= 0 and int(coordinates[1]) < 3:
                    # the coordinates are valid for the range of the board
                    # send the coordinates in the next message
                        message = move_str + ',' + coor_str_raw
                        input_flag = 0
            # check to see if the input was not valid
            if input_flag:
                print("Please enter valid coordinates!")
    elif dec_rsp[0] == 'EOG':
        # the game is over
        # print the current board
        print(dec_rsp[2])
        # print the end of game message
        print(dec_rsp[1])
        # send a disconnect message
        message = disconnect_str
     
    elif dec_rsp[0] == 'DCNT':
        # the server is disconecting
        # print the server's disconnect message
        print(dec_rsp[1])
        game_flag = 0
        ''' debug print '''
        # print('disconnect')
    
    elif dec_rsp[0] == 'ERR':
        # the server did not recieve the message correctly 
        # check to see if there is already an active error message
        if error_flag:
            # the previous message also failed exit the program
            print("Error sending message to server!")
            message = disconnect_str
            game_flag = 0
            error_flag = 2
        else:
            # this is the first error
            # attempt to resend the previous message
            message = prev_msg
            error_flag = 1
    # check to see if the server did not disconnect
    if not dec_rsp[0] == 'DCNT':
        # send the message
        client_socket.sendto(message.encode(), (args.server_name, server_port))
print('Thank you for playing!')
client_socket.close()
