Dienstag, April 23, 2024
   
Text Size
Login

Auszahlquoten elektromechanischer GSG durch Simulation bestimmen

Wie schon an anderer Stelle angedeutet, eröffne ich mal einen Thread, in dem die Analyse des langfristigen Auszahlverhalten elektromechanischer Geräte durch Simulation erfolgt. Alle Simulationsprogramme stehen zur Einsicht, zur freien Benutzung und zur Anpassung an andere Geräte zur Verfügung, so dass dieses Ansinnen hoffentlich den größtmöglichen Nutzen für alle hat.

Besonderes Augenmerk wird bei der Simulation auf die Untersuchung "strategischen" Spieles durch Nutzung der Nachstartfunktion gelegt. Dieses wird immer dem "Laufenlassen" des Gerätes gegenüber gestellt, welches wohl seit der Einführung der Münzvorlage in den späten 60er Jahren ein häufiger "Anwendungsfall" vor allem in Kneipen und Restaurants war.
Bei Geräten, bei denen das sinnvoll erscheint, werden auch mehrere mögliche Strategien untersucht. Die interessierenden Punkte dabei sind die Auszahlquote, die Häufigkeit verschiedener Gewinnklassen (kleine Gewinne, Direktserien, größere Serien) und die generelle Häufigkeit von Gewinnen.
 
Wenn hier eine breite Anzahl verschiedener elektromechanischer Geräte betrachtet wird, dann entsteht auch ein Eindruck der Entwicklung des Auszahlverhaltens im historischen Kontext der jeweiligen Spielsysteme. (Wow, das nenne ich einen gelungenen Ausdruck, klingt gewichtig und sagt wenig...) Ich gedenke, zu jedem simulierten Automaten einen längeren Text mit der Beschreibung der Ergebnisse zu erstellen, eine Vorlage dafür steht auch zum Download.


Warum?

Im Gegensatz zu den Programmabläufen microprozessor-gesteuerter Geräte, die kein wirklich zufälliges Spiel mehr implementieren, sind elektromechanische Geräte so gebaut, dass das Spiel so zufällig wie möglich erfolgen soll. Natürlich hat die dabei zum Mischen verwendete Mechanik auch ihre Ungenauigkeiten, und manchmal kommt es deshalb auch zu kleinen Häufungen und Regelmäßigkeiten, wenn das Gerät etwas älter geworden ist.

Die zur Erzeugung des Zufalls angewandten Verfahren sind (meines an Bergmann- und NSM-Geräten erworbenen und hier angelesenen Wissens nach - also bitte ergänzen, wenn ich etwas vergaß):

- Verwendung von Kontakten, die über einen unsichtbaren, motorgesteuerten Umlaufkörper geschlossen werden. Diese Kontakte bestimmen den genauen Stoppzeitpunkt der Umlaufkörper, die zum Spielergebnis führen.

- Quasizufällige Unterbrechung dieses Motorlaufes der Würfelstopper durch quasiperiodisch variable Nockenkontakte, die über exzentrisch montierte Rädchen auf der Nockenwelle realisiert werden. Diese Rädchen bewegen sich durch ihre mechanische Beanspruchung bei jedem Schaltvorgang ein wenig, so dass sich Schaltzeitpunkt und Schaltdauer variabel verhalten. Die Kombination von drei bis fünf solcher exzentrisch montierter Rädchen, deren genaues Verhalten auch durch mechanische Unvollkommenheit, thermische Effekte und Reibung bestimmt ist, führt zu einem praktisch nicht vorhersehbaren, chaotisch wirkenden Verhalten.

- Schnelle Rotationsgeschwindigkeit der Umlaufkörper, die selbst bei zu regelmäßiger Verwürfelung der Stoppzeitpunkte durch mechanische Unvollkommenheiten ein weiteres unvorhersagbares Element in das Ergebnis bringen.

