Created
March 9, 2019 00:14
-
-
Save bendavis78/f756e0a42cc22100b3583d53c68f5256 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import tkinter as tk | |
from tkinter import messagebox | |
# We can make a "virtual" game board as a list of lists. The list contains 3 other lists (rows). | |
# Each row list contains 3 string values representing spaces. The spaces will be populated with | |
# either "X" or "O", but we start with empty strings to denote an empty space. | |
# | |
# We can access a space on the board using row and column indexes. For example, the top-left space | |
# is `board[0][0]`, the center space is `board[1][1]`, and the bottom-right space is `board[2][2]`. | |
board = [ | |
["", "", ""], | |
["", "", ""], | |
["", "", ""] | |
] | |
# We'll also store the buttons in a similiar way. This list will be auto-populated by make_button() | |
buttons = [] | |
# | |
# Game Window Setup | |
# | |
# Now we create our window and give it a title. We'll store this window in a variable called | |
# "game", so we can access it later. | |
game = tk.Tk() | |
game.title("Tic Tac Toe") | |
# Add a message area (to display text to the user). Use `message.set(text)` to change the message. | |
message = tk.StringVar() | |
message_area = tk.Label(game, textvariable=message, font="Times 20 bold", pady=20) | |
message_area.grid(row=0) | |
# Add a "holder" for our buttons | |
button_holder = tk.Frame(game) | |
button_holder.grid(row=1) | |
# | |
# Function Definitions: | |
# | |
def check_victory(row, col): | |
""" | |
Checks to see if there's a winner based on the last-played postion | |
""" | |
# check if previous move caused a win on vertical line | |
if board[0][col] == board[1][col] == board[2][col]: | |
return True | |
# check if previous move caused a win on horizontal line | |
if board[row][0] == board[row][1] == board[row][2]: | |
return True | |
# check if previous move was on the main diagonal and caused a win | |
if row == col and board[0][0] == board[1][1] == board[2][2]: | |
return True | |
# check if previous move was on the secondary diagonal and caused a win | |
if row + col == 2 and board[0][2] == board[1][1] == board[2][0]: | |
return True | |
return False | |
def make_move(row, col): | |
""" | |
Makes a move for the current player (whether human or computer). | |
This function returns `True` if the move results in a win, otherwise it returns `False`. | |
""" | |
# If the clicked space is not already occupied, set it to the current player | |
if not board[row][col]: | |
# Set "X" or "O" on our virtual game board | |
board[row][col] = game.current_player | |
# Change the button text to the current player (X or O) | |
button = buttons[row][col] | |
button.configure(text=game.current_player) | |
# Check if there's a winner, otherwise, switch the current player | |
if check_victory(row, col): | |
messagebox.showinfo("Game over!", "The winner is: " + game.current_player + "!") | |
new_game() | |
return True | |
else: | |
if game.current_player == "X": | |
game.current_player = "O" | |
else: | |
game.current_player = "X" | |
message.set(game.current_player + "'s move") | |
return False | |
def on_click(event): | |
""" | |
This function gets called any time a button is clicked. Each button has a `row` and `col` | |
attribute, which we can pass to the `make_move()` function. | |
""" | |
clicked_button = event.widget | |
row = clicked_button.row | |
col = clicked_button.col | |
# The `make_move()` function returns `True` if it results in a win. | |
# We store that result in a variable so we can use it in the next step. | |
is_winning_move = make_move(row, col) | |
if not is_winning_move: | |
# Wait 1 second before calling `ai_move()` to make it look like the computer is "thinking". | |
game.after(1000, ai_move) | |
def new_game(): | |
""" | |
Clears the game board and starts a new game | |
""" | |
# re-set the buttons list | |
buttons.clear() | |
# Remove existing button widgets | |
for widget in button_holder.winfo_children(): | |
widget.destroy() | |
# Fill the window with new buttons | |
for row in range(0, 3): | |
buttons.append([]) | |
for col in range(0, 3): | |
button = make_button(row, col) | |
buttons[row].append(button) | |
board[row][col] = "" | |
# X player goes first | |
game.current_player = "X" | |
message.set(game.current_player + "'s move") | |
def make_button(row, col): | |
""" | |
Creates a button in the given space | |
""" | |
button = tk.Button(button_holder, text=" ", font="Times 20 bold", | |
bg="gray", fg="white", height=4, width=8) | |
button.row = row | |
button.col = col | |
button.grid(row=row, column=col, stick=tk.S + tk.N + tk.E + tk.W) | |
# Call on_click() when the button is clicked | |
button.bind("<Button-1>", on_click) | |
# return the button object for use later on | |
return button | |
def ai_move(): | |
""" | |
This function contains the AI for the computer to make a move on behalf of the current player. | |
""" | |
# "DUMB" ALGORITHM: | |
for row in range(0, 3): | |
for col in range(0, 3): | |
if board[row][col] == "": | |
make_move(row, col) | |
# by returning here, we stop the function entirely (no more looping) | |
return | |
# The rest of the code is called immediately when the script starts: | |
new_game() | |
# Start the app | |
game.mainloop() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment