3
\$\begingroup\$

`I made this Tic-Tac-Toe game first, by using user input in the console here: https://pastebin.com/6zLjrWcf , and now new and improved using Tkinter:

import tkinter as tk
from tkinter import ttk
import time


def create_button(relx, rely):
    button = tk.Button(width=10, height=2, command=lambda: callback(button))
    button.place(relx=relx, rely=rely)
    return button


def check_win():                     
    if (buttons[0]['text'] == buttons[1]['text'] == buttons[2]['text'] != '') or \
            (buttons[3]['text'] == buttons[4]['text'] == buttons[5]['text'] != '') or \
            (buttons[6]['text'] == buttons[7]['text'] == buttons[8]['text'] != '') or \
            (buttons[0]['text'] == buttons[3]['text'] == buttons[6]['text'] != '') or \
            (buttons[1]['text'] == buttons[4]['text'] == buttons[7]['text'] != '') or \
            (buttons[2]['text'] == buttons[5]['text'] == buttons[8]['text'] != '') or \
            (buttons[2]['text'] == buttons[4]['text'] == buttons[6]['text'] != '') or \
            (buttons[0]['text'] == buttons[4]['text'] == buttons[8]['text'] != ''):
        return True
    else:
        return False


def callback(button):
    global turn, x
    if x == 1:
        time.sleep(1)
        game.quit()
    invalid['text'] = ''
    if button['text'] != '':
        invalid['text'] = 'Invalid space try again'
        return
    button['text'] = turn
    if check_win():
        invalid['text'] = 'Player ' + turn + ' WINS!!!!!'
        x = 1
    turn = ('0' if turn == 'X' else 'X')
    label_button['text'] = 'PLAYER ' + turn + '\'S TURN.'


x = 0
turn = 'X'
game = tk.Tk()
game.title('TicTacToe')
game.geometry('700x500')
buttons = []
for i in range(1, 10):
    button_created = create_button(0.25 if i / 3 <= 1 else 0.45 if i / 3 <= 2 else 0.65, 0.2 if i in [1, 4, 7] else
                                   0.4 if i in [2, 5, 8] else 0.6)
    buttons.append(button_created)
label_button = ttk.Button(game, text='PLAYER ' + turn + '\'S TURN.', style='Fun.TButton', width=20, state='disabled')
label_button.pack(pady=30)
invalid = tk.Label(text='')
invalid.place(relx=0.4, rely=0.12)
game.mainloop()

My main question is if there is a way to compact check_win()? Also please review the rest of the code.

\$\endgroup\$
1
\$\begingroup\$

Let us begin with some layout. This is a typical grid use case so that you won't have to manually manage grid. At the lowest level, a grid structure essentially places buttons by calculating coordinates. But this gets tedious over time, that's why a grid layout is provided. Also, it was a clever way of cheking win by being symbol agnostic. A normal tic tac toe game would have used checkwin(symbol) to determine win.

On strings

To escape ' within ' ' you can use " instead. From

'\'S TURN.'

to

"'S TURN."

and you can also use string formatting to clear up some clutter.

"PLAYER {}'S TURN.".format(turn)

On layout

Modifying your create_button function to this allows a grid structure

def create_button(x, y):
    button = tk.Button(width=10, height=2, command=lambda: callback(button))
    button.grid(row=x, column=y)
    return button

Then we modify others since different layouts can't be mixed

label_button = ttk.Button(
                        game, 
                        text="PLAYER {}'S TURN.".format(turn), 
                        style='Fun.TButton', width=20, 
                        state='disabled')
label_button.grid(row=0, column=1)
invalid = tk.Label(text='')
invalid.grid(row=4, column=1)

adding the buttons can be then done as

buttons = []

buttons.append(create_button(1, 0))
buttons.append(create_button(1, 1))
buttons.append(create_button(1, 2))
buttons.append(create_button(2, 0))
buttons.append(create_button(2, 1))
buttons.append(create_button(2, 2))
buttons.append(create_button(3, 0))
buttons.append(create_button(3, 1))
buttons.append(create_button(3, 2))

You can use a loop for the row and itertools.cycle for the 0, 1, 2 if you want to simplify it.

The check_win function

  • Simplifying if

Adding a () to if statements allows you to write or without \


if ... :
    ...

to

if (...):
    ...

thus the win_function can be simplified from

if (buttons[0]['text'] == buttons[1]['text'] == buttons[2]['text'] != '') or \
(buttons[3]['text'] == buttons[4]['text'] == buttons[5]['text'] != '') or \

to

def check_win():
    if (
        (buttons[0]['text'] == buttons[1]['text'] == buttons[2]['text'] != '') or
        (buttons[3]['text'] == buttons[4]['text'] == buttons[5]['text'] != '') or
        ...
        ):
        return True
    else:
        return False
  • Simplifying values write-up

This can also be further simplified by defining a function to replace buttons[0]['text']

def btext(i):
    return buttons[i]['text']

and using it

def check_win():
    if (
        (btext(0) == btext(1) == btext(2) != '') or
        (btext(3) == btext(4) == btext(5) != '') or
        (btext(6) == btext(7) == btext(8) != '') or
        (btext(0) == btext(3) == btext(6) != '') or
        (btext(1) == btext(4) == btext(7) != '') or
        (btext(2) == btext(5) == btext(8) != '') or
        (btext(2) == btext(4) == btext(6) != '') or
        (btext(0) == btext(6) == btext(8) != '')
        ):
        return True
    else:
        return False

On architecture

A common pattern is the MVC (Model, View, Controller). While checking and updating gui directly works here, you might consider adding states in a structure like this:

board = [
    ['', '', ''],
    ['', '', ''],
    ['', '', '']
]

Operations are done on this and the gui is updated according to this.

\$\endgroup\$
-1
\$\begingroup\$

From : Python Tic Tac Toe Game

    win_commbinations = ((0, 1, 2), (3, 4, 5), (6, 7, 8), (0, 3, 6), (1, 4, 7), (2, 5, 8), (0, 4, 8), (2, 4, 6))

for a in win_commbinations:
            if board[a[0]] == board[a[1]] == board[a[2]] == "X":
                print("Player 1 Wins!\n")
                print("Congratulations!\n")
                return True

            if board[a[0]] == board[a[1]] == board[a[2]] == "O":
                print("Player 2 Wins!\n")
                print("Congratulations!\n")
                return True
        for a in range(9):
            if board[a] == "X" or board[a] == "O":
                count += 1
            if count == 9:
                print("The game ends in a Tie\n")
                return True

This alternative solution is a bit cleaner + includes a "Tie" check, which your original solution doesn't check ( just remove if you consider it irrelevant ofc )

\$\endgroup\$
  • \$\begingroup\$ You have not reviewed the code, but instead presented your own solution. While the code may improve some aspects, it does not have a tkinter GUI like the original code does. \$\endgroup\$ – spyr03 Aug 1 at 16:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.