Tic Tac Toe-Spiel in Python
Ein einfaches Tic Tac Toe-Spiel mit grafischer Benutzeroberfläche für zwei Spieler. Dabei zeigt das Fenster neben dem Spielfeld den aktuellen Spieler an, sowie den Ausgang des Spiels und einer Revanche-Nachfrage. Mehr Features gibt’s in meinem nächsten Beitrag “Upgrade: Neue Features für das Tic Tac Toe-Spiel“.
Das alles wird mit Python und dessen Bibliothek tkinter
umgesetzt.
- Grundvoraussetzungen
- Tic Tac Toe
- Aufbau des Spielfelds
- Fenster erzeugen
- Spielfeld mit Knöpfen einsetzen
- Überprüfung der Ausgangsmöglichkeiten
- Reihen, Spalten und Diagonalen prüfen
- Gleichstand prüfen
- Deaktivierung der Knöpfe
- Revanche-Nachfrage
- Spielfeld zurücksetzen
- Styling der Schrift
- Der vollständige Code und Download
Grundvoraussetzungen
- Rechner, auf dem python3 installiert ist
- Entwicklungsumgebung, die mit Python umgehen kann
- IDLE – Python-Entwicklungsumgebung, die je nach Installation von Python gleich mit installiert wird (Download der neuesten Version von python.org)
- Visual Studio Code mit Python-Plugin – weit verbreitete OpenSource-Entwicklungsumgebung, die die nötigen Plugins selbst vorschlägt und auf Nachfrage installiert
- prinzipiell egal, welche Entwicklungsumgebung – das ist Geschmackssache 😉
Tic Tac Toe
Tic Tac Toe ist ein Brettspiel für zwei Personen mit simplem Aufbau. Beim “Spielbrett” handelt es sich um ein Raster, das 9 Felder eröffnet. Gespielt wird abwechselnd, wobei bei jedem Zug das Zeichen (meist X oder O) des jeweiligen Spielers in eines der Kästchen eingetragen wird. Gewonnen hat, wer zuerst 3 Zeichen in einer Reihe hat.
Die Ausgangsmöglichkeiten sehen zum Beispiel so aus:
X | O | O |
X | ||
X |
X | X | X |
O | ||
O |
X | O | |
X | O | |
X |
X | O | X |
X | O | X |
O | X | O |
Aufbau des Spielfelds
Grundidee
Das Spielfeld besteht, wie oben bereits erläutert aus 9 gleichen Quadraten, in die das jeweilige Symbol eingetragen wird. Hier bietet es sich an, 9 quadratische Knöpfe zu erzeugen und diese in einem Raster anzuordnen. Dabei wird nach einem Klick auf den Knopf das Feld markiert. Da bei Tic Tac Toe immer abwechselnd gespielt wird, kann der Code nach einem Klick automatisch von der einen zur anderen Markierung springen.
Ein Fenster erzeugen
Die grafische Benutzeroberfläche soll nachher in einem Fenster erscheinen, das nach dem Start des Programms automatisch geöffnet wird. Dafür muss zunächst tkinter
importiert werden.
import tkinter as tk
Anschließend wird das Objekt des Fensters erzeugt und durch die mainloop
nach dem Programmstart geöffnet.
import tkinter as tk
window = tk.Tk()
window.mainloop()
Nun kann das Fenster noch an das Spiel angepasst werden. Es wird verhindert, dass das Fenster vergrößert oder verkleinert wird, sodass das Spielfeld zu sehen ist und nicht verzerrt wird. Außerdem wird der Titel des Fensters zu “Tic Tac Toe” geändert.
import tkinter as tk
window = tk.Tk()
window.resizable(False, False)
window.title("Tic Tac Toe")
window.mainloop()
Die Größe des Fensters wird nachher über die Größe der Knöpfe definiert.
Das Spielfeld einsetzen
Die Knöpfe werden in einen sogenannten Frame eingesetzt. Dadurch sind alle 9 Knöpfe zusammengefasst in einem Element, während die Anordnung der Knöpfe im Gitter einfach ermöglicht wird.
Daher wird zunächst der Frame erzeugt und mit der Funktion .pack()
in das Fenster eingesetzt. Da der Frame noch nichts enthält ist zunächst nichts sichtbar – also nicht wundern ;).
import tkinter as tk
window = tk.Tk()
window.resizable(False, False)
window.title("Tic Tac Toe")
game_setup = tk.Frame(window)
game_setup.pack()
window.mainloop()
Knöpfe erzeugen und einsetzen
Damit nicht jeder Knopf einzeln erzeugt und eingesetzt werden muss, wird für diesen Zweck eine Klasse erstellt, die zudem die Parameter jedes einzelnen Knopfes festlegt. Dies sieht so aus:
import tkinter as tk
window = tk.Tk()
window.resizable(False, False)
window.title("Tic Tac Toe")
game_setup = tk.Frame(window)
class XO_tag:
def __init__(self, x, y):
self.x = x
self.y = y
self.value = None
self.button = tk.Button(
game_setup,
text = "",
width = 10,
height = 5,
bg = "white",
fg = "black",
command = self.set_tag,
)
self.button.grid(row = x, column = y, padx = 2, pady = 2)
game_setup.pack()
window.mainloop()
Erläuterung: Es wird die Klasse XO_tag
erstellt. Wenn ein neues Objekt dieser Klasse erstellt wird, wird die Funktion __init__(self, x, y)
aufgerufen, wobei die Übergabeparameter x und y bei der Objekterzeugung übergeben werden müssen. Jedes Objekt dieser Klasse enthält die Attribute x
, y
, value
und button
. Dabei entsprechen x und y den Koordinaten des Knopfes im Raster des Spielfelds, während value
dem Wert des Buttons (default ist None, nach einem Klick entspricht dieser X oder O) entspricht. Mit tk.Button()
wird ein Knopf erstellt, dem wiederum verschiedene Attribute mitgegeben werden können, wobei es sich bei game_setup
um den Frame handelt, in den der Knopf eingesetzt werden soll. Durch die Funktion .grid()
des button
wird der Knopf in das Gitter des Frames eingefügt, in die x
-te Reihe und die y
-te Spalte, während der Abstand in alle Richtung 2 entspricht.
Jetzt müssen alle Knöpfe erstellt werden, wobei die x
– und y
-Koordinaten übergeben werden. Dafür werden zwei for
-Schleifen ineinander verschlungen.
import tkinter as tk
window = tk.Tk()
window.resizable(False, False)
window.title("Tic Tac Toe")
game_setup = tk.Frame(window)
class XO_tag:
def __init__(self, x, y):
self.x = x
self.y = y
self.value = None
self.button = tk.Button(
game_setup,
text = "",
width = 10,
height = 5,
bg = "white",
fg = "black",
)
self.button.grid(row = x, column = y, padx = 2, pady = 2)
for x in range(3):
for y in range(3):
XO_tag(x, y)
game_setup.pack()
window.mainloop()
Funktion der Knöpfe
Wenn ein Knopf gedrückt wird, soll das aktuelle Zeichen auf den Knopf geschrieben werden, sofern dieser noch nicht gedrückt wurde.
current_tag = "X"
def set_tag(self):
global current_tag
if self.value == None:
self.value = current_tag
if current_tag == "X":
self.button.configure(text=current_tag, fg = "red")
new_tag = "O"
else:
self.button.configure(text=current_tag, fg = "blue")
new_tag = "X"
current_tag = new_tag
which_player.configure(text = 'Player ' + str(current_tag))
which_player = tk.Label(window, text = 'Player ' + str(current_tag))
which_player.pack()
Erläuterung: Zuerst wird das aktuelle Zeichen festgelegt, hier “X”, mit dem das Spiel begonnen wird. Wenn der Wert value
des angeklickten Knopfes button None
ist, dann wird dieser Wert mit dem aktuellen Zeichen überschrieben. Wenn “X” gerade an der Reihe ist, dann wird der Knopf mit einem roten “X” beschrieben, bei einem “O” dementsprechend mit einem blauen “O”. Je nach dem wird dann das aktuelle Zeichen auf das gegensätzliche Zeichen geändert. Zudem wird das Label, also die Beschriftung which_player
nach jedem Klick aktualisiert und zeigt an, welcher Spieler am Zug ist.
Um die Funktion set_tag(self)
aufzurufen, wird der button
aus der Klasse über das Attribut command
so modifiziert, dass der Knopf bei einem Klick diese Funktion aufruft. Dies geschieht über die folgende Zeile:
self.button = tk.Button(command = self.set_tag)
Der zusammengesetzte Code:
import tkinter as tk
window = tk.Tk()
window.resizable(False, False)
window.title("Tic Tac Toe")
game_setup = tk.Frame(window)
current_tag = "X"
class XO_tag:
def __init__(self, x, y):
self.x = x
self.y = y
self.value = None
self.button = tk.Button(
game_setup,
text = "",
width = 10,
height = 5,
bg = "white",
fg = "black",
command = self.set_tag,
)
self.button.grid(row = x, column = y, padx = 2, pady = 2)
def set_tag(self):
global current_tag
if self.value == None:
self.value = current_tag
if current_tag == "X":
self.button.configure(text=current_tag, fg = "red")
new_tag = "O"
else:
self.button.configure(text=current_tag, fg = "blue")
new_tag = "X"
current_tag = new_tag
which_player.configure(text = 'Player ' + str(current_tag))
for x in range(3):
for y in range(3):
XO_tag(x, y)
which_player = tk.Label(window, text = 'Player ' + str(current_tag))
which_player.pack()
game_setup.pack()
window.mainloop()
Überprüfung der Ausgangsmöglichkeiten
Nach jedem Klick, also nach jeder gesetzten Markierung muss das Spiel auf die Ausgangsmöglichkeiten hin geprüft werden. Dabei muss überprüft werden, ob der Spieler gewonnen hat, also ob drei gleiche Markierungen in einer Reihe gesetzt wurden, oder ob das Spielfeld voll ist und das Spiel somit mit einem Unentschieden beendet ist.
Grundvoraussetzung
Um die eingetragenen Zeichen überprüfen zu können, müssen diese irgendwo eingetragen werden. Dafür eignet sich ein zweidimensionales Array, in das der jeweilige Wert nach einem Knopfdruck eingetragen wird. Dieses Array wird beispielsweise folgendermaßen initlialisiert:
XO_tags = [["-", "-", "-"],
["-", "-", "-"],
["-", "-", "-"]]
In dieser Darstellung ist die Struktur des zweidimensionalen Arrays am Besten erkennbar. Diese Methode ist für die Überprüfung eines Gewinns, bei dem man oft über dieses Array iterieren muss effizienter als wenn man versuchen würde, self.value
auszulesen, da hierbei das ganze Objekt in dem Array gespeichert werden würde.
Nach jedem Klick wird die aktuelle Markierung an der entsprechenden Koordinate des Knopfes in das Array eingetragen.
def set_tag(self):
global current_tag
if self.value == None:
self.value = current_tag
XO_tags[self.x][self.y] = current_tag
if current_tag == "X":
self.button.configure(text=current_tag, fg = "red")
new_tag = "O"
else:
self.button.configure(text=current_tag, fg = "blue")
new_tag = "X"
current_tag = new_tag
which_player.configure(text = 'Player ' + str(current_tag))
Im bisherigen Code:
import tkinter as tk
window = tk.Tk()
window.resizable(False, False)
window.title("Tic Tac Toe")
game_setup = tk.Frame(window)
XO_tags = [["-", "-", "-"],
["-", "-", "-"],
["-", "-", "-"]]
current_tag = "X"
class XO_tag:
def __init__(self, x, y):
self.x = x
self.y = y
self.value = None
self.button = tk.Button(
game_setup,
text = "",
width = 10,
height = 5,
bg = "white",
fg = "black",
command = self.set_tag,
)
self.button.grid(row = x, column = y, padx = 2, pady = 2)
def set_tag(self):
global current_tag
if self.value == None:
self.value = current_tag
XO_tags[self.x][self.y] = current_tag
if current_tag == "X":
self.button.configure(text=current_tag, fg = "red")
new_tag = "O"
else:
self.button.configure(text=current_tag, fg = "blue")
new_tag = "X"
current_tag = new_tag
which_player.configure(text = 'Player ' + str(current_tag))
for x in range(3):
for y in range(3):
XO_tag(x, y)
which_player = tk.Label(window, text = 'Player ' + str(current_tag))
which_player.pack()
game_setup.pack()
window.mainloop()
Reihen überprüfen
Es müssen alle drei Reihen geprüft werden, wobei der aktuelle Spieler gewonnen hat, wenn seine Markierung in jedem Feld der Reihe gesetzt wurde. Das bedeutet, dass der Inhalt jedes Arrays der zweiten Dimension nur der aktuellen Markierung entsprechen darf. Die Gewinnmöglichkeiten im Array sehen folgendermaßen aus:
XO_tags = [["X", "X", "X"],
["-", "-", "-"],
["-", "-", "-"]]
XO_tags = [["-", "-", "-"],
["X", "X", "X"],
["-", "-", "-"]]
XO_tags = [["-", "-", "-"],
["-", "-", "-"],
["X", "X", "X"]]
Da man ja nicht die gesamte Überprüfung von Hand tippen möchte, sollte diese möglichst automatisch mit wenig Code umgesetzt werden. In diesem Fall bietet sich eine for
-Schleife an. Damit wird dreimal – für jedes Array einmal – jedes Feld des Arrays mit der aktuellen Markierung verglichen. Wenn alle drei Felder dieser entsprechen, wird True
zurückgegeben, also hat der Spieler gewonnen.
def check_rows(current_tag):
for i in range(3):
if (XO_tags[i][0] == current_tag) and (XO_tags[i][1] == current_tag) and (XO_tags[i][2] == current_tag):
return True
return False
Spalten überprüfen
Es müssen alle drei Reihen geprüft werden, wobei der aktuelle Spieler gewonnen hat, wenn seine Markierung in jedem Feld der Reihe gesetzt wurde. Das bedeutet, dass der Inhalt derselben Stelle jedes Arrays der zweiten Dimension der aktuellen Markierung entsprechen muss. Die Gewinnmöglichkeiten im Array sehen folgendermaßen aus:
XO_tags = [["X", "-", "-"],
["X", "-", "-"],
["X", "-", "-"]]
XO_tags = [["-", "X", "-"],
["-", "X", "-"],
["-", "X", "-"]]
XO_tags = [["-", "-", "X"],
["-", "-", "X"],
["-", "-", "X"]]
Auch hier bietet sich eine for
-Schleife an. Dabei handelt es sich um dieselbe wie die der Überprüfung der Reihen, mit dem Unterschied, dass die Indexe vertauscht werden müssen.
def check_columns(current_tag):
for i in range(3):
if (XO_tags[0][i] == current_tag) and (XO_tags[1][i] == current_tag) and (XO_tags[2][i] == current_tag):
return True
return False
Diagonalen überprüfen
Um die Diagonalen zu überprüfen müssen einmal die erste Stelle des ersten Arrays, die zweite Stelle des zweiten Arrays und die dritte Stelle des dritten Arrays mit der aktuellen Markierung übereinstimmen sowie einmal die dritte Stelle des ersten Arrays, die zweite Stelle des zweiten Arrays und die erste Stelle des dritten Arrays. Also folgendermaßen:
XO_tags = [["X", "-", "-"],
["-", "X", "-"],
["-", "-", "X"]]
XO_tags = [["-", "-", "X"],
["-", "X", "-"],
["X", "-", "-"]]
Der Einfachheit halber wird diese Überprüfung hier “von Hand” durchgeführt. Das heißt, dass die Indexe in diesem Fall von Hand angegeben werden.
def check_diagonals(current_tag):
if (XO_tags[0][0] == current_tag) and (XO_tags[1][1] == current_tag) and (XO_tags[2][2] == current_tag):
return True
elif (XO_tags[0][2] == current_tag) and (XO_tags[1][1] == current_tag) and (XO_tags[2][0] == current_tag):
return True
else:
return False
Gleichstand überprüfen
Wie oben bereits erklärt, tritt ein Gleichstand dann ein, wenn bisher kein Spieler drei Markierungen in einer Reihe setzen konnte, das Spielfeld allerdings gefüllt ist. Dabei wird die Überprüfung mithilfe von zwei ineinander verschachtelten for
-Schleifen durchgeführt – ähnlich wie beim Aufsetzen des Spielfelds. Das Spielfeld ist gefüllt, wenn alle Felder ungleich “-” sind.
def check_draw():
for i in range(3):
for j in range(3):
if not ((XO_tags[i][j] == "X") or (XO_tags[i][j] == "O")):
return False
return True
Funktionen zusammenfassen
Damit nach einem Knopfdruck nicht vier verschiedene Funktionen aufgerufen werden müssen, werden alle vier Überprüfungen in einer Funktion check_win()
zusammengefasst.
def check_win(current_tag):
if check_rows(current_tag) == False:
if check_columns(current_tag) == False:
if check_diagonals(current_tag) == False:
if check_draw() == False:
return False
elif check_draw() == True:
return "draw"
return True
Erläuterung: Zuerst werden die Reihen überprüft. Wenn keine der Reihen drei gleiche Markierungen enthält, werden die Spalten überprüft. Wenn keine der Spalten drei gleiche Markierungen enthält, werden die zwei Diagonalen überprüft. Wenn auch die Diagonalen keine gleichen Markierungen vorweisen können, wird liegt kein Gewinn vor und es wird False
zurückgegeben. Wenn die Überprüfung auf Unentschieden True
zurückgibt, wird "draw"
zurückgegeben. In jedem anderen Fall, also wenn die Überprüfung der Reihen, Spalten oder Diagonalen True
zurückgibt, liegt ein Gewinn vor und es wird True
zurückgegeben.
In den Code einsetzen
Nach jedem Klick muss ein möglicher Gewinn überprüft werden, also check_win(current_tag)
aufgerufen werden. Zudem wird ein Text win_label
eingefügt, wobei der Text je nach return
-Wert von check_win
verändert wird. Wenn ein Gewinn vorliegt, wird der Spieler angezeigt und wenn ein Unentschieden vorliegt wird dementsprechend “Draw !!” ausgegeben.
import tkinter as tk
window = tk.Tk()
window.resizable(False, False)
window.title("Tic Tac Toe")
game_setup = tk.Frame(window)
XO_tags = [["-", "-", "-"],
["-", "-", "-"],
["-", "-", "-"]]
current_tag = "X"
win_label = tk.Label(window)
def check_rows(current_tag):
for i in range(3):
if (XO_tags[i][0] == current_tag) and (XO_tags[i][1] == current_tag) and (XO_tags[i][2] == current_tag):
return True
return False
def check_columns(current_tag):
for i in range(3):
if (XO_tags[0][i] == current_tag) and (XO_tags[1][i] == current_tag) and (XO_tags[2][i] == current_tag):
return True
return False
def check_diagonals(current_tag):
if (XO_tags[0][0] == current_tag) and (XO_tags[1][1] == current_tag) and (XO_tags[2][2] == current_tag):
return True
elif (XO_tags[0][2] == current_tag) and (XO_tags[1][1] == current_tag) and (XO_tags[2][0] == current_tag):
return True
else:
return False
def check_draw():
for i in range(3):
for j in range(3):
if not ((XO_tags[i][j] == "X") or (XO_tags[i][j] == "O")):
return False
return True
def check_win(current_tag):
if check_rows(current_tag) == False:
if check_columns(current_tag) == False:
if check_diagonals(current_tag) == False:
if check_draw() == False:
return False
elif check_draw() == True:
return "draw"
return True
class XO_tag:
def __init__(self, x, y):
self.x = x
self.y = y
self.value = None
self.button = tk.Button(
game_setup,
text = "",
width = 10,
height = 5,
bg = "white",
fg = "black",
command = self.set_tag,
)
self.button.grid(row = x, column = y, padx = 2, pady = 2)
def set_tag(self):
global current_tag
if self.value == None:
self.value = current_tag
XO_tags[self.x][self.y] = current_tag
if current_tag == "X":
self.button.configure(text=current_tag, fg = "red")
new_tag = "O"
else:
self.button.configure(text=current_tag, fg = "blue")
new_tag = "X"
if not check_win(current_tag) == False:
if check_win(current_tag) == True:
win_label.configure(text = str(current_tag) + " wins !!")
else:
win_label.configure(text = "Draw !!")
win_label.pack()
current_tag = new_tag
which_player.configure(text = 'Player ' + str(current_tag))
for x in range(3):
for y in range(3):
XO_tag(x, y)
which_player = tk.Label(window, text = 'Player ' + str(current_tag))
which_player.pack()
game_setup.pack(padx = 5, pady = 5)
window.mainloop()
Die Knöpfe des Spielfelds deaktivieren
Nach einem Gewinn beziehungsweise Unentschieden sollen die Knöpfe des Spielfelds nicht mehr anklickbar sein. Dafür wird allerdings ein neues Array benötigt, in dem sich die Objekte der Knöpfe befinden, sodass über diese iteriert werden kann.
all_XO = []
for x in range(3):
for y in range(3):
XO_tag(x, y)
all_XO.append(XO_tag(x, y))
Wenn nun ein Gewinn oder ein Unentschieden vorliegt, wird über das Array all_XO
iteriert und die Einstellungen jedes einzelnen Knopfes bearbeitet.
for button in all_XO:
button.button.configure(state = "disabled")
Hier im bisherigen Code:
import tkinter as tk
window = tk.Tk()
window.resizable(False, False)
window.title("Tic Tac Toe")
game_setup = tk.Frame(window)
all_XO = []
XO_tags = [["-", "-", "-"],
["-", "-", "-"],
["-", "-", "-"]]
current_tag = "X"
win_label = tk.Label(window)
def check_rows(current_tag):
for i in range(3):
if (XO_tags[i][0] == current_tag) and (XO_tags[i][1] == current_tag) and (XO_tags[i][2] == current_tag):
return True
return False
def check_columns(current_tag):
for i in range(3):
if (XO_tags[0][i] == current_tag) and (XO_tags[1][i] == current_tag) and (XO_tags[2][i] == current_tag):
return True
return False
def check_diagonals(current_tag):
if (XO_tags[0][0] == current_tag) and (XO_tags[1][1] == current_tag) and (XO_tags[2][2] == current_tag):
return True
elif (XO_tags[0][2] == current_tag) and (XO_tags[1][1] == current_tag) and (XO_tags[2][0] == current_tag):
return True
else:
return False
def check_draw():
for i in range(3):
for j in range(3):
if not ((XO_tags[i][j] == "X") or (XO_tags[i][j] == "O")):
return False
return True
def check_win(current_tag):
if check_rows(current_tag) == False:
if check_columns(current_tag) == False:
if check_diagonals(current_tag) == False:
if check_draw() == False:
return False
elif check_draw() == True:
return "draw"
return True
class XO_tag:
def __init__(self, x, y):
self.x = x
self.y = y
self.value = None
self.button = tk.Button(
game_setup,
text = "",
width = 10,
height = 5,
bg = "white",
fg = "black",
command = self.set_tag,
)
self.button.grid(row = x, column = y, padx = 2, pady = 2)
def set_tag(self):
global current_tag
if self.value == None:
self.value = current_tag
XO_tags[self.x][self.y] = current_tag
if current_tag == "X":
self.button.configure(text=current_tag, fg = "red")
new_tag = "O"
else:
self.button.configure(text=current_tag, fg = "blue")
new_tag = "X"
if not check_win(current_tag) == False:
if check_win(current_tag) == True:
win_label.configure(text = str(current_tag) + " wins !!")
else:
win_label.configure(text = "Draw !!")
win_label.pack()
for button in all_XO:
button.button.configure(state = "disabled")
current_tag = new_tag
which_player.configure(text = 'Player ' + str(current_tag))
for x in range(3):
for y in range(3):
XO_tag(x, y)
all_XO.append(XO_tag(x, y))
which_player = tk.Label(window, text = 'Player ' + str(current_tag))
which_player.pack()
game_setup.pack(padx = 5, pady = 5)
window.mainloop()
Revanche-Nachfrage
Nach Spielende soll je nach Wahl das Spielfeld zurückgesetzt werden wenn eine Revanche stattfinden soll, oder nicht. Hierfür wird eine neuer Frame erzeugt, in dem sich ein Label und zwei Button befinden.
request_frame = tk.Frame(window, borderwidth = 2, relief = "sunken")
def rematch():
request_label = tk.Label(request_frame, text = "Rematch ??")
request_label.grid(row = 0, padx = 5, pady = 5)
button_yes = tk.Button(request_frame, text = "Yes", command = lambda: [request_frame.pack_forget(), reset()])
button_yes.grid(row = 1, column = 0, padx = (0, 0), pady = 5)
button_no = tk.Button(request_frame, text = "No", command = lambda: [request_frame.pack_forget()])
button_no.grid(row = 1, column = 1, padx = (0, 30), pady = 5)
request_frame.pack(padx = 5, pady = 5)
Wenn der Knopf für eine Revanche gedrückt wird, dann wird der Frame wieder aus dem Fenster genommen und das Spielfeld mit der Funktion reset()
zurückgesetzt. Wenn der Knopf für keine Revanche gedrückt wird, wird lediglich der Frame entfernt. Dies geschieht über den Button-Parameter command
.
Das Spielfeld zurücksetzen
Such für diese Funktion wird unter anderem das Array all_XO
benötigt, um die Einstellungen aller Knöpfe zu verändern. Dabei werden die Knöpfe wieder aktiviert, der Text geleert und der Wert value
der Knöpfe zurück auf None
gesetzt. Abgesehen davon werden alle Werte des zweidimensionalen Arrays XO_tags
wieder mit “-” überschrieben. Zuletzt wird der Text des Gewinns aus dem Fenster entfernt.
def reset():
for button in all_XO:
button.button.configure(state = "normal")
button.button.configure(text="")
button.value = None
for i in range(3):
for j in range(3):
XO_tags[i][j] = "-"
win_label.pack_forget()
Revanche-Nachfrage im Spiel
import tkinter as tk
window = tk.Tk()
window.resizable(False, False)
window.title("Tic Tac Toe")
game_setup = tk.Frame(window)
all_XO = []
XO_tags = [["-", "-", "-"],
["-", "-", "-"],
["-", "-", "-"]]
current_tag = "X"
request_frame = tk.Frame(window, borderwidth = 2, relief = "sunken")
win_label = tk.Label(window)
def check_rows(current_tag):
for i in range(3):
if (XO_tags[i][0] == current_tag) and (XO_tags[i][1] == current_tag) and (XO_tags[i][2] == current_tag):
return True
return False
def check_columns(current_tag):
for i in range(3):
if (XO_tags[0][i] == current_tag) and (XO_tags[1][i] == current_tag) and (XO_tags[2][i] == current_tag):
return True
return False
def check_diagonals(current_tag):
if (XO_tags[0][0] == current_tag) and (XO_tags[1][1] == current_tag) and (XO_tags[2][2] == current_tag):
return True
elif (XO_tags[0][2] == current_tag) and (XO_tags[1][1] == current_tag) and (XO_tags[2][0] == current_tag):
return True
else:
return False
def check_draw():
for i in range(3):
for j in range(3):
if not ((XO_tags[i][j] == "X") or (XO_tags[i][j] == "O")):
return False
return True
def check_win(current_tag):
if check_rows(current_tag) == False:
if check_columns(current_tag) == False:
if check_diagonals(current_tag) == False:
if check_draw() == False:
return False
elif check_draw() == True:
return "draw"
return True
class XO_tag:
def __init__(self, x, y):
self.x = x
self.y = y
self.value = None
self.button = tk.Button(
game_setup,
text = "",
width = 10,
height = 5,
bg = "white",
fg = "black",
command = self.set_tag,
)
self.button.grid(row = x, column = y, padx = 2, pady = 2)
def set_tag(self):
global current_tag
if self.value == None:
self.value = current_tag
XO_tags[self.x][self.y] = current_tag
if current_tag == "X":
self.button.configure(text=current_tag, fg = "red")
new_tag = "O"
else:
self.button.configure(text=current_tag, fg = "blue")
new_tag = "X"
if not check_win(current_tag) == False:
if check_win(current_tag) == True:
win_label.configure(text = str(current_tag) + " wins !!")
else:
win_label.configure(text = "Draw !!")
win_label.pack()
for button in all_XO:
button.button.configure(state = "disabled")
rematch()
current_tag = new_tag
which_player.configure(text = 'Player ' + str(current_tag))
def rematch():
request_label = tk.Label(request_frame, text = "Rematch ??")
request_label.grid(row = 0, padx = 5, pady = 5)
button_yes = tk.Button(request_frame, text = "Yes", command = lambda: [request_frame.pack_forget(), reset()])
button_yes.grid(row = 1, column = 0, padx = (0, 0), pady = 5)
button_no = tk.Button(request_frame, text = "No", command = lambda: [request_frame.pack_forget()])
button_no.grid(row = 1, column = 1, padx = (0, 30), pady = 5)
request_frame.pack(padx = 5, pady = 5)
def reset():
for button in all_XO:
button.button.configure(state = "normal")
button.button.configure(text="")
button.value = None
for i in range(3):
for j in range(3):
XO_tags[i][j] = "-"
win_label.pack_forget()
for x in range(3):
for y in range(3):
XO_tag(x, y)
all_XO.append(XO_tag(x, y))
which_player = tk.Label(window, text = 'Player ' + str(current_tag))
which_player.pack()
game_setup.pack(padx = 5, pady = 5)
window.mainloop()
Styling der Schrift
Um Schriften zu verändern muss eine neue Bibliothek importiert werden.
from tkinter import font
Nun kann eine Schrift folgendermaßen festgelegt werden:
game_font = font.Font(family='Comic Sans MS', size=12, weight='bold')
Hinweis: Je nach ausgewählter Schrift und deren Größe muss eventuell die Größe der Buttons überarbeitet werden. In diesem Fall muss die Höhe height
von 5 auf 4 herabgesetzt werden.
Jetzt kann die Variable game_font
in die einzelnen Elemente unter dem Parameter font
eingesetzt werden.
import tkinter as tk
from tkinter import font
window = tk.Tk()
window.resizable(False, False)
window.title("Tic Tac Toe")
game_setup = tk.Frame(window)
all_XO = []
XO_tags = [["-", "-", "-"],
["-", "-", "-"],
["-", "-", "-"]]
current_tag = "X"
game_font = font.Font(family='Comic Sans MS', size=12, weight='bold')
request_frame = tk.Frame(window, borderwidth = 2, relief = "sunken")
win_label = tk.Label(window, font = game_font)
def check_rows(current_tag):
for i in range(3):
if (XO_tags[i][0] == current_tag) and (XO_tags[i][1] == current_tag) and (XO_tags[i][2] == current_tag):
return True
return False
def check_columns(current_tag):
for i in range(3):
if (XO_tags[0][i] == current_tag) and (XO_tags[1][i] == current_tag) and (XO_tags[2][i] == current_tag):
return True
return False
def check_diagonals(current_tag):
if (XO_tags[0][0] == current_tag) and (XO_tags[1][1] == current_tag) and (XO_tags[2][2] == current_tag):
return True
elif (XO_tags[0][2] == current_tag) and (XO_tags[1][1] == current_tag) and (XO_tags[2][0] == current_tag):
return True
else:
return False
def check_draw():
for i in range(3):
for j in range(3):
if not ((XO_tags[i][j] == "X") or (XO_tags[i][j] == "O")):
return False
return True
def check_win(current_tag):
if check_rows(current_tag) == False:
if check_columns(current_tag) == False:
if check_diagonals(current_tag) == False:
if check_draw() == False:
return False
elif check_draw() == True:
return "draw"
return True
class XO_tag:
def __init__(self, x, y):
self.x = x
self.y = y
self.value = None
self.button = tk.Button(
game_setup,
text = "",
width = 10,
height = 4,
bg = "white",
fg = "black",
font = game_font,
command = self.set_tag,
)
self.button.grid(row = x, column = y, padx = 2, pady = 2)
def set_tag(self):
global current_tag
if self.value == None:
self.value = current_tag
XO_tags[self.x][self.y] = current_tag
if current_tag == "X":
self.button.configure(text=current_tag, fg = "red")
new_tag = "O"
else:
self.button.configure(text=current_tag, fg = "blue")
new_tag = "X"
if not check_win(current_tag) == False:
if check_win(current_tag) == True:
win_label.configure(text = str(current_tag) + " wins !!")
else:
win_label.configure(text = "Draw !!")
win_label.pack()
for button in all_XO:
button.button.configure(state = "disabled")
rematch()
current_tag = new_tag
which_player.configure(text = 'Player ' + str(current_tag))
def rematch():
request_label = tk.Label(request_frame, text = "Rematch ??", font = game_font)
request_label.grid(row = 0, padx = 5, pady = 5)
button_yes = tk.Button(request_frame, text = "Yes", font = game_font, command = lambda: [request_frame.pack_forget(), reset()])
button_yes.grid(row = 1, column = 0, padx = (0, 0), pady = 5)
button_no = tk.Button(request_frame, text = "No", font = game_font, command = lambda: [request_frame.pack_forget()])
button_no.grid(row = 1, column = 1, padx = (0, 30), pady = 5)
request_frame.pack(padx = 5, pady = 5)
def reset():
for button in all_XO:
button.button.configure(state = "normal")
button.button.configure(text="")
button.value = None
for i in range(3):
for j in range(3):
XO_tags[i][j] = "-"
win_label.pack_forget()
for x in range(3):
for y in range(3):
XO_tag(x, y)
all_XO.append(XO_tag(x, y))
which_player = tk.Label(window, text = 'Player ' + str(current_tag), font = game_font)
which_player.pack()
game_setup.pack(padx = 5, pady = 5)
window.mainloop()
Der vollständige Code
import tkinter as tk
from tkinter import font
window = tk.Tk()
window.resizable(False, False)
window.title("Tic Tac Toe")
game_setup = tk.Frame(window)
all_XO = []
XO_tags = [["-", "-", "-"],
["-", "-", "-"],
["-", "-", "-"]]
current_tag = "X"
game_font = font.Font(family='Comic Sans MS', size=12, weight='bold')
request_frame = tk.Frame(window, borderwidth = 2, relief = "sunken")
win_label = tk.Label(window, font = game_font)
def check_rows(current_tag):
for i in range(3):
if (XO_tags[i][0] == current_tag) and (XO_tags[i][1] == current_tag) and (XO_tags[i][2] == current_tag):
return True
return False
def check_columns(current_tag):
for i in range(3):
if (XO_tags[0][i] == current_tag) and (XO_tags[1][i] == current_tag) and (XO_tags[2][i] == current_tag):
return True
return False
def check_diagonals(current_tag):
if (XO_tags[0][0] == current_tag) and (XO_tags[1][1] == current_tag) and (XO_tags[2][2] == current_tag):
return True
elif (XO_tags[0][2] == current_tag) and (XO_tags[1][1] == current_tag) and (XO_tags[2][0] == current_tag):
return True
else:
return False
def check_draw():
for i in range(3):
for j in range(3):
if not ((XO_tags[i][j] == "X") or (XO_tags[i][j] == "O")):
return False
return True
def check_win(current_tag):
if check_rows(current_tag) == False:
if check_columns(current_tag) == False:
if check_diagonals(current_tag) == False:
if check_draw() == False:
return False
elif check_draw() == True:
return "draw"
return True
class XO_tag:
def __init__(self, x, y):
self.x = x
self.y = y
self.value = None
self.button = tk.Button(
game_setup,
text = "",
width = 10,
height = 4,
bg = "white",
fg = "black",
font = game_font,
command = self.set_tag,
)
self.button.grid(row = x, column = y, padx = 2, pady = 2)
def set_tag(self):
global current_tag
if self.value == None:
self.value = current_tag
XO_tags[self.x][self.y] = current_tag
if current_tag == "X":
self.button.configure(text=current_tag, fg = "red")
new_tag = "O"
else:
self.button.configure(text=current_tag, fg = "blue")
new_tag = "X"
if not check_win(current_tag) == False:
if check_win(current_tag) == True:
win_label.configure(text = str(current_tag) + " wins !!")
else:
win_label.configure(text = "Draw !!")
win_label.pack()
for button in all_XO:
button.button.configure(state = "disabled")
rematch()
current_tag = new_tag
which_player.configure(text = 'Player ' + str(current_tag))
def rematch():
request_label = tk.Label(request_frame, text = "Rematch ??", font = game_font)
request_label.grid(row = 0, padx = 5, pady = 5)
button_yes = tk.Button(request_frame, text = "Yes", font = game_font, command = lambda: [request_frame.pack_forget(), reset()])
button_yes.grid(row = 1, column = 0, padx = (0, 0), pady = 5)
button_no = tk.Button(request_frame, text = "No", font = game_font, command = lambda: [request_frame.pack_forget()])
button_no.grid(row = 1, column = 1, padx = (0, 30), pady = 5)
request_frame.pack(padx = 5, pady = 5)
def reset():
for button in all_XO:
button.button.configure(state = "normal")
button.button.configure(text="")
button.value = None
for i in range(3):
for j in range(3):
XO_tags[i][j] = "-"
win_label.pack_forget()
for x in range(3):
for y in range(3):
XO_tag(x, y)
all_XO.append(XO_tag(x, y))
which_player = tk.Label(window, text = 'Player ' + str(current_tag), font = game_font)
which_player.pack()
game_setup.pack(padx = 5, pady = 5)
window.mainloop()
One Comment
Pingback: