# import tkinter to create GUI
import tkinter as tk
# import random for robot moves
import random
# import messagebox for when someone wins
from tkinter import messagebox
# create game window
window = tk.Tk()
# the game is tic tac toe
window.title("Tic Tac Toe")
# status of the game
# X always starts
current_player = "X"
# create the board for tic tac toe
# using a 2-dimensional list would prevent a lot of confusing variables in the future.
board = [["" for _ in range(3)] for _ in range(3)]
game_mode = "" # save the mode of the game. there are 2 modes, player vs player and player vs robot.
# scorboard for player x and player o. There are multi-dimensional dictionaries
player_stats = {"X": {"wins": 0, "losses": 0, "ties": 0}, "O": {"wins": 0, "losses": 0, "ties": 0}}
# function that handles button clicks for the board
def button_click(row, col):
# current_player has to be global because it could be changed in this function.
global current_player
# check if position was occupied
# if it isn't, then the click will be registered.
if board[row][col] == "":
# update the list board
board[row][col] = current_player
# update the actual display board, and show the colour as well.
buttons[row][col].config(text=current_player, fg=get_player_color(current_player))
# if the player is red, then it is blue's turn, and vice versa.
if get_player_color(current_player) == "red":
next_move_label.config(text=f"Next Move: O", font = ("Arial", 20), fg="blue")
else:
next_move_label.config(text=f"Next Move: X", font = ("Arial", 20), fg="red")
# check if game was won
if check_win(current_player):
# show dialogue who won
messagebox.showinfo("Game Over", "Player {} Won!".format(current_player))
# update scoreboard
player_stats[current_player]["wins"] += 1
player_stats[get_other_player(current_player)]["losses"] += 1
update_scoreboard()
# reset the game for another round
reset_game()
# check if there is a tie to end the game if the board is full and nobody won
elif check_tie():
messagebox.showinfo("Game Over", "Tie!Let's play another round!")
# update the stats
player_stats[current_player]["ties"] += 1
player_stats[get_other_player(current_player)]["ties"] += 1
update_scoreboard()
# reset the game for another round
reset_game()
else:
# switch to next move. if it is x, it needs to be changed to o, and vice versa.
# if it is against the robot and the current player is o, then the robot needs to move.
current_player = get_other_player(current_player)
if game_mode == "Player X Vs Robot O" and current_player == "O":
play_robot_move()
# function to check if player/robot won
def check_win(player):
# check rows
for row in range(3):
# check every row to see if everything on a row is the same
if all(board[row][col] == player for col in range(3)):
return True
# check columns
for col in range(3):
# check every column to see if everything on a column is the same
if all(board[row][col] == player for row in range(3)):
return True
# check every diagonal to see if everything on a diagonal is the same
if board[0][0] == board[1][1] == board[2][2] == player:
return True
if board[0][2] == board[1][1] == board[2][0] == player:
return True
# nothing else is met, nobody won yet. the game continues
return False
# check if tie
def check_tie():
# see if every cell isn't empty and has either x or o.
return all(all(cell != "" for cell in row) for row in board)
# function to reset the board
def reset_game():
# the following variables will be changed in the function, so they need to be global
global current_player, board
# default player is x
current_player = "X"
# the initital status of the board has to be empty
board = [["" for _ in range(3)] for _ in range(3)]
# make game board blank
for row in range(3):
for col in range(3):
# the default text needs to have a certain colour, even though it doesn't matter.
# the default has no text
buttons[row][col].config(text="", fg="black")
next_move_label.config(text=f"Next Move: X", font = ("Arial", 20), fg="red")
# player vs robot mode,make robot move
def play_robot_move():
# the robot randomly moves based off of remaining spaces
# it moves randomly to provide people a chance to win
# find a list of available moves
available_moves = [(row, col) for row in range(3) for col in range(3) if board[row][col] == ""]
# the robot instantly moves, so it is always the player's turn.
next_move_label.config(text=f"Next Move: X", font = ("Arial", 20), fg="red")
if available_moves:
# randomly select an empty cell
row, col = random.choice(available_moves)
button_click(row, col)
# switch player
def get_other_player(player):
if player == "X":
return "O"
return "X"
# get the player's colour
# I made each type to make the game easier to play and make it more fun
def get_player_color(player):
if player == "X":
return "red" # player x is red
elif player == "O":
if game_mode == "Player X Vs Robot O":
return "blue" # the robot is blue
else:
return "blue" # player o is blue
# function to update the scoreboard
def update_scoreboard():
x_wins = player_stats["X"]["wins"]
x_losses = player_stats["X"]["losses"]
x_ties = player_stats["X"]["ties"]
o_wins = player_stats["O"]["wins"]
o_losses = player_stats["O"]["losses"]
o_ties = player_stats["O"]["ties"]
x_score_label.config(text=f"Player X: Wins {x_wins},Loses {x_losses},Ties {x_ties}")
o_score_label.config(text=f"Player O: Wins {o_wins},Loses {o_losses},Ties {o_ties}")
# function to reset the scoreboard
def reset_scoreboard():
player_stats["X"]["wins"] = 0
player_stats["X"]["losses"] = 0
player_stats["X"]["ties"] = 0
player_stats["O"]["wins"] = 0
player_stats["O"]["losses"] = 0
player_stats["O"]["ties"] = 0
update_scoreboard()
# function to select player vs player mode
def select_pvp_mode():
# game_mode has to be changed, so it needs to be global
global game_mode
# change the mode to player vs player
game_mode = "Player X Vs Player O"
# change the text to say player vs player
game_mode_label.config(text="Current Mode:Player X Vs Player O")
# the game has to be reset
reset_game()
# select player vs robot mode
def select_pvr_mode():
# game_mode has to be changed, so it needs to be global
global game_mode
# change the mode to player vs robot
game_mode = "Player X Vs Robot O"
# change the text to say player vs robot
game_mode_label.config(text="Current Mode:Player X Vs Robot O")
# the game has to be reset
reset_game()
# create buttons
buttons = []
for row in range(3):
row_buttons = []
for col in range(3):
# initially create the buttons
# the font, width, height
button = tk.Button(window, text="", font=("Arial", 40), width=6, height=3,
command=lambda r=row, c=col: button_click(r, c))
# the space between the buttons
button.grid(row=row, column=col, padx=5, pady=5)
# add buttons
row_buttons.append(button)
# add buttons
buttons.append(row_buttons)
# create the scoreboard
scoreboard_frame = tk.Frame(window)
scoreboard_frame.grid(row=0, column=3, rowspan=3, padx=10, pady=10)
# add the scoreboard title
game_mode_label = tk.Label(scoreboard_frame, text="Scoreboard", font=("Arial", 20))
game_mode_label.pack(pady=5)
# add the scoreboard
# player x is red to keep the colours the same
x_score_label = tk.Label(scoreboard_frame, text="Player X: Wins 0,Loses 0,Ties 0", font=("Arial", 12), fg="red")
x_score_label.pack(pady=5)
# player o is green to keep the colours the same
o_score_label = tk.Label(scoreboard_frame, text="Player O: Wins 0,Loses 0,Ties 0", font=("Arial", 12), fg="blue")
o_score_label.pack(pady=5)
# add a button that resets the scoreboard
reset_button = tk.Button(scoreboard_frame, text="Reset Scoreboard", font=("Arial", 12), command=reset_scoreboard)
reset_button.pack(pady=10)
# add a button to select game mode
game_mode_label = tk.Label(scoreboard_frame, text="Current Mode:Player X Vs Player O", font=("Arial", 12))
game_mode_label.pack(pady=5)
# add a button for player vs player mode
pvp_button = tk.Button(scoreboard_frame, text="Player X Vs Player O", font=("Arial", 12), command=select_pvp_mode)
pvp_button.pack(pady=5)
# add a button for player vs robot mode
pvr_button = tk.Button(scoreboard_frame, text="Player X Vs Robot O", font=("Arial", 12), command=select_pvr_mode)
pvr_button.pack(pady=5)
# Show who is supposed to move next
next_move_label = tk.Label(scoreboard_frame, text="Next Move: X", font=("Arial", 20), fg="red")
next_move_label.pack(pady=30)
# run the window
window.mainloop()
Copy and paste this code into a python IDE, and you got tic tac toe!