Python Forum
My first python game : Tic-Tac-Toe
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
My first python game : Tic-Tac-Toe
#1
Hello everyone,

Here is my first python game : Tic-Tac-Toe.

I would like hints about code optimization.

#grid creation
grid = []
line = []
for i in range (3):
    for j in range (3):
        line.append(" ")
    grid.append(line)
    line = []

#grid printing
def print_grid():
    for i in range(3):
        print("|", end ="")
        for j in range(3):
            print (grid[i][j], "|", end ="")
        print("")
        
#player turn
def player_turn(turn_player1):
    if turn_player1 == True:
        turn_player1 = False
        print(f"It's {player2}'s turn")
    else:
        turn_player1 = True
        print(f"It's {player1}'s turn")        
    return turn_player1

#choosing cell
def write_cell(cell):
    cell -= 1
    i = int(cell / 3)
    j =  cell % 3   
    if turn_player1 == True:
        grid[i][j] = player1_symbol
    else:
        grid[i][j] = player2_symbol
    return grid

#checking cell
def free_cell(cell):
    cell -= 1
    i = int(cell / 3)
    j =  cell % 3
    if grid[i][j] == player1_symbol or grid[i][j] == player2_symbol:
        print("This cell is not free")
        return False
    return True

#game opening
print("Welcome to the Tic-Tac-Toe !")
print("")
print_grid()
print("")
player1 = input("Please enter name of player 1 : ")
player1_symbol = input("Please enter the symbol of player 1 : ")
player2 = input("Please enter name of player 2 : ")
player2_symbol = input("Please enter the symbol of player 2 : ")
game = True
full_grid = False
turn_player1 = False
winner = ""

#win check
def win_check(grid, player1_symbol, player2_symbol):
    full_grid = True
    player1_symbol_count = 0
    player2_symbol_count = 0
    #checking rows    
    for i in range(3):
        for j in range(3):
            if grid[i][j] == player1_symbol:
                player1_symbol_count += 1
                player2_symbol_count = 0
                if player1_symbol_count == 3:
                    game = False
                    winner = player1
                    return game, winner
            if grid[i][j] == player2_symbol:
                player2_symbol_count += 1
                player1_symbol_count = 0
                if player2_symbol_count == 3:
                    game = False
                    winner = player2
                    return game, winner
            if grid[i][j] == " ":
                full_grid = False
                
        player1_symbol_count = 0
        player2_symbol_count = 0
    #checking columns
    player1_symbol_count = 0
    player2_symbol_count = 0    
    for i in range (3):
        for j in range (3):
            for k in range (3):
                if i + k <= 2:
                    if grid[i + k][j] == player1_symbol:
                        player1_symbol_count += 1
                        player2_symbol_count = 0
                        if player1_symbol_count == 3:
                            game = False
                            winner = player1
                            return game, winner
                    if grid[i + k][j] == player2_symbol:
                        player2_symbol_count += 1
                        player1_symbol_count = 0
                        if player2_symbol_count == 3:
                            game = False
                            winner = player2
                            return game, winner
            if grid[i][j] == " ":
                full_grid = False

            player1_symbol_count = 0
            player2_symbol_count = 0
    #checking diagonals
    player1_symbol_count = 0
    player2_symbol_count = 0    
    for i in range (3):
        for j in range (3):
            for k in range (3):
                if j + k <= 2 and i + k <= 2:
                    if grid[i + k][j + k] == player1_symbol:
                        player1_symbol_count += 1
                        player2_symbol_count = 0
                        if player1_symbol_count == 3:
                            game = False
                            winner = player1
                            return game, winner
                    if grid[i + k][j + k] == player2_symbol:
                        player2_symbol_count += 1
                        player1_symbol_count = 0
                        if player2_symbol_count == 3:
                            game = False
                            winner = player2
                            return game, winner
            if grid[i][j] == " ":
                full_grid = False
            
            player1_symbol_count = 0
            player2_symbol_count = 0
            
    player1_symbol_count = 0
    player2_symbol_count = 0    
    for i in range (3):
        for j in range (3):
            for k in range (3):
                if j - k >= 0 and i + k <= 2:
                    if grid[i + k][j - k] == player1_symbol:
                        player1_symbol_count += 1
                        player2_symbol_count = 0
                        if player1_symbol_count == 3:
                            game = False
                            winner = player1
                            return game, winner
                    if grid[i + k][j - k] == player2_symbol:
                        player2_symbol_count += 1
                        player1_symbol_count = 0
                        if player2_symbol_count == 3:
                            game = False
                            winner = player2
                            return game, winner
            if grid[i][j] == " ":
                full_grid = False
        
            player1_symbol_count = 0
            player2_symbol_count = 0              
        
    #full grid or not
    if full_grid == True:
        game = False
        winner = ""
        return game, winner
    else:
        game = True
        winner = ""
        return game, winner

#game
while game == True:
    turn_player1 = player_turn(turn_player1)
    free_box = False
    while free_box == False:
        cell = int(input("Please enter a number for your case (1 to 9 from left to right and from top to bottom) : "))
        free_box = free_cell(cell)
    grid = write_cell(cell)
    print_grid()
    game, winner = win_check(grid, player1_symbol, player2_symbol)
    
#end of game
if winner == player1:
    print(f"Winner is {player1} !")
