I'm making a scalable tic tac toe game in Tkinter (meaning the board size can be 2x2 up to whatever fits the screen). I'm using cget("image")
to find what mark a button has. For some reason, the win check displays very random things. I've tried a lot of semi-random things to fix it, but had no success in fixing it. Here's the code:
from tkinter import *
class XOGame:
def main_game(self):
self.__game_window = Tk()
self.__grid_size = 3 # User inputted in a different part of the code
self.__game_window.title("Tic Tac Toe (" + str(self.__grid_size) + "x"
+ str(self.__grid_size) + ")")
# this is user inputted in a different part of the program.
self.__players = ["p1", "p2"]
self.__player1 = self.__players[0]
self.__player2 = self.__players[1]
self.build_board(self.__game_window)
self.__game_window.mainloop()
def build_board(self, window):
self.__size = self.__grid_size ** 2
self.__turn_nr = 1
self.__win = False
self.__empty_square = PhotoImage(master=window,
file="rsz_empty.gif")
self.__x = PhotoImage(master=window,
file="rsz_cross.gif")
self.__o = PhotoImage(master=window,
file="rsz_nought.gif")
self.__squares = [None] * self.__size
self.create_win_check_lists()
# Building the buttons and gridding them
for i in range(self.__size):
self.__squares[i] = (Button(window, image=self.__empty_square))
row = 0
column = 0
number = 1
for j in self.__squares:
j.grid(row=row, column=column)
j.config(command=lambda index=self.__squares.index(j):
self.change_mark(index))
column += 1
if number % 3 == 0:
row += 1
column = 0
number += 1
# This is the part where the picture changing happens.
def change_mark(self, i):
"""
Function changes mark of empty button to either X or O depending on the
player in turn. It also checks if the change in mark results in a win.
:param i: The button number, whose mark is being changed
:return: None
"""
if self.__turn_nr % 2 == 1:
self.__player_in_turn = self.__player1
else:
self.__player_in_turn = self.__player2
if self.__player_in_turn == self.__player1:
self.__mark = self.__x
else:
self.__mark = self.__o
self.__squares[i].configure(image=self.__mark, state=DISABLED)
self.__turn_nr += 1
self.check_win(i)
if self.__win is True:
print("this is thought to be a win")
else:
print("the game thinks this is not a win")
# Checking if the game tied.
if self.__turn_nr == self.__size + 1 and not self.__win:
print("the game thinks it tied.")
def check_win(self, i):
"""
Checks if mark placement leads to a win.
:param i: i is the button location.
:return: None
"""
# checks row
if self.__win == False:
for row in self.__rows:
if i + 1 in row:
self.__win = self.checksameimage(row)
if self.__win == True:
break
# checks column
if self.__win == False:
for column in self.__columns:
if i + 1 in column:
self.__win = self.checksameimage(column)
if self.__win == True:
break
# if i is in a diagonal, checks one/both diagonals
if self.__win == False:
for diag in self.__diagonals:
if i + 1 in diag:
self.__win = self.checksameimage(diag)
if self.__win == True:
break
return self.__win
# checking if all the images are same
# This is likely where the issue is. Either this part or checkEqual.
def checksameimage(self, lst):
images = []
for nr in lst:
try:
images.append(self.__squares[nr].cget("image"))
except IndexError:
pass
return self.checkEqual(images)
def checkEqual(self, lst):
"""
Function checks if all elements in a list are equal. Used for checking
if the dice throws are the same.
:param lst: The list the check is performed on
:return: True/False, True if all elements are equal.
"""
if all(x == lst[0] for x in lst):
return True
else:
return False
def create_win_check_lists(self):
"""
Creates lists whose elements are lists of the locations of each spot
of the game board that belongs to a row/column/diagonal
:return:
"""
self.__rows = [[] for _ in range(self.__grid_size)]
for i in range(self.__grid_size):
self.__rows[i].append(i + 1)
for k in range(1, self.__grid_size):
self.__rows[i].append(i + 1 + self.__grid_size * k)
self.__columns = [[] for _ in range(self.__grid_size)]
for i in range(self.__grid_size):
for j in range(1, self.__grid_size + 1):
self.__columns[i].append(i * self.__grid_size + j)
self.getDiagonals(self.__columns)
def getDiagonals(self, lst):
self.__diagonals = [[], []]
self.__diagonals[0] = [lst[i][i] for i in range(len(lst))]
self.__diagonals[1] = [lst[i][len(lst) - i - 1] for i in
range(len(lst))]
def start(self):
# Function starts the first window of the game.
self.main_game()
def main():
ui = XOGame()
ui.start()
main()
The images used in the code are 125x125. Here is a link that works for 24h: https://picresize.com/b5df006025f0d8
your problem is unquestionably with indices. We can see what index each square corresponds to by changing the image to be text corresponding to the index:
Try changing this loop in build_board
:
for i in range(self.__size):
self.__squares[i] = (Button(window, image=self.__empty_square))
To instead show the index it corresponds to:
for i in range(self.__size):
self.__squares[i] = (Button(window, text=str(i)))
Then print out lst
passed to checksameimage
and you will see that the indices you are checking are all one higher than you intended.