Berechnung der Gewinnwahrscheinlichkeit beim Poker in C#

PokerChancenRechner - BerechnungHeute geht es wieder einmal um die Erstellung eines kleinen C#-Tools. Somit begeben wir uns thematisch wieder einmal in Richtung “Back to the roots” der Code-Bude.

Bei dem Tool handelt es sich um eine kleine Applikation, die Siegchancen einer Poker-Hand ermittelt. Das Tool erwartet als Eingabe die zwei Karten, die ein Spieler auf der Hand hat, sowie die drei initial ausgelegten Karten auf dem Deck (der sogenannte “Flop”) und berechnet daraus die Gewinnchancen für den Spieler.

Bevor nun die Ersten “Aber Poker ist doch ein Glücksspiel und Glücksspiele kann man nicht berechnen” schreien, wollen wir nachfolgend noch einmal kurz die Frage nach dem “Glücksspiel” stellen.

Ist Poker Glücks- oder Geschicklichkeitsspiel?

Zwar wird Poker in Deutschland rechtlich gesehen weit überwiegend zu den Glücksspielen gezählt, jedoch ist dies abseits der rein rechtlichen Regelung gar nicht so eindeutig.

Neben der wachsenden Popularität sowohl in Form von Online-Poker, TV-Duellen wie Stefan Raabs Pokernacht und Livestreams der großen Poker-Events gibt es auch noch weitere Gründe, warum Glücksspiel nicht gleich Glücksspiel ist.

Entgegen, zum Beispiel, dem Roulette-Spiel oder einem “Einarmigen Banditen”, kann der Spieler beim Poker aktiv Einfluss auf das Spiel nehmen. Neben der psychologischen Komponente lässt sich auch, unter Zuhilfenahme des Wissens der bereits ausgegebenen Karten, statistisch berechnen, wie hoch die Gewinnwahrscheinlichkeit einer Hand ist.

Forscher der Universität Hamburg sind im Rahmen einer Untersuchung sogar zu dem Entschluss gekommen, dass Poker eher als Geschicklichkeits- denn als Glücksspiel anzusehen ist. (Wobei sie das Pokerspiel dennoch nicht verharmlosen, sondern für eine Regulierung in Abhängigkeit der Sozialschädlichkeit eines Spiels anstelle der Regulierung in Abhängigkeit der Glücksspieldefinition plädieren.)

Fakt ist, dass die Wahrscheinlichkeit mit einer Pokerhand zu gewinnen, in Abhängigkeit zum Flop steht und sich dieser Wert berechnen lässt. Dies ist zwar kein Garant für einen Sieg, hilft jedoch beim Training, ein Gefühl für den Wert einer Hand zu erlangen. Und deshalb wollen wir nachfolgend ein kleines C#-Tool zur Berechnung eben dieser Wahrscheinlichkeit schreiben.

Poker-Gewinnchance mit C# berechnen

Für die Entwicklung des Tools nutzen wir das Visual Studio und legen eine WinForms-Anwendung an. Ich habe die Applikation “PokerChancenRechner” genannt.

Wie vielleicht schon aus anderen Artikeln hier auf code-bude.net bekannt, gestalten wir zuerst das GUI im Form-Designer, bevor es ans Coding geht. Für die Anwendung brauchen wir mindestens:

  • 5x ListBox (Kartenwerte, Kartenfarben, Zielstapel, Handstapel, Deck-/Tischstapel)
  • 3x Buttons (Karte auf einen Stapel legen, Stapel zurücksetzen, Wahrscheinlichkeit berechnen)
  • 1x Label (zur Anzeige der Gewinnwahrscheinlichkeit)

Ich habe zudem noch vier GroupBoxen platziert, um das Layout etwas zu strukturieren. Hier gilt aber: “Alles kann, nichts muss.”

PokerChancenRechner - StartDie Anordnung der Elemente könnt ihr nebenstehendem Screenshot entnehmen.

Wenn die Elemente platziert sind, benötigen wir noch Eventhandler – insgesamt 4 Stück. Einen Click-Handler pro Button und den FormLoad-Eventhandler. Diese können entweder über das Events-Eigenschaftsfenster des jeweiligen Controls oder direkt im Code-File angelegt werden.

Laden der GUI-Werte

Sind Controls und Eventhandler angelegt, kann es fast schon mit dem Coding losgehen. Zuvor binden wir jedoch noch die “HandEvaluator” Klassenbibliothek ein, die uns bei der Berechnung der gesuchten Wahrscheinlichkeit unterstützt. Die dazugehörige DLL-Datei kann kostenlos auf folgender codeproject.com-Projektseite heruntergeladen werden.

Nachdem Download fügen wir die DLL per “Rechtsklick->Verweis hinzufügen” auf “Verweise” hinzu. Danach schreiben wir noch folgende Using-Direktive in den Kopf unserer Form1.cs-Datei:

using HoldemHand;

Nun kann es mit dem Coding losgehen. Im ersten Schritt wollen wir die Initialwerte laden. Dies machen wir im FormLoad-Event unserer Applikation. Bei den Werten, die in die Listboxen geladen werden sollen, handelt es sich um die Kartenwerte, die Kartenfarben und die Zielstapel (Hand oder Deck). Nachfolgend der Code für das FormLoad-Event inklusive Kommentaren.

//Kartenwerte und dazugehörige Bezeichnungen für die Poker-Library
private Dictionary<string, string> cards = new Dictionary<string, string>()
{
    {"2", "2"}, {"3", "3"}, {"4", "4"}, {"5", "5"}, {"6", "6"}, {"7", "7"},
    {"8", "8"}, {"9", "9"}, {"10", "10"}, {"Bube", "J"}, {"Dame", "Q"},
    {"König", "K"}, { "Ass", "A" }
};

//Kartenfarben und dazugehörige Bezeichnungen für die Poker-Library
private Dictionary<string, string> colors = new Dictionary<string, string>()
{
    {"Pik", "s"}, {"Herz", "h"}, {"Karo", "d"}, {"Kreuz", "c"}
};

private void Form1_Load(object sender, EventArgs e)
{
    //Kartenbezeichnungen, -farben und Stapel in die Listboxen laden
    listBoxCard.Items.AddRange(cards.Select(x => x.Key).ToArray());
    listBoxColor.Items.AddRange(colors.Select(x => x.Key).ToArray());
    listBoxStack.Items.AddRange(new string[]{"Hand", "Deck"});
}

Die Lösung mittels Dictionary hat folgenden Grund: Die HandEvaluator-Library nimmt die Spielhände (also die Karten in der Hand des Spielers) nur in einem bestimmten Format an, welches aus Abkürzungen der englischen Kartennamen besteht. Da dies für den Nutzer jedoch relativ unverständlich sein kann, speichern wir die Kurzbezeichnungen für die Library inklusive sprechender Langbezeichnung auf Deutsch. So können wir in der Programmoberfläche sprechende Namen verwenden und diese später einfach durch die passenden Kurzbezeichnungen austauschen.

Karten auf einen Stapel hinzufügen

Sind die Initialwerte geladen, ist der Nutzer in der Lage Karten durch die Wahl des Wertes und der Farbe zu selektieren. Damit er diese auf einem Stapel (Hand oder Deck) platzieren kann, müssen wir nun die Button-Click-Methode für den “Hinzufügen”-Button ausprogrammieren.

private void buttonAddCard_Click(object sender, EventArgs e)
{
    //Überprüfe, ob Kartenwert, -farbe und der Zielstapel
    //ausgewählt wurden
    if (listBoxCard.SelectedItems.Count != 1 ||
        listBoxColor.SelectedItems.Count != 1 ||
        listBoxStack.SelectedItems.Count != 1)
    {
        MessageBox.Show("Bitte Karte, Farbe und Stack wählen.");
    }
    else
    {
        //Überprüfe ob der Zielstapel schon voll ist
        if ((listBoxDeck.Items.Count == 3 && listBoxStack.SelectedItem.ToString() == "Deck") ||
            (listBoxHand.Items.Count == 2 && listBoxStack.SelectedItem.ToString() == "Hand"))
        {
            MessageBox.Show("Maximale Kartenanzahl erreicht. Anderen Stack wählen oder zurücksetzen.");
        }
        else
        {
            //Füge die Karte dem Zielstapel hinzu
            if (listBoxStack.SelectedItem.ToString() == "Deck")
            {
                listBoxDeck.Items.Add(listBoxCard.SelectedItem.ToString() + "-" + listBoxColor.SelectedItem.ToString());
            }
            else
            {
                listBoxHand.Items.Add(listBoxCard.SelectedItem.ToString() + "-" + listBoxColor.SelectedItem.ToString());
            }
        }
    }
}

