I recently learned about classes
in python. I only have a brief understanding of them, but I think it's good enough for me to write a tic tac toe program (been working on this for the last week or so). My title may be a little misleading (again, because I only have a brief understanding...) but I think you'll get the point when you see my code. I don't have any concerns with my code, but if you have any suggestions/criticism please don't hesitate to let me know.
my code:
from tkinter import *
import numpy as np
size_of_board = 600
size_of_symbol = (size_of_board / 3 - size_of_board / 8) / 2
thickness_of_symbol = 50
colour_of_X = '#FFB6C1'
colour_of_O = '#2ADCCB'
score_board_colour = '#8A2BE2'
class Tic_Tac_Toe():
# ------------------------------------------------------------------
# initialize functions:
# ------------------------------------------------------------------
def __init__(self):
self.window = Tk()
self.window.title('Tic-Tac-Toe')
self.canvas = Canvas(self.window, width = size_of_board, height = size_of_board)
self.canvas.pack()
# input from user in the form of clicks
self.window.bind('<Button-1>', self.click)
self.initialize_board()
self.player_X_turns = True
self.board_status = np.zeros(shape = (3, 3))
self.player_X_starts = True
self.reset_board = False
self.gameover = False
self.tie = False
self.X_wins = False
self.O_wins = False
self.X_score = 0
self.O_score = 0
self.tie_score = 0
def mainloop(self):
self.window.mainloop()
def initialize_board(self):
for i in range(2):
self.canvas.create_line((i + 1) * size_of_board / 3, 0, (i + 1) * size_of_board / 3, size_of_board)
for i in range(2):
self.canvas.create_line(0, (i + 1) * size_of_board / 3, size_of_board, (i + 1) * size_of_board / 3)
def play_again(self):
self.initialize_board()
self.player_X_starts = not self.player_X_starts
self.player_X_turns = self.player_X_starts
self.board_status = np.zeros(shape = (3, 3))
# ------------------------------------------------------------------
# the modules required to draw required game based object on canvas
# ------------------------------------------------------------------
def draw_O(self, logical_position):
logical_position = np.array(logical_position)
# logical_position = grid value on the board
# grid_position = actual pixel values of the center of the grid
grid_position = self.convert_logical_to_grid_position(logical_position)
self.canvas.create_oval(grid_position[0] - size_of_symbol, grid_position[1] - size_of_symbol,
grid_position[0] + size_of_symbol, grid_position[1] + size_of_symbol,
width = thickness_of_symbol, outline = colour_of_O)
def draw_X(self, logical_position):
grid_position = self.convert_logical_to_grid_position(logical_position)
self.canvas.create_line(grid_position[0] - size_of_symbol, grid_position[1] - size_of_symbol,
grid_position[0] + size_of_symbol, grid_position[1] + size_of_symbol,
width = thickness_of_symbol, fill = colour_of_X)
self.canvas.create_line(grid_position[0] - size_of_symbol, grid_position[1] + size_of_symbol,
grid_position[0] + size_of_symbol, grid_position[1] - size_of_symbol,
width = thickness_of_symbol, fill = colour_of_X)
def display_gameover(self):
if self.X_wins:
self.X_score += 1
text = 'Player 1 (X) has won'
colour = colour_of_X
elif self.O_wins:
self.O_score += 1
text = 'Player 2 (O) has won'
colour = colour_of_O
else:
self.tie_score += 1
text = 'Draw!!'
colour = 'coral'
self.canvas.delete('all')
self.canvas.create_text(size_of_board / 2, size_of_board / 3, font = 'cmr 40 bold', fill = colour, text = text)
score_text = 'Scores \n'
self.canvas.create_text(size_of_board / 2, 5 * size_of_board / 8, font = 'cmr 30 bold', fill =
score_board_colour, text = score_text)
score_text = 'Player 1 (X) : ' + str(self.X_score) + '\n'
score_text += 'Player 2 (O) : ' + str(self.O_score) + '\n'
score_text += 'Tie : ' + str(self.tie_score)
self.canvas.create_text(size_of_board / 2, 3 * size_of_board / 4, font = 'cmr 20 bold', fill =
score_board_colour, text = score_text)
self.reset_board = True
score_text = 'Click to play again \n'
self.canvas.create_text(size_of_board / 2, 15 * size_of_board / 16, font = 'cmr 10 bold', fill = 'orange',
text = score_text)
# ------------------------------------------------------------------
# the modules required to carry out game logic
# ------------------------------------------------------------------
def convert_logical_to_grid_position(self, logical_position):
logical_position = np.array(logical_position, dtype = int)
return (size_of_board / 3) * logical_position + size_of_board / 6
def convert_grid_to_logical_position(self, grid_position):
grid_position = np.array(grid_position)
return np.array(grid_position // (size_of_board / 3), dtype = int)
def is_grid_occupied(self, logical_position):
if self.board_status[logical_position[0]][logical_position[1]] == 0:
return False
else:
return True
def is_winner(self, player):
player = -1 if player == 'X' else 1
# three in a row
for i in range(3):
if self.board_status[i][0] == self.board_status[i][1] == self.board_status[i][2] == player:
return True
if self.board_status[0][i] == self.board_status[1][i] == self.board_status[2][i] == player:
return True
# diagonals
if self.board_status[0][0] == self.board_status[1][1] == self.board_status[2][2] == player:
return True
if self.board_status[0][2] == self.board_status[1][1] == self.board_status[2][0] == player:
return True
return False
def is_tie(self):
r, c = np.where(self.board_status == 0)
tie = False
if len(r) == 0:
tie = True
return tie
def is_gameover(self):
# either someone is declared the winner or the entire grid is occupied
self.X_wins = self.is_winner('X')
if not self.X_wins:
self.O_wins = self.is_winner('O')
if not self.O_wins:
self.tie = self.is_tie()
gameover = self.X_wins or self.O_wins or self.tie
if self.X_wins:
print('X wins')
if self.O_wins:
print('O wins')
if self.tie:
print('It\'s a tie')
return gameover
def click(self, event):
grid_position = [event.x, event.y]
logical_position = self.convert_grid_to_logical_position(grid_position)
if not self.reset_board:
if self.player_X_turns:
if not self.is_grid_occupied(logical_position):
self.draw_X(logical_position)
self.board_status[logical_position[0]][logical_position[1]] = -1
self.player_X_turns = not self.player_X_turns
else:
if not self.is_grid_occupied(logical_position):
self.draw_O(logical_position)
self.board_status[logical_position[0]][logical_position[1]] = 1
self.player_X_turns = not self.player_X_turns
# check if the game has concluded
if self.is_gameover():
self.display_gameover()
# print('Done')
else: # Play Again
self.canvas.delete('all')
self.play_again()
self.reset_board = False
play_game = Tic_Tac_Toe()
play_game.mainloop()