Bir süre önce oldukça etkileyici bir hizmetle karşılaştım: Kart Destesi API'si . Bu API, kart desteleriyle çalışmayla ilgili akla gelebilecek her şeyi yönetir. Karıştırılmış bir kart seti (bir veya daha fazla deste içeren) oluşturmayı, bir kartı (veya kartları) dağıtmayı ve hatta yeniden karıştırmayı yönetir.
Daha da iyisi, kendi kart görsellerinizi bulmak istemiyorsanız kullanabileceğiniz kart görsellerini içerir:
İnanılmaz derecede özelliklerle dolu bir API'dir ve hepsinden önemlisi tamamen ücretsizdir. Anahtara bile gerek yok. Bu API'yi bir süredir biliyordum ve onunla bir kart oyunu oluşturmayı düşündüm, ancak oyunların basitten oldukça karmaşık hale hızla geçebileceğini fark ettim.
Aslında arkadaşlarım buna zaman harcamamam konusunda beni şiddetle teşvik ettiler ve dürüst olmak gerekirse, muhtemelen haklıydılar, ancak hiçbir anlam ifade etmeyen kod demoları oluşturma konusunda uzun bir geçmişim var. ;)
Demom için aşağıdaki kurallara uydum:
Başlangıçta hem oyuncu hem de bilgisayar, ellerini temsil eden bir diziye sahiptir.
playerCards:[], pcCards:[],
deal
yöntemi her iki oyuncu için de ellerin ayarlanmasını yönetir:
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()); },
Dikkat edilmesi gereken iki şey. Önce oyuncuyla, sonra PC'yle (ya da dağıtıcıyla, isim olarak ileri geri gidiyorum) ve sonra tekrar geri dönüyorum. Ayrıca kart sonuç nesnesini, kartın arkasını krupiyer için oluşturabileceğim şekilde showback
sahip olacak şekilde değiştiriyorum.
HTML'de bunun nasıl yapıldığı aşağıda açıklanmıştır:
<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
basitçe bir sabittir:
const BACK_CARD = "https://deckofcardsapi.com/static/img/back.png";
Yani bu noktada uygulamaya tıklayıp Blackjack eli alabilirim:
Altta mevcut durumu görüntülemek için bir div kullandım:
Benim mantığım şöyleydi:
Önce oyuncuya odaklanalım. Vurmak için bir kart eklememiz yeterli:
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; },
Göğüs kontrolü biraz karmaşıktı. Elin 'sayımını' elde etmek için bir fonksiyon geliştirdim, ancak Blackjack'te Aslar 1 veya 11 olabilir.
Asla iki 'yüksek' asa sahip olamayacağınızı anladım (ve umarım haklıyımdır), bu nedenle işlevim bir lowCount
ve highCount
değeri döndürür; burada yüksek sürüm için, eğer bir As varsa, 11 olarak sayılır, ancak yalnızca bir. İşte o mantık:
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 }; },
Eğer oyuncu bozulursa oyunu sonlandırıyoruz ve kullanıcının baştan başlamasına izin veriyoruz. Eğer ayakta kalırlarsa, krupiyerin görevi devralma zamanı gelmiştir. Bu mantık basitti; 17'nin altındayken vurun ve ya çökün ya da ayakta kalın.
Bunu biraz daha heyecanlı hale getirmek için, dağıtıcının eylemlerini yavaşlatmak için değişken ve eşzamansız bir işlev olan delay
kullandım, böylece bunların (bir nevi) gerçek zamanlı olarak oynandığını görebilirsiniz. İşte krupiyenin mantığı:
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; } } }
Bilginize, pcText
beyaz durum alanında oyun mesajlarını ayarlamanın bir yolu olarak kullanılır.
Ve temelde - işte bu. Kendiniz oynamak istiyorsanız, aşağıdaki CodePen'e göz atın ve onu çatallamaktan ve iyileştirmeler eklemekten çekinmeyin: