Python challenge - Tic-Tac-Toe Game

Python challenge - Tic-Tac-Toe Game

Author : Hamza EL YAAQOUBI

2020, May 12    

Introduction

The task is to write a program which pretends to play tic-tac-toe with the user.

To make it all easier for you, I’ve decided to simplify the game. Here are our assumptions:

  • The computer (i.e., your program) should play the game using ‘X’s

  • The user (e.g., you) should play the game using ‘O’s

  • The first move belongs to the computer - it always puts its first ‘X’ in the middle of the board

  • All the squares are numbered row by row starting with 1 (see the example session below for reference)

  • The user inputs their move by entering the number of the square they choose - the number must be valid, i.e., it must be an integer, it must be greater than 0 and less than 10, and it cannot point to a field which is already occupied

  • The program checks if the game is over - there are four possible verdicts: the game should continue, or the game ends with a tie, your win, or the computer’s win

  • The computer responds with its move and the check is repeated

  • I won’t implement any form of artificial intelligence - a random field choice made by the computer is good enough for the game

Requirement

  • We have to implement the following features :

  • The board should be stored as a three-element list, while each element is another three-element list (the inner lists represent rows) so that all of the squares may be accessed using the following syntax: board[row][column]

  • Each of the inner list’s elements can contain ‘O’, ‘X’, or a digit representing the square’s number (such a square is considered free)

  • The board’s appearance should be exactly the same as the one presented in the example bellow :

Solution

Step 1 : init the board

The following function init the Tic Tac Toe board :

def init_borad():
    global current_player
    board = [['' for x in range(3)] for i in range(3)]
    pos = 1
    for row in range(3):
        for column in range(3):
            board[row][column] = pos
            pos += 1
    
    board[1][1] = 'X'
    current_player = 'O'
    return board

Step 2 : display the board

To display the board as required, I will use the power of the print function by using its arguments sep and end.

def display_board(board):
    print('+-------' * 3, '+', sep='')
    for row in range(3):
        print('|       ' * 3, '|', sep='')
        for col in range(3):
            print('|  ', str(board[row][col])+ '   ', end='')
        print('|')
        print('|       ' * 3, '|', sep='')
        print('+-------' * 3, '+', sep='')

Step 3 : Enter a move

The next function accepts the board current status, asks the user about their move, checks the input and updates the board according to the user’s decision

def enter_move(board):
    turn_ok = False
    
    while not turn_ok:
        move = input('Enter a move (1 to 9) : ')
        
        if len(move) != 1 or move <= '0' or move > '9':
            print("Your move isn't correct, retry please ")
            continue
        
        move = int(move) - 10
        row = move // 3
        col = move % 3
        
        if board[row][col] in ['O', 'X']:
            print("Your move isn't correct, this square is occupied, please retry again !")
            continue

        turn_ok = not turn_ok
        board[row][col] = 'O'

Step 4 : Draw a move

The function draws the computer’s move and updates the board.

Firstly, we have to build a list of all the free squares on the board. This list consists of tuples , while each tuple is a pair of row and column numbers.

def free_fileds(board):
    free_squares = []
    for row in range(3):
        for column in range(3):
            if board[row][column] not in ['O', 'X']:
                free_squares.append((row, column))
    return free_squares

We can now implement our function draw_move, it’s very easy :

def draw_move(board):
    free_squares = free_fileds(board)

    free_squares_length = len(free_squares)
    if free_squares_length > 0:
        random = randrange(free_squares_length)
        row, col = free_squares[random] 
        board[row][col] = 'X'

Step 5 : Victory for

This function analyzes the board status in order to check if the player using ‘O’s or ‘X’s has won the game

To know if we have a winner or not, we must check three options :

  1. check if we have a winner in each row
  2. check if we have a winner in each column
  3. check ii we have a winner through diagonals
def victory_for(board, sign):
    # check rows
    for row in range(3):
        if board[row][0] == sign and board[row][0] == board[row][1] and board[row][1]==board[row][2]:
            return sign
    
    # check columns
    for column in range(3):
        if board[0][column] == sign and board[0][column] == board[1][column] and board[0][column]==board[2][column]:
            return sign
    
    # check diagonals
    if board[0][0] == sign and board[0][0] == board[1][1] and board[1][1] == board[2][2] or \
        board[0][2] == sign and board[0][2] == board[1][1] and board[1][1] == board[2][0]:
        return sign

    return None

Step 6 : Run the game

I have added two functions to start and play the game.

Here’s the code :

def start_the_game():
    game_status = '0'
    while game_status != '-1':
        print('*-----------------------------*')
        print('* Welcome to TicTacToe game!', ' *')
        print('* Enter 0 to start the game.', ' *')
        print('* Enter -1 to end the game.', '  *')
        print('*-----------------------------*')

        game_status = input('Enter your choice : ')

        if type(game_status) != "<class 'str'>" and game_status not in ['0', '-1']:
            print('Please enter 0 or -1')
            continue

        if game_status == '-1':
            break
        
        # main program
        board = init_borad()
        play(board)
        display_board(board)

        if winner != None :
            print()
            print('Yohoo ! The player', winner, 'won the game !')
            print()
        else:
            print('Tie game !') 
def play(board):
    free_squares = len(free_fileds(board))
    global winner
    global current_player
    
    while free_squares != 0:
        display_board(board)
        
        if current_player == 'O':
            # player turn
            enter_move(board)
        else:
            # computer turn
            draw_move(board)
        
        game_winner = victory_for(board, current_player)
        
        if game_winner != None:
            winner = game_winner
            break
        else:
            if current_player == 'O':
                current_player = 'X'
            else:
                current_player = 'O'

        free_squares = len(free_fileds(board))

Finally, run the game :

start_the_game()

Enjoy the demos !