Was du hier lernst
- Zufallszahlen mit random
- Eingaben mit await ask()
- Fehler abfangen mit try/except
- Wahrscheinlichkeit und Erwartungswert
- Verdopplungsstrategie (Martingale)
- Diagramm selbst zeichnen
So funktioniert die Simulation
Das Programm hat zwei Teile. Im ersten spielst du selbst: Du setzt in der Konsole auf eine Zahl, eine Farbe oder beides, und die Kugel entscheidet. Im zweiten übernimmt der Computer und spielt 1000 Runden nach einer Strategie, die auf den ersten Blick unschlagbar wirkt. Beides zusammen zeigt, wie leicht sich die Intuition beim Glücksspiel täuschen lässt.
1. Setzen in der Konsole
Jede Eingabe läuft über readInput. Die Funktion wartet mit await ask(...) auf eine Zeile und versucht, daraus eine ganze Zahl zu machen:
guess = await ask(prompt)
number = -1
if guess != '':
try:
number = int(guess)
except Exception as arg:
print("no valid number", arg)
Eine leere Eingabe ergibt −1, und −1 bedeutet »ich setze nicht«. Tippst du etwas, das keine Zahl ist, fängt try/except den Fehler ab, statt das Programm abstürzen zu lassen. askForBet fragt nur dann nach einem Einsatz, wenn wirklich gesetzt wurde. Lässt du Zahl und Farbe leer, endet dein Spiel.
2. Der Zufall entscheidet
checkReality zieht mit random.randint(0, 36) eine der 37 Zahlen – jede gleich wahrscheinlich. Die Farbe ergibt sich aus einer vereinfachten Regel: gerade Zahlen sind rot, ungerade schwarz, die 0 ist grün.
if realityNumber % 2 == 0:
if realityNumber == 0:
realityColor = 3 # 3 == green
else:
realityColor = 1 # 1 == red
else:
realityColor = 2 # 2 == black
Farben als Zahlen 1, 2 und 3 heißen »magische Zahlen«: Man muss sich merken, was sie bedeuten. Die Kommentare helfen, eine Konstante wie RED = 1 wäre noch klarer. Auf einem echten Kessel sind übrigens andere Zahlen rot, an den Chancen ändert das aber nichts – es bleiben 18 rote, 18 schwarze und eine grüne.
3. Gewinn und Verlust
calculateBudget zieht erst die Einsätze ab und zahlt dann aus. Eine richtige Zahl bringt das 36-Fache des Einsatzes zurück, also 35-mal Gewinn plus den Einsatz. Eine richtige Farbe verdoppelt den Einsatz:
budget -= colorBet
if color == realityColor:
budget += colorBet * 2
Neu im Browser: Während die Konsole wartet, zeigt das Bild einen Roulettetisch. Die Kugel rollt über das Rad und bleibt auf der gezogenen Zahl liegen, daneben stehen dein Budget und die letzten Ergebnisse.
4. Warum die Bank gewinnt
Setzt du auf Rot, gewinnst du bei 18 von 37 Zahlen, also in rund 48,6 Prozent der Fälle. Die grüne Null gehört niemandem – außer der Bank. Pro gesetztem Dollar verlierst du deshalb im Durchschnitt 1/37, rund 2,7 Cent. Bei einer einzelnen Zahl ist es genauso: Sie kommt in einem von 37 Fällen, als Gewinn gibt es aber nur das 35-Fache des Einsatzes.
Die Kugel hat kein Gedächtnis
Nach vier Mal Rot fühlt sich Schwarz »fällig« an. Das ist ein bekannter Denkfehler, der Spielerfehlschluss. Jede Runde ist unabhängig von der vorigen, Schwarz kommt weiterhin in 18 von 37 Fällen – egal, was vorher kam.
5. Der Computer verdoppelt
Im zweiten Teil wartet der Computer, bis viermal hintereinander dieselbe Farbe kam. howManyTimes zählt die Wiederholungen: Beim ersten Mal ist der Zähler 0, nach der vierten gleichen Farbe steht er auf 3. Dann setzt der Computer auf die andere Farbe – und verdoppelt nach jedem Verlust:
if howManyTimes >= 3 and lastColor != 3:
...
colorBet = 5 * (2 ** lossCounter)
lossCounter zählt die Verluste in Folge. Die Einsätze wachsen also 5, 10, 20, 40, 80 … Dollar. Die Idee dahinter: Irgendwann kommt die richtige Farbe, und dann holt ein einziger Gewinn alle Verluste zurück, plus 5 Dollar.
6. Was das Diagramm zeigt
Die goldene Linie zeigt das Budget nach jedem Spiel. Meist steigt sie langsam in kleinen Stufen – die Strategie scheint zu funktionieren. Dann kommt eine lange Pechsträhne: Nach acht Verlusten in Folge steht schon ein Einsatz von 1280 Dollar im Raum, mehr als der Computer noch hat. Weil das Programm den Einsatz nicht auf das vorhandene Geld begrenzt, kann das Budget dabei sogar ins Minus rutschen.
Wir haben die Nacht 20 000-mal simuliert: Gut vier von fünf Nächten enden mit Gewinn, etwa jede elfte mit der Pleite. Über alle Nächte gerechnet verliert der Computer trotzdem Geld. Viele kleine Gewinne und wenige große Verluste – genau das macht die Strategie so verführerisch.
Die Bank spielen
Das Programm prüft nicht, ob ein Einsatz positiv ist. Setzt du einen negativen Betrag, drehen sich Gewinn und Verlust um: Du spielst die Bank. Am besten auf eine einzelne Zahl – dann gewinnst du meistens, und ab und zu wird es richtig teuer.
Weiterlesen: Monte-Carlo-Simulation
Ein Zufallsexperiment tausendfach durchspielen und die Ergebnisse auszählen – so wie hier die 20 000 Roulette-Nächte – nennt man Monte-Carlo-Simulation. Wie die Methode funktioniert und wie genau ihre Ergebnisse sind, erklärt smartlytics.de.
Probier mal
Jede Variante ändert den Code oben. Ein Klick auf »Ausprobieren« übernimmt die Änderung in den Editor und startet das Programm. »Zurücksetzen« holt das Original zurück.
-
01Der Computer mit 20 Dollar
Gib dem Computer nur das Budget, mit dem du selbst angefangen hast. Nach zwei verlorenen Einsätzen (5 und 10 Dollar) bleiben ihm noch 5 Dollar, der dritte Einsatz verlangt schon 20. In unseren Testläufen ging er so in fast neun von zehn Nächten pleite.
- budget = 2000 # Startkapital des Computers + budget = 20 # Startkapital des Computers -
02Mit Tischlimit
Echte Kasinos begrenzen den Einsatz – das Buch schlägt vor, genau das auszuprobieren. Mit 500 Dollar Limit kann der Computer nach einer langen Pechsträhne seine Verluste nicht mehr zurückholen. Er geht seltener pleite, beendet die Nacht aber öfter mit weniger Geld, als er mitgebracht hat.
- colorBet = 5 * (2 ** lossCounter) + colorBet = min(5 * (2 ** lossCounter), 500) # Tischlimit -
03Eine lange Nacht: 10 000 Spiele
Je länger gespielt wird, desto sicherer kommt irgendwann die Serie, die alles vernichtet. In unseren Testläufen endete ungefähr jede zweite Nacht mit 10 000 Spielen in der Pleite.
- numberOfGamesToPlay = 1000 + numberOfGamesToPlay = 10000 - if len(history) % 10 == 0: + if len(history) % 100 == 0: -
04Nicht auf Serien warten
Das Warten auf vier gleiche Farben ist Aberglaube: Die Kugel hat kein Gedächtnis, jeder Einsatz gewinnt in 18 von 37 Fällen. Hier setzt der Computer bei jedem Spiel gegen die letzte Farbe. Pro Einsatz ändern sich die Chancen nicht – aber er setzt viel öfter, und die zerstörerische Pechsträhne kommt schneller. In unseren Testläufen ging er in gut vier von zehn Nächten pleite.
- if howManyTimes >= 3 and lastColor != 3: + if lastColor != 3 and lastColor != 0: -
05Ein echter Roulettekessel
Im Buch sind der Einfachheit halber alle geraden Zahlen rot. Auf einem echten Kessel sind 1, 3, 5, 7, 9, 12, 14, 16, 18, 19, 21, 23, 25, 27, 30, 32, 34 und 36 rot. An den Chancen ändert das nichts: Es bleiben 18 rote, 18 schwarze und eine grüne Zahl. Die Reihenfolge auf dem Rad bleibt hier trotzdem einfach 0 bis 36.
- if realityNumber % 2 == 0: - if realityNumber == 0: - realityColor = 3 # 3 == green - else: - realityColor = 1 # 1 == red - else: - realityColor = 2 # 2 == black + red = {1, 3, 5, 7, 9, 12, 14, 16, 18, 19, 21, 23, 25, 27, 30, 32, 34, 36} + if realityNumber == 0: + realityColor = 3 # 3 == green + elif realityNumber in red: + realityColor = 1 # 1 == red + else: + realityColor = 2 # 2 == black - return COLORS[1] if number % 2 == 0 else COLORS[2] + red = {1, 3, 5, 7, 9, 12, 14, 16, 18, 19, 21, 23, 25, 27, 30, 32, 34, 36} + return COLORS[1] if number in red else COLORS[2]
Vom Buch in den Browser
Im Buch sind es zwei Programme: Roulette.py zum Selbstspielen und AutomatedRoulette.py, in dem der Computer die Verdopplungsstrategie spielt. Hier laufen beide nacheinander in einer Datei. Die Spiellogik ist übernommen, neu sind der Roulettetisch im Bild und das Budget-Diagramm.
Eingaben mit await ask() statt raw_input(). Ein Browser kann ein Programm nicht einfach anhalten, bis jemand etwas eintippt – sonst würde die ganze Seite einfrieren. ask() aus c4f wartet deshalb mit await: Python pausiert, der Browser bleibt bedienbar, und sobald du Enter drückst, geht es weiter. In modernen Browsern funktioniert auf dieser Website sogar das eingebaute input(): Python läuft in einem eigenen Hintergrund-Thread, der auf die Eingabe warten darf, ohne die Seite zu blockieren. Das setzt aber einen besonderen Sicherheitsmodus des Browsers voraus (»cross-origin isolation«). ask() funktioniert dagegen überall, deshalb nutzen es die Beispiele. Weil readInput jetzt wartet, sind auch askForBet und das Hauptprogramm async-Funktionen.
| Im Buch | Hier |
|---|---|
print "Choose a number: ", + raw_input() |
await ask("Choose a number: ") |
except Exception, arg: |
except Exception as arg: |
print als Anweisung |
print() als Funktion |
zwei Funktionen calculateBudget in zwei Dateien |
calculateBudget und calculateColorBudget in einer Datei |
| Ergebnis nur als Text | zusätzlich Roulettetisch und Budget-Diagramm |
Ein Fehler weniger. Das Original fragt Red(1) or Black(0):, Schwarz hat im Programm aber die Nummer 2. Wer der Frage folgte und 0 tippte, setzte auf eine Farbe, die es nicht gibt, und verlor jede Farbwette. Die Frage lautet jetzt Red(1) or Black(2):.
Zwei Programme, ein Name. Beide Buchprogramme haben eine Funktion calculateBudget, die automatische Fassung ohne die Wette auf eine Zahl. In einer gemeinsamen Datei würde die zweite die erste überschreiben, deshalb heißt sie hier calculateColorBudget.
Kleine Ergänzungen für das Diagramm. Die automatische Schleife merkt sich jedes Budget in der Liste history, und alle zehn Spiele wird das Diagramm neu gezeichnet. Am Ende steht zusätzlich der höchste Einsatz der Nacht in der Konsole. »Noch eine Nacht« startet die Simulation mit neuen Zufallszahlen.
Original aus dem Buch ansehen Roulette.py · Python 2
'''
Created on 24.01.2010
@author: sourcer
'''
import random
def readInput():
guess = raw_input()
number = -1
if guess != '':
try:
number = int(guess)
except Exception, arg:
print "no valid number", arg
return number
def askForBet(number):
bet = 0
if number != -1:
print "Your bet:",
bet = readInput()
return bet
def checkReality():
realityNumber = random.randint(0, 36)
if realityNumber % 2 == 0:
if realityNumber == 0:
realityColor = 3 # 3 == green
else:
realityColor = 1 # 1 == red
else:
realityColor = 2 # 2 == black
return realityNumber, realityColor
def calculateBudget(budget, number, numberBet, color, colorBet, realityNumber, realityColor):
if number != -1:
budget -= numberBet
if number == realityNumber:
budget += numberBet*36 # 35 times plus bet which was taken
print budget, "number"
if color != -1:
budget -= colorBet
if color == realityColor:
budget += colorBet*2
return budget
def showReality(realityNumber, realityColor, budget):
colorDef = {2: "black", 1: "Red", 3: "Green"}
print
print "number:\t", realityNumber
print "color:\t", colorDef.get(realityColor)
print "budget:\t", budget
print "Welcome to Las Vegas!"
budget = 20
while True:
print
print "Choose a number: ",
number = readInput()
numberBet = askForBet(number)
print "Red(1) or Black(0):",
color = readInput()
colorBet = askForBet(color)
if number == -1 and color == -1:
break
realityNumber, realityColor = checkReality()
budget = calculateBudget(budget, number, numberBet, color, colorBet, realityNumber, realityColor)
showReality(realityNumber, realityColor, budget)
if budget <= 0:
break
print
print "Congratulations your budget is:", budget, "Dollar"
print "Ciao!"
Original aus dem Buch ansehen AutomatedRoulette.py · Python 2
'''
Created on 24.01.2010
@author: sourcer
'''
import random
def checkReality():
realityNumber = random.randint(0, 36)
if realityNumber % 2 == 0:
if realityNumber == 0:
realityColor = 3 # 3 == green
else:
realityColor = 1 # 1 == red
else:
realityColor = 2 # 2 == black
return realityNumber, realityColor
def calculateBudget(budget, color, colorBet, realityNumber, realityColor):
if color != -1:
budget -= colorBet
if color == realityColor:
budget += colorBet*2
return budget
print "Welcome to Las Vegas!"
budget = 2000
lastColor = 0
howManyTimes = 0
lossCounter = 0
numberOfGamesToPlay = 1000
while numberOfGamesToPlay > 0:
numberOfGamesToPlay -= 1
if howManyTimes >= 3 and lastColor != 3:
if lastColor == 1:
color = 2
else:
color = 1
colorBet = 5*(2**lossCounter)
else:
color = 0
colorBet = 0
realityNumber, realityColor = checkReality()
budget = calculateBudget(budget, color, colorBet, realityNumber, realityColor)
if realityColor != color and colorBet != 0:
lossCounter += 1
else:
lossCounter = 0
if lastColor == realityColor:
howManyTimes += 1
else:
howManyTimes = 0
lastColor = realityColor
if budget <= 0:
break
print
print "Congratulations your budget is:", budget, "Dollar"
print "Ciao!"