- Darüber hinaus wurden teilweise noch andere Verfahren eingesetzt. Einige sehr alte Hellomat-Geräte mischten über einen Behälter mit einer Metallkugel, die den entscheidenden Kontakt schloss. Einige neuere Wulff-Geräte (ich weiß es vom Rotomat Classic) mischten über eine (für mich völlig unverständliche) IC-Schaltung, die übrigens nicht so gut arbeitete wie die althergebrachten elektromechanischen Verfahren. Angeblich verließen sich einige alte Geräte auch vollkommen auf die mechanischen Unvollkommenheiten und den schnellen Lauf der Scheiben oder Walzen - weitere Vorrichtungen zur Verwürfelung waren schlicht nicht vorhanden. (Das kann ich nicht bestätigen, aber ich habe auch noch nie ein elektromechanisches 10Pfg-Gerät genauer untersuchen können.)

Wenn diese Verfahren gut funktionieren, dann kann man von einem quasizufälligen Spielverlauf und daher auch von einer Gleichverteilung der möglichen Kombinationen ausgeben. Das macht es grundsätzlich möglich, alle Fragen zum Spielverlauf durch bloßes Ausrechnen zu beantworten - eine Strafe für jemanden, der Vater und Mutter erschlagen hat!

In dieser Situation hat der Spieler bei den meisten Geräten die Möglichkeit des Nachstarts. Die zu beantwortende Frage ist, ob die überlegte Anwendung dieser Einflussnahme zu einer Verbesserung der Gewinnerwartung führt. Da die betrachteten Spielsysteme und Strategien relativ komplex sind, ist der Rechenweg langwierig und sehr fehleranfällig. Einfacher ist die Simulation einer großen Anzahl Spiele unter der Annahme der Gleichverteilung. Und genau darum geht es in diesem Thread.
 


Programmierung allgemein