Zuerst wird geprüft, ob alle drei nötigen Angaben (Wert, Farbe und Zielstapel) gesetzt sind. Wenn dem so ist, wird der Zielstapel per if-Abfrage ermittelt und die Karte auf den Zielstapel übertragen.

Hat sich der Nutzer verklickt oder will seine Auswahl zurücksetzen, kann er dies über den “Zurücksetzen”-Button tun. Der Code für dessen Click-Event sieht wie folgt aus.

private void buttonReset_Click(object sender, EventArgs e)
{
    //Leere die Zielstapel um eine neue Berechnung zu ermöglichen
    listBoxHand.Items.Clear();
    listBoxDeck.Items.Clear();
}

Nun haben wir also die Initialwerte geladen und können Karten auf einen Stapel ablegen sowie die Stapel leeren. Im nächsten Schritt geht es um die Berechnung der Gewinnwahrscheinlichkeit.

Berechnen der Gewinnwahrscheinlichkeit

Die Berechnung der Siegchancen findet nach dem Klick auf den “Chancen berechnen”-Button statt. Der Code dafür liegt komplett im Click-Event dieses Buttons.

Die Funktionsweise des Codes sollte anhand der Kommentare im nachfolgende Sourcecode klar werden. (Wenn dennoch etwas unklar ist, dann schreibt einfach einen Kommentar unter diesen Artikel.)

private void buttonCalculate_Click(object sender, EventArgs e)
{
    //Überprüfe, ob beide Zielstapel ausreichend belegt sind
    if (listBoxHand.Items.Count != 2 ||
        listBoxDeck.Items.Count != 3)
    {
        MessageBox.Show("Bitte geben Sie erst die Hand und das Deck an.");
    }
    else
    {
        //Fasse alle Hand-Karten als string in der Poker-Library-Syntax zusammen
        string handCards = "";
        foreach (var item in listBoxHand.Items)
        {
            handCards += cards[item.ToString().Split('-')[0]]
                        + colors[item.ToString().Split('-')[1]]
                        + " ";
        }
        handCards = handCards.Trim();

        //Fasse alle Deck-Karten als string in der Poker-Library-Syntax zusammen
        string deckCards = "";
        foreach (var item in listBoxDeck.Items)
        {
            deckCards += cards[item.ToString().Split('-')[0]]
                        + colors[item.ToString().Split('-')[1]]
                        + " ";
        }
        deckCards = deckCards.Trim();

        //Initialisiere Hilfsvariablen für die Poker-Library
        double[] self = new double[9];
        double[] opponent = new double[9];
        double selfWin = 0.0;

        //Berechne Poker-Hände und Wahrscheinlichkeiten
        Hand.HandPlayerOpponentOdds(handCards, deckCards, ref self, ref opponent);

        //Berechne Sieg-/Split-Wahrscheinlichkeit
        for (int i = 0; i < 9; i++)
        {
            selfWin += self[i] * 100.0;
        }

        //Gebe Sieg-/Split-Wahrscheinlichkeit aus
        labelChance.Text = string.Format("{0:##0.0}%", selfWin);
    }
}

Nun ist die Applikation fürs Erste auch schon vollständig. Wir haben nun ein Tool, in das wir sowohl die Karten auf dem Tisch als auch unsere beiden Hand-Karten eingeben und uns mit einem Klick die Gewinnwahrscheinlichkeit anzeigen lassen können.

Fazit und Download

Wer nun “Blut geleckt hat”, kann das Programm noch um weitere Funktionen erweitern. Denkbar wäre eine Berechnung für vier Karten auf dem Tisch oder das dynamische Austauschen von Karten auf einem der beiden Stapel, ohne das beide Stapel komplett zurückgesetzt werden müssen.

Den Download des Visual Studio Projekts findet ihr unter folgendem Link:

Download: PokerChancenRechner – Visual Studio Projekt

Über Anregungen als auch (konstruktive) Kritik freue ich mich wie immer.

Raffi

Seit 2011 blogge ich hier über Programmierung, meine Software, schreibe Tutorials und versuche mein Wissen, so gut es geht, mit meinen Lesern zu teilen.

0 Kommentare

Hinterlasse einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Sie dient nur dem Spamschutz.