Python Forum

Full Version: Tic Tac Toe Remake
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
I thought I would re-write my first attempt at tkinter tictactoe.
I've hit a bump. Can go through the first round but, after resetting it doesn't won't to work.
Any insight would be helpful.


I figured it out. Wasn't resetting the text to original text. which is (x, y)

What I have. Now need to research on making an ai for it. That has been a tough subject for me in the past.

#! /usr/bin/env python3
'''Tkinter TIC TAC TOE'''
 
# Do the imports
import tkinter as tk
from tkinter import ttk
import os
import random as rnd
 
 
# This is so rgb colors can be used
def rgbcolor(rgb):
    return '#%02x%02x%02x' % rgb
 
 
class Game:
    ''' Game Class '''
    def __init__(self, parent):
        # Setup the root window
        self.parent = parent
        self.parent.columnconfigure(0, weight=1)
        self.parent.rowconfigure(0, weight=1)
 
        # Create the main container frame
        self.mainframe = tk.Frame(self.parent, height=510, bg=rgbcolor((80, 40, 35)))
        self.mainframe.grid(row=0, column=0, sticky='news')
        self.mainframe.grid_columnconfigure(0, weight=3)
        self.mainframe.grid_rowconfigure(0, weight=3, )
 
        # Create the header frame
        self.headerframe = tk.Frame(self.mainframe, bg=rgbcolor((80, 40, 35)), height=40)
        self.headerframe.grid(row=0, column=0, sticky='new')
        self.headerframe.grid_columnconfigure(0, weight=3)
 
        # This will display the header text
        self.header = tk.Canvas(self.headerframe, height=100, width=600, bg=rgbcolor((80, 40, 35)), highlightthickness=0)
        self.header.create_text(600/2.12, 100/1.93, text='TIC TAC TOE', fill=rgbcolor((45,10,5)), font=('sans', int(600/15), 'bold'))
        self.header.create_text(600/2.09, 100/1.87, text='TIC TAC TOE', fill='white', font=('sans', int(600/15), 'bold'))
        self.header.grid(row=0, column=0)
 
        # Container frame for the buttons
        self.btnframe = tk.Frame(self.mainframe, bg=rgbcolor((80,40,35)), pady=15)
        self.btnframe.grid(row=3, column=0, sticky='s')
        self.btnframe.grid_columnconfigure(0, weight=3)
 
        # Create the buttons
        btn_style = ttk.Style()
        btn_style.configure('Wild.TButton', foreground='tomato', borderwidth=1, background=rgbcolor((100,60,55)), \
                            highlightthickness=1, highlightcolor=rgbcolor((100,60,55)), font='sans 16 bold')
        btn_style.map('Wild.TButton',
                background = [('active',rgbcolor((125,85,80)))],
                foreground = [('active', 'orange')]
                )
        self.btn = ttk.Button(self.btnframe, text=' Reset ', cursor='hand2', style='Wild.TButton', \
                              command=self.reset)
        self.btn.grid(row=0, column=0, sticky='s', padx=32)
 
        self.ext_btn = ttk.Button(self.btnframe, text=' Quit ', style='Wild.TButton', command=os.sys.exit)
        self.ext_btn.grid(row=0, column=1, sticky='s', padx=32)
 
        # Create a message container frame
        self.msgframe = tk.Frame(self.mainframe, borderwidth=1, bg=rgbcolor((80,40,35)))
        self.msgframe.grid(row=2, column=0, pady=8, sticky='n')
 
        # The label will display game winner
        self.msg_label = tk.Label(self.msgframe, width=44, anchor='w', fg='tomato', bg=rgbcolor((80,40,35)))
        self.msg_label.grid(row=0, column=0, sticky='nw')
 
        # Initiate the game frame
        self.play()
 
 
    # This function will create the game board
    def play(self):
        # Create the container frame for the grid
        self.gameframe = tk.Frame(self.mainframe, relief = 'solid', highlightthickness=3, \
                                  highlightcolor='brown', highlightbackground='tomato', \
                                  borderwidth=3, width=350, bg=rgbcolor((180,140,135)), height=300)
        self.gameframe.grid(row=1,column=0, sticky='n')
        self.gameframe.grid_columnconfigure(0, weight=3)
        self.gameframe.grid_rowconfigure(0, weight=3)
 
        # This will create a 3x3 label grid and bind each label to the left mouse button
        for i in range(3):
            for j in range(3):
                self.label = tk.Frame(self.gameframe, relief='solid', width=150, height=90, \
                                      borderwidth=1, bg=rgbcolor((180,140,135)))
                self.label.grid(row=j, column=i, sticky='news')
                self.label.bind('<Button-1>', self.player_pick)
 
        # Create the what is needed for a win. Three 1's for player and three 2's for computer
        self.x_win = [1, 1, 1]
        self.o_win = [2, 2, 2]
 
        # This is where either a 1 or two will be placed depending on which player went.
        # This represents our 3x3 grid
        self.grid = [[0, 0, 0],[0, 0, 0],[0, 0, 0]]
 
        # This is the grid coordinates used for the computer to make a random choice.
        # This gets updated when each player makes a choice. e.g. 1 gets removed
        # so it can not be chosen again.
        self.grid_cords = [(0, 0), (0, 1), (0, 2),
                           (1, 0), (1, 1), (1, 2),
                           (2, 0), (2, 1), (2, 2)]
 
    # Resets the game board
    def reset(self):
        self.winner_label.destroy()
        self.gameframe.destroy()
        self.play()
        # self.msg_label['text'] = ''
        self.gameframe.update()
 
    # This places either X or O on the games board
    def do_entries(self, text,row, col, fg='brown'):
        label = tk.Label(self.gameframe, fg=fg, bg=rgbcolor((180,140,135)), text=text, \
                         font=('serif', int(150/2 - 85/3), 'bold'))
        label.grid(column = col, row=row)
        # self.labels[col][row]['text'] = text
 
    # Used for player turn
    def player_pick(self, event):
 
        # Get the coordinates for the clicked grid
        x_cord = event.x_root - self.gameframe.winfo_rootx()
        y_cord = event.y_root - self.gameframe.winfo_rooty()
 
        # Store the x and y coordinates in a varible
        loc = self.gameframe.grid_location(x_cord, y_cord)
        try:
            # Call the function to place the entry to the board
            self.do_entries('X',loc[1],loc[0])
 
            # Remove the grid coordinats from the grid cords above
            self.grid_cords.remove(loc)
 
            # Store one in the self.grid so we will be able to check for a winneror tie
            self.grid[loc[1]][loc[0]] = 1
        except ValueError as error:
            pass
 
        # Computers turn
        self.computer()
 
        # Check to see if any player has won or if there is a tie.
        # Display message of the outcome
        if self.x_win in self.possibilities():
            self.gameframe.destroy()
            self.gameframe.update()
            self.winner()
        elif self.o_win in self.possibilities():
            self.gameframe.destroy()
            self.mainframe.update()
            self.winner()
        else:
            if not self.grid_cords:
                self.gameframe.destroy()
                self.gameframe.update()
                self.winner()
 
    def winner(self):
        if self.x_win in self.possibilities():
            text = 'Congradulations! You have won!'
        elif self.o_win in self.possibilities():
            text = 'Oh! I\'m sorry, you have lost'
        else:
            text = 'Wow! A tie game!'
 
        self.winner_label = tk.Label(self.parent, text=text, fg='tomato', \
                         font = 'sans 18 bold', bg=rgbcolor((80,40,35)))
        self.winner_label.grid(column=0, row=0)
 
    # The computer player setup. All is the same as for player
    def computer(self):
        if self.grid_cords:
            try:
                loc = rnd.choice(self.grid_cords)
                self.grid_cords.remove(loc)
                self.do_entries('0', loc[1], loc[0], fg=rgbcolor((150,60,20)))
                self.grid[loc[1]][loc[0]] = 2
            except ValueError as error:
                print(error)
 
    # Setup the win possibilities and return
    def possibilities(self):
        # Columns
        col1 = [self.grid[0][0], self.grid[0][1], self.grid[0][2]]
        col2 = [self.grid[0][1], self.grid[1][1], self.grid[2][1]]
        col3 = [self.grid[0][2], self.grid[1][2], self.grid[2][2]]
 
        # Rows
        row1 = [self.grid[0][0], self.grid[1][0], self.grid[2][0]]
        row2 = [self.grid[1][0], self.grid[1][1], self.grid[1][2]]
        row3 = [self.grid[2][0], self.grid[2][1], self.grid[2][2]]
 
        # Diagonals
        diag1 = [self.grid[0][0], self.grid[1][1], self.grid[2][2]]
        diag2 = [self.grid[2][0], self.grid[1][1], self.grid[0][2]]
 
        wins = [col1, col2, col3, row1, row2, row3, diag1, diag2]
        return wins
 
 
def main():
    root = tk.Tk()
    root.title('TIC TAC TOE')
    root['borderwidth'] = 2
    root['relief'] = 'solid'
    root['highlightthickness'] = 10
    root['highlightcolor'] = 'tomato'
    root['highlightbackground'] = 'tomato'
    root['bg'] = rgbcolor((80,40,35))
    root.geometry('600x510+150+150')
    root.resizable(width=False, height=False)
    Game(root)
    root.mainloop()
 
if __name__ == '__main__':
    main()
I would suggest using Buttons instead of Labels. This saves the whole "locate where the click is", etc. You can set a picked button's state to disabled.
Once the label is clicked it can't be clicked again as the coordinates are removed from the list.
Only way it can be picked again is by resetting the game.