2020
Blackjack (JS)

Blackjack - Kartenspiel in JS erstellen und mischen

Datum: Juli 2020
Lesedauer: 4 Minuten


Vor ein paar Wochen erhielt ich den Auftrag, ein Spiel mit HTML, CSS und JS umzusetzen. Dabei fiel die Wahl auf Blackjack, das am meisten gespielte Glücksspiel.

Das Spiel ist aufwendig zu programmieren und auch die vielen Regeln vereinfachen dies nicht. Daher bezieht sich dieser Post lediglich auf das Erstellen und Mischen des Decks.

Deck erstellen

Um Blackjack zu spielen, benötigt man ein Kartenspiel. Dies galt auch für mein Spiel. Um das Deck zu erstellen, verwendete ich JSON. Darin konnte ich meine Daten gut lesbar speichern. Dies sah wie folgt aus:

{
    "cards": [
        {
            "suit": "clubs",
            "card": "ace",
            "value": "11",
            "img": "../pcards/clubs/ace.png"
        },
        {
            "suit": "clubs",
            "card": "2",
            "value": "2",
            "img": "../pcards/clubs/two.png"
        }
    ]
}

Eine Karte besteht aus vier Teilen.

  • Farbe: Kreuz/Pik/Herz/Karo
  • Karte: 2-10/Junge/Dame/König/Ass
  • Wert: Wert der jeweiligen Karten *
  • Bild: Link zum Bild mit der entsprechenden Karte

* Ein Ass kann als 11 oder 1 gezählt werden. Im JSON habe ich jedoch den Wert 11 angegeben. Dieser wird zu 1 geändert, sobald 21 überschritten wird. Somit hat der Spieler sowie auch die Bank immer die bestmögliche Punktzahl.

Deck einbinden

Um diese Karten im JS zu verwenden, musste ich sie zuerst einbinden. Dafür nutzte ich die bekannteste JS-Bibliothek: jQuery
jQuery stellt hilfreiche Funktionen zur verfügung. Um die Bibliothek zu verwenden fügte ich folgenden script Tag ins HTML ein:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
$.getJSON('bjgame.json', function (deck) {});

Mit wenig Code in JS kann ich nun das Objekt unter dem Namen deck verwenden. Will ich jetzt den Wert der ersten Karte so kann ich diesen mit deck.cards[0].value ermitteln.

Deck mischen

Wenn man ein faires und realistisches Spiel erreichen will, ist das Mischen ein entscheidender Punkt. Daher wollte ich sicherstellen, dass das Deck gut gemischt wird.

Fun Fact

Wenn man seit dem 12. Jahrhundert (als Spielkarten auftauchen),jede Sekunde eine Million Kartenspiele gemischt hätte, wäre die Wahrscheinlichkeit eine Reihenfolge zu erhalten, die vorher schon vorkam, bei:
1 zu 2.867.038.870.154.391.317.064.159.598.662.483.914.762.118.736.300.000
Das sind mehrere Oktilliarden… fast unmöglich.

Permutation

Zur Berechnung der Anzahl von möglichen Anordnungen benötigt man folgende Formel:
n! = n * (n - 1) * (n - 2) * ... * 1

Wenn das Deck nur aus den 4 Assen bestehen würde, so gäbe es 24 Möglichkeiten.

  • Zu beginn kann ein Ass von 4 ausgewählt werden
  • Danach sind noch übrig 3
  • Nachdem man schon zwei platziert hat, kann man sich für eins der 2 verbleibenden Assen entscheiden
  • Zum Schluss Platziert man die letzte Karte

Man muss jeweils die Anzahl Optionen multiplizieren: 4 * 3 * 2 * 1 = 24

Mit 7 Karten sind das schon 5040 Möglichkeiten: 7 * 6 * 5 * 4 * 3 * 2 * 1 = 5040

Will man die Möglichkeiten für das ganze Deck berechnen, so muss man 52 * (52 - 1) * (52 - 2) * ... * 1 rechnen. Diese Zahl wird viel grösser als die Anzahl Atome auf dieser Erde sein.

Die Anzahl Möglichkeiten kann auf dieser Webseite leicht ermittelt werden: https://www.zum.de/Faecher/Materialien/gebhardt/stochastik/Kombin.html (opens in a new tab)

So mische ich meine Karten

function shuffleCards(deck) {
    for (let counter = deck.cards.length - 1; counter >= 0; counter--) {
        let randomIndex = Math.floor(Math.random() * counter);
        let store = deck.cards[counter];
        deck.cards[counter] = deck.cards[randomIndex];
        deck.cards[randomIndex] = store;
    }
}

Mein Zähler (counter) hat den Wert 51, da ein Kartenspiel 52 Karten hat und ein Array bei 0 beginnt.

Die for Schleife wird also 52-mal ausgeführt. Jede Karte wird in der store Variable gespeichert. Danach wird sie durch eine zufällige Karte ersetzt. Die zufällige Karte wird wiederum durch die Karte ersetzt, welche zuvor in der store Variable gespeichert wurde.

Nach diesem Vorgang hat die Karte mit einer zufälligen, anderen Karte den Platz getauscht. Nun wird dasselbe mit der Karte an der 50. Position gemacht.

Diese Grafik veranschaulicht dies:

shuffle

Danach sind die Karten gemischt.

mixed

Es gibt es natürlich mehrere Möglichkeiten einen Array zu mischen.

Überlegungen

Random

ch habe mich gefragt, wie zufällig eine von einem Computer generierte Zahl sein kann. Jedoch kam bei meiner Recherche heraus, das math.random() gut ist.

Wie math.random eine Zahl generiert, kann man hier nachlesen: https://hackernoon.com/how-does-javascripts-math-random-generate-random-numbers-ef0de6a20131 (opens in a new tab)

Sieben

In einem Casino werden normalerweise 6 oder 8 Decks verwendet. Diese werden lange zusammen gemischt, bevor sie in den Kartenschlitten platziert werden. Dadurch wird der Bankvorteil verändert. Die Wahrscheinlichkeit, ein Blackjack zu erhalten, liegt mit einem Deck bei 4,83%, mit 8 Decks nur noch bei 4,75%. Ich wollte das Ganze nicht zu kompliziert machen. Daher habe ich mich vorerst für ein Deck entschieden.

Danach fragte ich mich, wieviel ich vor dem Austeilen mischen soll. Ich habe mich für 7-mal entschieden, da dies laut Studien das Kartenspiel zufällig durchmischt:

function shuffleCards(deck) {
    for (let shuffleCounter = 1; shuffleCounter <= 7; shuffleCounter++) {
        for (let counter = deck.cards.length - 1; counter >= 0; counter--) {
            let randomIndex = Math.floor(Math.random() * counter);
            let store = deck.cards[counter];
            deck.cards[counter] = deck.cards[randomIndex];
            deck.cards[randomIndex] = store;
        }
    }
}

Nun muss ich mir noch überlegen, wann ich das nächste Mal mische. Zum jetzigen Zeitpunkt mische ich nach jeder Runde.

Mischen? Wozu?

Um zufällige Karten austeilen zu können, müsste man das Deck nicht mischen. Man könnte die Stelle der nächsten Karte mit math.random() ermitteln und austeilen. So würden die Karten auch durcheinander ausgeteilt werden.
Das Mischen bildet jedoch die Realität besser ab.

Ende

Dies ist zwar das Ende dieses Blogposts, das Spiel ist jedoch noch nicht fertig. Es ist eine äusserst spannende Aufgabe. Man kann sich extrem viel überlegen und recherchieren. Es gibt auch unzählige Möglichkeiten neue Dinge einzubauen. Dabei lernt man einige spannende Dinge übers Spiel und natürlich sehr viel übers Programmieren.