elif winner == player2:
    print(f"Winner is {player2} !")
else:
    print(f"Grid is full : equality for {player1} and {player2} !")
Thanks in advance.
Reply
#2
That's very good. Don't you want players? Class files. Or objects in the module.py. They'll have to keep track of times won or how many people are playing in the game.
Programs are like instructions or rules. Learning it gets us closer to a solution. Desired outcome. Computer talk.
Reply
#3
Understanding the game helps reduce the amount of code. Tic-tac-toe is a one player game where the players take turns being the current player. For example, your win_check() functions is very long because your program does not take advantage of the fact that the only the last play can win. After X makes a play there is no need to check of O won. Only X could win. Checking if O won is a waste of time. O can only win when O made the last play.

You start with having the user enter 1-9 to specify what cell to place their marker, but after that you treat the "grid" like it has 3 rows and 3 columns. Making rows and columns makes the game more complicated. I would make the "grid" a list of length 9. The only time I would treat it like a 3x3 matrix is when drawing the board.

Data is better than code. Your win_check is overly complicated because you use code to check all possible winning combinations. I would put most of the winning combination information in a data structure and have a short function that uses the winning combinations to check if the player won.

This is my version of your code.
# Each tuple defines a win if all cells in tuple contain players mark.
winning_combos = (
    (0, 1, 2), (3, 4, 5), (6, 7, 8),
    (0, 3, 6), (1, 4, 7), (2, 5, 8),
    (0, 4, 8), (2, 4, 6)
)


def play(player):
    """Player selects cell.  Return winning combo if player won."""
    print("\n", " | ".join(grid[:3]))
    print("---+---+---")
    print("", " | ".join(grid[3:6]))
    print("---+---+---")
    print("", " | ".join(grid[6:]))

    # Loop until player enters number for empty cell.
    while True:
        try:
            cell = int(input(f"Enter cell for {player}: "))
            if str(cell) not in grid:
                raise ValueError
            grid[cell-1] = player
            break
        except ValueError:
            print("Enter number of open cell.")

    # Return winning combo if player wins, else None.
    for combo in winning_combos:
        if all(grid[cell] == player for cell in combo):
            return combo
    return None


player1 = "X"
player2 = "O"
player = player1
grid = list("123456789")

for i in range(9):
    won = play(player)
    if won:
        print(f"Player {player} won!")
        break
    player = player1 if player == player2 else player2
else:
    # 9 moves without a win is a draw.
    print("Game ends in a draw.")
Reply
#4
Quote:That's very good. Don't you want players? Class files. Or objects in the module.py. They'll have to keep track of times won or how many people are playing in the game.

Yeah, actually I am dodging OOP concepts in my code but I will throw myself into it soon.

Quote:Understanding the game helps reduce the amount of code. Tic-tac-toe is a one player game where the players take turns being the current player. For example, your win_check() functions is very long because your program does not take advantage of the fact that the only the last play can win. After X makes a play there is no need to check of O won. Only X could win. Checking if O won is a waste of time. O can only win when O made the last play.

You start with having the user enter 1-9 to specify what cell to place their marker, but after that you treat the "grid" like it has 3 rows and 3 columns. Making rows and columns makes the game more complicated. I would make the "grid" a list of length 9. The only time I would treat it like a 3x3 matrix is when drawing the board.

Data is better than code. Your win_check is overly complicated because you use code to check all possible winning combinations. I would put most of the winning combination information in a data structure and have a short function that uses the winning combinations to check if the player won.

My second game project is Connect4 with bigger grid than Tic-Tac-Toe, that's why I didn't use winning combo list, I was preparing myself to Connect4. But it's interesting how your code is short. I think now the best is to use both, winning combo list for Tic-Tac-Toe and my win function for Connect4. Thanks to your code, I discovered the all function. I am thinking now how I will make a second version of my Tic-Tac-Toe.
Reply
#5
Quote:Yeah, actually I am dodging OOP concepts in my code but I will throw myself into it soon.


You can do saves and high scores, etc, just writing to a text file. No OOP needed. Here is a tutorial I found:

https://www.pythontutorial.net/python-ba...text-file/
Reply
#6
In connect4, every win contains the last move. Starting from the last play, count consecutive markers vertically, horizontally and along both diagonals. Again this is made simpler using a combination of data and algorithm.
    def gameOver(self, column, row):
        """Did the last move end the game?
        Search the row, column and diagonals starting at the last marker looking for
        4 contiguous markers.  Return True if found.
        """
        marker = self.board[row][column]
        for dx, dy in ((1, 0), (0, 1), (1, 1), (1, -1)):
            count = 0
            # Count contiguous markers starting at row, column
            try:
                r, c = row, column
                while self.board[r][c] == marker:
                    count += 1
                    r, c = r + dx, c + dy
            except IndexError:
                pass
            # Count contiguous markers in the other direction.
            try:
                r, c = row - dx, column - dy
                while self.board[r][c] == marker:
                    count += 1
                    r, c = r - dx, c - dy
            except IndexError:
                pass
            if count >= 4:
                return True  # Found a winner!
        return False
This same algorithm can be used for tic-tac-toe.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  From python game in terminal to a website game using bottle Njanez 0 3,907 Aug-13-2021, 01:11 PM
Last Post: Njanez

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020