Python Forum
Tkinter Tic Tac Toe updated
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Tkinter Tic Tac Toe updated
#1
Here is the 2nd go.
Hope you enjoy.
As always, I welcome feedback.

#! /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 welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags


Reply
#2
dude the game is awesome
Reply
#3
My first thought would be rather than creating new labels in the grid could you just change the text of the existing label instead
Something like
# This will create a 3x3 label grid and bind each label to the left mouse button
        self.labels = [] # add this
        for i in range(3):
            inner_labels = [] # add this
            for j in range(3):
                self.label = tk.Label(self.gameframe, relief='solid', \
                                      borderwidth=1, width=int(350/25), height=int(300/50.1), \
                                      bg=rgbcolor((180,140,135)))
                self.label.grid(row=j, column=i, sticky='news')
                self.label.bind('<Button-1>', self.player_pick)
                inner_labels.append(self.label) # add this
            self.labels.append(inner_labels) # add this
    # 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', 51, 'bold'))
        # label.grid(column = col, row=row)
         self.labels[col][row]['text'] = text # add this
this works but the font size needs sorting out.
Reply
#4
Going to look into your suggestion. I did away with the first label creation. Now it's just frame. Label gets made when the player clicks.
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags


Reply


Forum Jump:

User Panel Messages

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