paint-brush
So erstellen Sie ein Blackjack-Spiel mit Alpine.js und der Deck of Cards APIby@raymondcamden
1,146
1,146

So erstellen Sie ein Blackjack-Spiel mit Alpine.js und der Deck of Cards API

Raymond Camden6m2023/07/20
Read on Terminal Reader
Read this story w/o Javascript

Die Deck of Cards API verwaltet alles, was man sich im Zusammenhang mit der Arbeit mit Kartendecks vorstellen kann. Es übernimmt das Erstellen eines gemischten Kartensatzes (der einen oder mehrere Stapel enthält), das Austeilen einer Karte (oder Karten) und sogar das Neumischen. Es handelt sich um eine unglaublich funktionsreiche API und das Beste ist, dass sie völlig kostenlos ist.
featured image - So erstellen Sie ein Blackjack-Spiel mit Alpine.js und der Deck of Cards API
Raymond Camden HackerNoon profile picture

Vor einiger Zeit bin ich auf einen ziemlich faszinierenden Dienst gestoßen, die Deck of Cards API . Diese API verarbeitet alles, was man sich im Zusammenhang mit der Arbeit mit Kartenspielen vorstellen kann. Es übernimmt das Erstellen eines gemischten Kartensatzes (der einen oder mehrere Stapel enthält), das Austeilen einer Karte (oder Karten) und sogar das Neumischen.


Noch besser: Es enthält Kartenbilder, die Sie verwenden können, wenn Sie keine eigenen finden möchten:


Es handelt sich um eine unglaublich funktionsreiche API, und das Beste ist, dass sie völlig kostenlos ist. Es ist nicht einmal ein Schlüssel erforderlich. Ich kenne diese API schon seit einiger Zeit und habe darüber nachgedacht, damit ein Kartenspiel zu erstellen, habe aber festgestellt, dass Spiele schnell von einfach zu ziemlich komplex werden können.


Tatsächlich haben mir meine Freunde dringend geraten, keine Zeit damit zu verschwenden, und ehrlich gesagt hatten sie wahrscheinlich Recht, aber ich habe eine lange Geschichte mit der Erstellung von Code-Demos, die keinen Sinn ergeben. ;)


Für meine Demo habe ich mich an die folgenden Regeln gehalten:


  • Offensichtlich ist es bei den Grundregeln von Blackjack so, möglichst nahe an die 21 heranzukommen, ohne sie zu überschreiten.


  • Kein Einsatz, nur eine Hand nach der anderen.


  • Kein Verdoppeln oder Aufteilen.


  • Der Händler hat eine „Soft 17“-Regel. (Ich bin mir größtenteils sicher, dass ich das richtig gemacht habe.)


  • Das Spiel verwendet sechs Decks (ich habe irgendwo gelesen, dass es Standard war).

Spiel-Setup

Zunächst verfügen sowohl der Spieler als auch der Computer über ein Array, das ihre Hände darstellt.


 playerCards:[], pcCards:[],