Ich habe alle meine Simulationen in Python programmiert. Diese Sprache führt zwar nicht zu einem besonders schnellen Programmablauf, aber sie ist leicht durchschaubar, sie ist leicht zu schreiben und hat eine relativ klare Syntax, so dass das Gehäcksel auch für weniger kranke Menschen wiederverwendbar ist (für mich allein hätte ich's in Perl gemacht, das geht mir schneller von der Hand). Eine vollständige Dokumentation der Sprache ist im Internet verfügbar. Der Interpreter ist (völlig frei und kostenlos) für so ziemlich alles zu haben, was Bits und Bytes verarbeitet - ich habe sogar schon eine Version unter AmigaOS gesehen.

Windows-Anwendern empfehle ich ActivePython, das mit einem Installationsprogramm und Dokumentation in Form normaler Windows-Hilfedateien kommt. Linux-Anwender werden meist schon ein Python installiert haben, ansonsten gibt's ein angepasstes Paket für jede Distribution. Für Mac-User kann ich leider keine Empfehlung aussprechen, da ich die letzten drei Monate nicht hinter einem Mac gesessen habe und lieber selbst ausprobiere, was ich anderen empfehle - aber es wird gerade für diesen komfortverwöhnten Kreis von Menschen gewiss eine Alternative zu allzu kryptischen Installationen geben.

Auf "fortgeschrittene" Programmiertechniken wurde in den Simulationen bewusst verzichtet, die grundlegenden Ideen des Programmes sollte somit jeder verstehen können, der sich seine Jugend mit QuickBasic, AmigaBasic oder ähnlichen fröhlichen Dingen verdorben hat. Allerdings sind die Konzepte der Sprache Python geringfügig anders als die Konzepte der Sprache Basic, insbesondere gibt es Datentypen, die in klassischen Sprachen (außer in LISP - Lost In the Swamp of Parantheses) unbekannt sind - und ich benutze diese Aliase, anonymen Funktionen, Listen, Tupel und assoziativen Arrays lang und schmutzig. Auch ist die Syntax völlig anders. Das in der Dokumentation enthaltene Tutorial sollte also einen schnellen Blick wert sein. Denn von diesen ungewohnten Konzepten abgesehen, ist Python eine wirklich einfache und noch relativ schöne Programmiersprache.

Mein ganzes Simulationsgerüst ist sehr ausführlich kommentiert. Für einen konkreten Automaten werden einfach die fehlenden Dinge eingefügt, so dass jedes Mal ein für sich allein verwendbares Programm entsteht, das keine weiteren Dateien braucht. Alles andere führte zu der Schwierigkeit, dass auch noch eine Installation eines kleinen Simulationsprogrammes beschrieben werden müsste - so etwas tut sich doch kein Mensch an, der nur "mal eben" ein Ergebnis überprüfen will...  
 


Zufallsgenerator

So ein Zufallsgenerator ist immer etwas fragil und bedarf der Betrachtung. Wen's nicht interessiert: Einfach überlesen!

Als Zufallsgenerator zur Ermittlung der Kombinationen wird das Python-Standardmodul random benutzt. Dieses verwendet im Hintergrund einen "Mersenne Twister" als Generator, ein gleichermaßen relativ schnelles, bewährtes und sehr gutes Standardverfahren zur Erzeugung von gleich verteilten Pseudozufallszahlen. Wegen dieser Eigenschaften und einiger unverbesserlicher Miesmacher (von gebildeten Menschen auch als Mathematiker und theoretische Informatiker bezeichnet) ist der Algorithmus der meistgetestete Zufallsgenerator aller Zeiten geworden - und keiner hat etwas substanzielles an den erzeugten Zahlen aussetzen können.

Der Generator ist extrem langperiodisch. Der ursprüngliche Zustand wird erst nach 2^19937-1 Durchläufen wieder erreicht. Verglichen mit dieser Zahl wirkt die geschätze Anzahl von Atomen im Universum, 10^80, geradezu winzig. Es ist schlicht nicht möglich, die ganze Periode durchlaufen zu lassen, der Rechner würde lange vorher durch den Beschuss mit hochenergetischen Energiequanten aus den Tiefen des Universums zerbröseln. Einmal ganz davon abgesehen, dass die beschränkte Lebensspanne des Computeranwenders schon lange vorher abgelaufen ist.

Die Gleichverteilung und Periodenfreiheit der generierten Zahlen ist hervorragend. Bis hin zu verschiedenen Anordnungen der generierten Zahlen in 623-dimensionale Strukturen wurden weder Regelmäßigkeiten noch Ungleichverteilungen festgestellt. Höherdimensionale Anordnungen werden vermutlich auch keine Schwächen des Verfahrens enthüllen, entsprechende Untersuchungen wurden aber wegen des damit verbundenen Aufwandes bislang auch von den größten Miesmachern noch nicht durchgeführt.

Es ist also ein idealer Generator für solche Simulationen, der keine unangenehmen Überraschungen in Form von auf die Ergebnisse durchschlagender Artefakte erwarten lässt. Wer aus Spieltrieb, Interesse oder für bestimmte Zwecke einen anderen Generator verwenden möchte, kann dies übrigens gern tun - viel Python ist dafür nicht erforderlich. Hierzu muss nur die Klasse random.Random beerbt und die Methode random() neu implementiert werden. Sie sollte eine Pseudozufallszahl x im Bereich 0 <= x < 1 zurückliefern. Anschließend wird eine Instanz dieser eigenen Klasse erzeugt und der Variablen random zugewiesen, diese Instanz wird dann automatisch von der Simulation verwendet. Empfehlenswert ist dies aber wahrscheinlich nicht.

Zu Beginn einer Simulation wird der Generator mit der Systemzeit des Computers in einen Anfangsstatus gebracht.  
 


Programmierung speziell

Meine Vorgehensweise ist brachial einfach.

Ich habe ein sehr geschwätzig kommentiertes Vorlageprogramm erstellt, in dem sich alles das befindet, was allen derartigen Simulationen gemeinsam ist. Nur die folgenden "Kleinigkeiten" (also die Programmierung des simulierten Automaten) müssen darin in aller Regel erledigt werden:

- Zuweisung der Einsatzhöhe an die Variable EINSATZ, an Hand dieser Angabe wird die Auszahlquote berechnet. Ich nehme als Einheit für Beträge immer ganzzahlige Werte, um eine bessere Performance zu bekommen, und deshalb rechne ich in Pfennigen.

- Zuweisung des Automatennamen an die Variable AUTOMAT, diese Angabe wird in die Ausgabe übernommen. So kann man am ausgegebenen Bericht sehen, auf welchen Automaten er sich bezieht.

- Zuweisung der Definition der Umlaufkörper an die Liste UMLAUF, wobei für Mehrfach-Displays einer Stellung ruhig vom Datentyp des Tupels Gebrauch gemacht werden sollte. Wenn spezielle Wünsche vorliegen, die man lieber nicht von Hand schreibt, sondern ein Programm erledigen lassen möchte, dann kann diese Zuweisung auch in der Funktion global_init() erfolgen, die garantiert genau einmal aufgerufen wird bevor irgendetwas simuliert wird.

- Definition erforderlicher Variablen (für Serien, Jackpots, Leitern) im globalen Namensraum - das Rahmenprogramm kümmert sich nur die Verwaltung und Zählung von Geldgewinnen.

- Zurücksetzen dieser Variablen in der Funktion init() - damit sie zum Beginn einer neuen Simulation immer auf einen definierten Wert stehen.

- Erstellen der Funktion gewinnpruefung(), diese kriegt die angezeigte Kombination als Paramter übergeben und muss einen Tupel aus Gewinnbetrag und textueller Gewinnbeschreibung zurückgeben. Hier wird die eigentliche Spiellogik gecodet, hier muss man sich um Gewinne, Serien, Jackpots und das ganze Zeugs kümmern. Dabei sollten auch eventuelle Beschränkungen der Zählwerke so gut wie möglich (oder bekannt) abgebildet werden, damit die Resultate der Simulation zuverlässig werden. Bitte immer daran denken, dass die Textbeschreibungen der Gewinne in der Ausgabe alphabetisch sortiert werden; für eine sinnvolle Reihenfolge beginne ich diese Texte einfach mit vernünftig kategoriesierenden zweistelligen Nummern.

- Erstellen eigener Funktionen für die Nachstart-Strategien. Diese Funktionen kriegen die angezeigte Stellung als Liste und geben einen Tupel mit allen Listenindizes zurück, die neu gestartet werden sollen. (Damit ist auch der Hellomat Doppelstart behandelbar.)

- Aufnahme der eigenen Strategien in die Liste STRATEGIEN, jede Strategie als Tupel aus Kurzbezeichnung und Funktion.

Und das war es auch schon. Wenn diese Schritte erledigt sind, hat man ein Simulationsprogramm für einen gewünschtes GSG und kann gleich anfangen, es an der Kommandozeile oder wie auch immer aufzurufen. Ohne weitere Angaben wird nach der gewünschten Anzahl von Spielen gefragt und jede in STRATEGIEN aufgenommene Strategie wird simuliert, die Ergebnisse gehen an die Standardausgabe. Die gesamte Programmlogik für die Simulation ist nämlich schon fertig. Die Simulation sollte unter einem vernünftigen Namen gespeichert werden, und zwar bitte mit '.py' als Extension (für Windows-Anwender) und mit höchstens acht Zeichen vor der Extension (für DOS-Anwender).

Um das ganze zu erleichtern, habe ich an allen Stellen, an denen "etwas gemacht" werden muss, den Text "EDIT:" in den Kommentar aufgenommen, so dass diese Stellen leicht über die Suchfunktion des Editors zu finden sind.

Wer sich von dieser kompakten Darlegung überfordert fühlt, aber trotzdem selbst eine Simulation programmieren möchte, kann ja mal mein Gerüst mit einem Beispiel, etwa dem Monarch vergleichen - manchmal sagt so ein Beispiel mehr als weitschwafeliges Beschreiben dessen, was nun getan werden soll.


Aufruf

Es ist bei so einem Programm durchaus sinnvoll, es an der Kommandozeile zu starten und die Ausgabe für eine bestimmte Anzahl simulierter Spiele in eine Datei umzulenken. Wie das geht, ist allerdings von Betriebssystem zu Betriebssystem verschieden, so dass ich hierzu keine allgemeinen Angaben machen kann. Unter DOS, Windows und unixoiden Systemen sieht das aber - am Beispiele von 100000 Spielen der Monarch-Simulation - so aus:


python monarch.py -n100000 > monarch.txt

Das Ergebnis befindet sich dann zum Programmende in der Datei "monarch.txt" und kann mit jedem Programm betrachtet und bearbeitet werden, das Textdateien nimmt. Natürlich kann der ganze Vorgang etwas Zeit in Anspruch nehmen; es handelt sich bei nennenswerten Anzahlen von Spielen um ideale Kandidaten für Programme, die man über Nacht oder mit gesenkter Priorität im Hintergrund laufen lässt...

Aber oops - da ist doch was Neues, nämlich dieses "-n100000". Da ist eine Option, mit der das Programm aufgerufen wird und die Einfluss auf das Programmverhalten nimmt - ein praktischer kleiner Mehrwert des Aufrufs an der Kommandozeile. Jede Option beginnt mit einem Minuszeichen, wer Unix-Programme kennt, fühlt sich gleich wie zu Hause. Ich habe die folgenden Optionen vorgesehen:

-h gibt eine Liste aller bekannten Optionen mit Kurzbeschreibung aus.

-l gibt eine nummerierte Liste aller für die Simulation implementierten Strategien aus.

-n unterdrückt die Rückfrage, wie viele Spiele simuliert werden sollen und nimmt den in gegebenen Wert. Das ist praktisch, wenn die Ausgabe in einer Datei gehen soll, da dann die Aufforderung zur Eingabe gar nicht sichtbar wird. (Sie geht auch in die Datei.)

-s gibt an, welche implementierten Strategien simuliert werden sollen. Dabei werden die Zahlen verwendet, die mit der Option "-l" angezeigt werden können. Mehrere Angaben müssen mit Komma getrennt werden, Leerzeichen sind zu vermeiden. So kann bequem die Simulation von nur einer einzelnen Strategie gestartet werden - etwa, wenn diese gerade erst hinzugefügt wurde.

-v steuert die "Geschwätzigkeit" von Programmausgaben. Dazu ist etwas mehr Erläuterung erforderlich. Ein Simulator kann nämlich auch Meldungen ausgeben, die nicht in die normale Ausgabe fließen, was praktisch ist, wenn die normale Ausgabe in eine Datei umgeleitet wurde. Normalerweise werden nur Fehlermeldungen auf diese Weise ausgegeben, aber dies lässt sich anpassen.

-v0 ist der Standardwert, es werden nur Fehlermeldungen ausgegeben.

-v1 macht die Ausgabe geschwätziger, es wird eine Meldung ausgegeben, wenn die Simulation einer neuen Strategie gestartet wurde und wenn die Ausgabe eines Ergebnisses erfolgt. So kann man einen Einblick in den Stand der Dinge bekommen, obwohl man die Ausgaben gar nicht sieht, weil sie ja in eine Datei gehen. Und das ist bei realen Simulationen mehr als praktisch.

-v2 empfiehlt sich nur zur Fehlersuche. Jede ermittelte Kombination der Umlaufkörper wird zusammen mit dem ermittelten Gewinn ausgegeben. Eine immense Textmenge, an Hand derer man sich überzeugen kann, das auch alles seine Richtigkeit hat. Oder an Hand derer man seine Fehler suchen kann.

Mehr gibt's zu diesem Thema auch nicht zu sagen.


Abschluss

Ich wünsche jetzt allen viel Spaß beim Nachvollziehen meiner Ergebnisse oder beim Erstellen eigener Simulationsprogramme.

Um einen elektromechanischen Automaten aussagekräftig zu simulieren, sind übrigens genaue Informationen über die Belegung des Walzenstreifens, das Spielsystem, eventuelle Beschränkungen der Zählwerke und Besonderheiten des Spielablaufes (in einigen Fällen: unter welchen Umständen kann gehalten werden) erforderlich. Eine solche Sammlung wäre doch genau richtig für dieses Forum, oder?


Noch Offenes

Die folgenden Punkte sind Ergänzungen, die für diesen Text wünschenswert wären.

- Es wäre hübsch, ein paar gute Fotos der mechanischen Realisierung des Würfelmechanismus und einiger "esoterischer" Würfelvorrichtungen (Kugelmischer) zu haben.

- Es fehlt eine Empfehlung für Mac-User, welches Python sie benutzen können.

- Es fehlt ein Verweis auf einen deutschen Einführungstext in Python. Als ich diesen Text schrieb, hatte ich leider keinen guten Zugang zum Internet.

Cookies

Einloggen