Einführung
Das Monty-Hall-Problem ist ein berühmtes Wahrscheinlichkeitsrätsel, das auf einem Spielshow-Szenario basiert. Im Spiel wird einem Teilnehmer drei Türen präsentiert. Hinter einer der Türen befindet sich ein Preis (z. B. ein Auto), während hinter den anderen beiden Türen Ziegen versteckt sind. Der Teilnehmer wählt eine der Türen aus. Der Moderator, der weiß, wo sich der Preis befindet, öffnet dann eine der anderen beiden Türen und zeigt eine Ziege. Dem Teilnehmer wird dann die Möglichkeit gegeben, entweder bei seiner ursprünglichen Wahl zu bleiben oder zur anderen noch nicht geöffneten Tür zu wechseln. Die Frage lautet: Was ist die beste Strategie, zu wechseln oder zu bleiben? In diesem Projekt werden Sie durch den Bau einer GUI-Anwendung geführt, um das Monty-Hall-Problem mit der Tkinter-Bibliothek in Python zu simulieren.
👀 Vorschau

🎯 Aufgaben
In diesem Projekt werden Sie lernen:
- Wie man mithilfe von Tkinter eine grafische Benutzeroberfläche (GUI) entwirft und entwickelt.
- Wie man das Monty-Hall-Problem simuliert, um seine Wahrscheinlichkeitsergebnisse zu verstehen.
- Wie man die Spiellogik in Python implementiert, um Benutzerauswahlen zu verarbeiten und Ergebnisse anzuzeigen.
- Wie man die
random-Bibliothek von Python verwendet, um den Preis zufällig hinter einer der Türen zu platzieren. - Wie man den Spielzustand zurücksetzt, um mehrere Spielrunden ohne Neustart der Anwendung zu ermöglichen.
🏆 Errungenschaften
Nach Abschluss dieses Projekts können Sie:
- GUI-Designprinzipien anwenden und sie in Python mit Tkinter implementieren.
- Die praktische Anwendung von Wahrscheinlichkeitstheorie und Statistik in Spielsimulationen verstehen.
- Ereignisgesteuertes Programmieren implementieren und Benutzerinteraktionen in einer GUI-Anwendung verarbeiten.
- Fortgeschrittene Python-Programmiertechniken wie Lambda-Funktionen und Listen-Abstraktionen nutzen.
- Die Wichtigkeit der Benutzererfahrung (UX) im Spieldesign erkennen und Feedback-Mechanismen mithilfe von Nachrichtenfenstern bereitstellen.
Projektdateien erstellen
Erstellen Sie zunächst eine Python-Datei mit dem Namen monty_hall_gui.py in ~/project. Öffnen Sie diese dann in einem Texteditor oder einer integrierten Entwicklungsumgebung (IDE).
cd ~/project
touch monty_hall_gui.py
Notwendige Bibliotheken importieren
Zu Beginn werden wir die erforderlichen Module importieren.
import tkinter as tk
from tkinter import messagebox
import random
from typing import List, Optional
Im obigen Code importieren wir die erforderlichen Python-Bibliotheken. tkinter dient zur Erstellung von GUI, messagebox für Pop-up-Warnungen und random für die randomisierte Spiellogik.
Initialisieren Sie die MontyHallSimulation-Klasse
Nun werden wir die Hauptklasse zur Verwaltung unserer Simulation erstellen.
class MontyHallSimulation:
def __init__(self, root: tk.Tk) -> None:
"""
Initialize the Monty Hall Simulation.
:param root: The main tkinter window
"""
## Window Configuration
self.root = root
self.root.title("Monty Hall Problem Simulation")
self.root.geometry("1200x800")
In den obigen Anweisungen beginnen wir, unsere Hauptklasse für die Simulation zu definieren.
Variablen und UI-Komponenten einrichten
Innerhalb der Klasse initialisieren Sie die erforderlichen Variablen und UI-Elemente.
## Variables to keep track of the user's selected door, the door that's shown, and the winning door
self.selected_door: Optional[int] = None
self.show_door: Optional[int] = None
self.winning_door: int = random.randint(1, 3)
## Variables to keep track of win counts based on decision to change or stay
self.wins_change: int = 0
self.wins_stay: int = 0
## Instructions displayed at the top
self.label = tk.Label(root, text="Select a door!", font=("Arial", 16))
self.label.pack(pady=5)
## Creating frame for door buttons
self.doors_frame = tk.Frame(root)
self.doors_frame.pack(pady=5)
Die obigen Anweisungen führen Sie durch die Initialisierung verschiedener Spielvariablen und das Einrichten der primären UI-Elemente für unsere Simulation.
Türbilder und Buttons einrichten
Laden Sie die Türbilder und konfigurieren Sie die Türbuttons, um die Benutzerinteraktion zu ermöglichen.
## Load door images
self.door_imgs: List[tk.PhotoImage] = [
tk.PhotoImage(file="door_closed.png"),
tk.PhotoImage(file="door_opened_empty.png"),
tk.PhotoImage(file="door_opened_prize.png")
]
## Create door buttons with images and attach select door function
self.door_buttons: List[tk.Button] = [
tk.Button(self.doors_frame, image=self.door_imgs[0], command=lambda: self.select_door(1)),
tk.Button(self.doors_frame, image=self.door_imgs[0], command=lambda: self.select_door(2)),
tk.Button(self.doors_frame, image=self.door_imgs[0], command=lambda: self.select_door(3))
]
for button in self.door_buttons:
button.pack(side=tk.LEFT, padx=5)
## Buttons for choosing whether to switch doors
self.yes_button = tk.Button(self.root, text="Yes", command=self.switch_door)
self.yes_button.pack_forget()
self.no_button = tk.Button(self.root, text="No", command=self.stay_door)
self.no_button.pack_forget()
## Displaying results
self.results_label = tk.Label(root, text="", font=("Arial", 14))
self.results_label.pack(pady=5)
Im obigen Code laden wir die Türbilder, die verschiedene Türzustände repräsentieren. Anschließend erstellen und konfigurieren wir drei Buttons, die die Türen darstellen und es dem Benutzer ermöglichen, seine Wahl zu treffen.
Logik zur Türauswahl implementieren
Zunächst werden wir uns mit der Logik zur Auswahl einer Tür befassen.
def select_door(self, door: int) -> None:
"""
Handle the event when a door is selected.
:param door: The number of the door selected
"""
## Ensure no re-selection of doors
if self.selected_door:
return
self.selected_door = door
## Determine which doors were not selected
non_selected_doors = [d for d in [1, 2, 3] if d!= self.selected_door]
## Decide which door to reveal
if self.selected_door == self.winning_door:
show_door = random.choice(non_selected_doors)
else:
non_selected_doors.remove(self.winning_door)
show_door = non_selected_doors[0]
self.show_door = show_door
self.door_buttons[show_door - 1].config(image=self.door_imgs[1])
## Prompt the user to decide whether to switch
self.label.config(text="Would you like to switch?")
## Display the yes and no buttons
self.yes_button.pack(side=tk.LEFT, padx=10, pady=20)
self.no_button.pack(side=tk.RIGHT, padx=10, pady=20)
Im obigen Code definieren wir die Methode select_door, die verwendet wird, wenn ein Spieler eine Tür auswählt. Die Methode speichert die Auswahl des Spielers und zeigt eine der Türen, die kein Preis enthält, auf.
Logik zur Türwechselung behandeln
Als Nächstes befassen wir uns mit der Logik des Türwechsels.
def switch_door(self) -> None:
"""Handle the event when the user decides to switch doors."""
## Find the door that was not selected and not revealed
remaining_doors = [d for d in [1, 2, 3] if d!= self.selected_door and d!= self.show_door]
new_door = remaining_doors[0]
## Check for a win
if new_door == self.winning_door:
self.wins_change += 1
self.show_win(True)
else:
self.show_win(False)
Hier verwaltet die Funktion switch_door, was passiert, wenn ein Spieler beschließt, die Tür zu wechseln, nachdem der Moderator hinter einer der nicht ausgewählten Türen eine Ziege aufgedeckt hat.
Logik zur Türbeibehaltung umsetzen
Jetzt befassen wir uns mit der Logik für die Beibehaltung der ursprünglichen Türauswahl.
def stay_door(self) -> None:
"""Handle the event when the user decides to stay with the initial door selection."""
## Check for a win
if self.selected_door == self.winning_door:
self.wins_stay += 1
self.show_win(True)
else:
self.show_win(False)
In diesem Codeausschnitt überwacht die Funktion stay_door das Ergebnis, wenn der Spieler beschließt, bei seiner ursprünglichen Türauswahl zu bleiben.
Sieg oder Niederlage darstellen
Anschließend wird angezeigt, ob der Spieler gewonnen hat oder nicht.
def show_win(self, did_win: bool) -> None:
"""
Display the results after revealing all doors.
:param did_win: Boolean indicating whether the user won or not
"""
## Update door images based on the results
for i, button in enumerate(self.door_buttons):
if i + 1 == self.winning_door:
button.config(image=self.door_imgs[2])
else:
button.config(image=self.door_imgs[1])
## Show win/lose message
if did_win:
messagebox.showinfo("Congratulations!", "You won!")
else:
messagebox.showinfo("Sorry!", "Better luck next time!")
## Update the win statistics
self.results_label.config(text=f"Wins (Switch): {self.wins_change} Wins (Stay): {self.wins_stay}")
## Prepare for the next round
self.reset_game()
Diese Methode show_win zeigt die Ergebnisse an. Wenn die endgültige Türauswahl des Spielers den Preis verbirgt, wird er als Gewinner erklärt; andernfalls wird ihm für das nächste Mal viel Glück gewünscht.
Spielmechanik zurücksetzen
Schließlich müssen Sie sicherstellen, dass Sie das Spiel für eine neue Runde neu initialisieren können.
def reset_game(self) -> None:
"""Reset the game for a new round."""
self.selected_door = None
self.winning_door = random.randint(1, 3)
for button in self.door_buttons:
button.config(image=self.door_imgs[0])
self.label.config(text="Select a door!")
self.yes_button.pack_forget()
self.no_button.pack_forget()
Diese Funktion reset_game setzt den Spielzustand zurück und ermöglicht es den Spielern, eine weitere Runde zu spielen.
Die Anwendung ausführen
Schließlich initialisieren und starten wir unsere Anwendung.
if __name__ == "__main__":
root = tk.Tk()
sim = MontyHallSimulation(root)
root.mainloop()
Nachdem wir alle Schritte abgeschlossen haben, können wir den Code mit dem folgenden Befehl ausführen:
cd ~/project
python monty_hall_gui.py

Zusammenfassung
Herzlichen Glückwunsch! Sie haben gerade eine GUI-basierte Simulation des Monty-Hall-Problems mit Tkinter erstellt. Um die Simulation auszuführen, führen Sie das Skript monty_hall_gui.py aus und interagieren Sie mit der grafischen Benutzeroberfläche. Denken Sie daran, dass das Spiel die unintuitive Natur der Wahrscheinlichkeit zeigt und die beste Strategie besteht darin, immer die Tür zu wechseln!