Die deal Methode übernimmt das Einrichten der Hände für beide Spieler:


 async deal() { // first to player, then PC, then player, then PC this.playerCards.push(await this.drawCard()); // for the dealer, the first card is turned over let newcard = await this.drawCard(); newcard.showback = true; this.pcCards.push(newcard); this.playerCards.push(await this.drawCard()); this.pcCards.push(await this.drawCard()); },


Zwei Dinge sind hervorzuheben. Zuerst teile ich es dem Spieler aus, dann dem PC (oder dem Dealer, was den Namen angeht, gehe ich hin und her) und dann wieder zurück. Außerdem ändere ich das Kartenergebnisobjekt so, dass es showback Einstellung hat, sodass ich die Rückseite der Karte für den Dealer rendern kann.


So geht das in HTML:


 <div id="pcArea" class="cardArea"> <h3>Dealer</h3> <template x-for="card in pcCards"> <!-- todo: don't like the logic in template --> <img :src="card.showback?BACK_CARD:card.image" :title="card.showback?'':card.title"> </template> </div> <div id="playerArea" class="cardArea"> <h3>Player</h3> <template x-for="card in playerCards"> <img :src="card.image" :title="card.title"> </template> </div>


BACK_CARD ist einfach eine Konstante:


 const BACK_CARD = "https://deckofcardsapi.com/static/img/back.png";

Spiellogik

An diesem Punkt könnte ich also die App aufrufen und eine Blackjack-Hand erhalten:


Demo der angezeigten Karten


Unten habe ich ein div verwendet, um den aktuellen Status anzuzeigen:


Das weiße Statusfeld fragt den Spieler, was er tun möchte.


Meine Logik war so:


  • Beginnen Sie mit dem Spieler und lassen Sie ihn schlagen oder stehen.


  • Wenn sie getroffen haben, fügen Sie eine neue Karte hinzu und prüfen Sie, ob sie kaputt gegangen sind.


  • Wenn sie stehen bleiben, lassen Sie den Dealer-Spieler.


Konzentrieren wir uns zunächst auf den Spieler. Zum Schlagen fügen wir einfach eine Karte hinzu:


 async hitMe() { this.hitMeDisabled = true; this.playerCards.push(await this.drawCard()); let count = this.getCount(this.playerCards); if(count.lowCount >= 22) { this.playerTurn = false; this.playerBusted = true; } this.hitMeDisabled = false; },


Die Brustprüfung war etwas komplex. Ich habe eine Funktion erstellt, um die „Anzahl“ für die Hand zu ermitteln, aber beim Blackjack können Asse 1 oder 11 sein.


Ich habe herausgefunden (und hoffe, dass ich recht habe), dass man niemals zwei „hohe“ Asse haben kann, also gibt meine Funktion einen lowCount und highCount Wert zurück, wobei für die hohe Version ein Ass, wenn es existiert, als 11 gezählt wird, aber nur eins. Hier ist diese Logik:


 getCount(hand) { /* For a hand, I return 2 values, a low value, where aces are considered 1s, and a high value, where aces are 11. Note that this fails to properly handle a case where I have 3 aces and could have a mix... although thinking about it, you can only have ONE ace at 11, so maybe the logic is: low == all aces at 1. high = ONE ace at 11. fixed! */ let result = {}; // first we will do low, all 1s let lowCount = 0; for(card of hand) { if(card.value === 'JACK' || card.value === 'KING' || card.value === 'QUEEN') lowCount+=10; else if(card.value === 'ACE') lowCount += 1; else lowCount += Number(card.value); //console.log(card); } //console.log('lowCount', lowCount); let highCount = 0; let oneAce = false; for(card of hand) { if(card.value === 'JACK' || card.value === 'KING' || card.value === 'QUEEN') highCount+=10; else if(card.value === 'ACE') { if(oneAce) highCount += 1; else { highCount += 10; oneAce = true; } } else highCount += Number(card.value); } //console.log('highCount', highCount); return { lowCount, highCount }; },


Wenn der Spieler ausscheidet, beenden wir das Spiel und lassen den Benutzer von vorne beginnen. Wenn sie bestehen bleiben, ist es Zeit für den Händler, zu übernehmen. Diese Logik war einfach: einen Treffer erzielen, während man unter 17 ist, und entweder Pleit oder Stand.


Um es etwas spannender zu machen, habe ich eine variable und asynchrone Funktion namens delay verwendet, um die Aktionen des Dealers zu verlangsamen, sodass Sie sie (irgendwie) in Echtzeit sehen können. Hier ist die Logik des Händlers:


 async startDealer() { /* Idea is - I take a card everytime I'm < 17. so i check my hand, and do it, see if im going to stay or hit. if hit, i do a delay though so the game isn't instant. */ // really first, initial text this.pcText = 'The dealer begins their turn...'; await delay(DEALER_PAUSE); // first, a pause while we talk this.pcText = 'Let me show my hand...'; await delay(DEALER_PAUSE); // reveal my second card this.pcCards[0].showback = false; // what does the player have, we need the best under 22 let playerCount = this.getCount(this.playerCards); let playerScore = playerCount.lowCount; if(playerCount.highCount < 22) playerScore = playerCount.highCount; //console.log('dealer needs to beat', playerScore); // ok, now we're going to loop until i bust/win let dealerLoop = true; while(dealerLoop) { let count = this.getCount(this.pcCards); /* We are NOT doing 'soft 17', so 1 ace always count as 11 */ if(count.highCount <= 16) { this.pcText = 'Dealer draws a card...'; await delay(DEALER_PAUSE); this.pcCards.push(await this.drawCard()); } else if(count.highCount <= 21) { this.pcText = 'Dealer stays...'; await delay(DEALER_PAUSE); dealerLoop = false; this.pcTurn = false; if(count.highCount >= playerScore) this.pcWon = true; else this.playerWon = true; } else { dealerLoop = false; this.pcTurn = false; this.pcBusted = true; } } }


Zu Ihrer Information: pcText wird im weißen Statusbereich verwendet, um Spielnachrichten festzulegen.


Und im Grunde – das ist es. Wenn Sie es selbst spielen möchten, schauen Sie sich den CodePen unten an und teilen Sie ihn gerne mit einem Fork und fügen Sie Verbesserungen hinzu: