`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.
2 Answers 2
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.
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 )
-
\$\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– spyr032019年08月01日 16:11:52 +00:00Commented Aug 1, 2019 at 16:11
Explore related questions
See similar questions with these tags.