কিছু সময় আগে, আমি একটি চমত্কার চিত্তাকর্ষক পরিষেবা জুড়ে দৌড়েছিলাম, ডেক অফ কার্ডস এপিআই ৷ এই API কার্ডের ডেকের সাথে কাজ করার সাথে সম্পর্কিত কল্পনাযোগ্য সবকিছু পরিচালনা করে। এটি কার্ডের একটি এলোমেলো সেট তৈরি (এক বা একাধিক ডেক ধারণকারী), একটি কার্ড (বা কার্ড), এমনকি রদবদল করা পরিচালনা করে।
আরও ভাল, এতে কার্ডের ছবিগুলি অন্তর্ভুক্ত রয়েছে যা আপনি ব্যবহার করতে পারেন যদি আপনি নিজের সন্ধান করতে না চান:
এটি একটি অবিশ্বাস্যভাবে বৈশিষ্ট্য-পূর্ণ API, এবং সর্বোপরি, এটি সম্পূর্ণ বিনামূল্যে। এমনকি চাবিরও দরকার নেই। আমি কিছু সময়ের জন্য এই API সম্পর্কে জেনেছি এবং এটির সাথে একটি কার্ড গেম তৈরি করার চিন্তা করেছি, কিন্তু বুঝতে পেরেছি যে গেমগুলি দ্রুত সহজ থেকে মোটামুটি জটিল হতে পারে।
প্রকৃতপক্ষে, আমার বন্ধুরা আমাকে দৃঢ়ভাবে এই বিষয়ে সময় ব্যয় না করার জন্য অনুরোধ করেছিল, এবং সত্যই, তারা সম্ভবত সঠিক ছিল, কিন্তু আমি বিল্ডিং কোড ডেমোগুলির একটি দীর্ঘ ইতিহাস পেয়েছি যা অর্থহীন। ;)
আমার ডেমোর জন্য, আমি নিম্নলিখিত নিয়মগুলির সাথে গিয়েছিলাম:
প্রাথমিকভাবে, প্লেয়ার এবং কম্পিউটার উভয়েরই তাদের হাতের প্রতিনিধিত্ব করে একটি অ্যারে থাকে।
playerCards:[], pcCards:[],
deal
পদ্ধতি উভয় খেলোয়াড়ের জন্য হাত সেট আপ পরিচালনা করে:
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()); },
উল্লেখ করার জন্য দুটি জিনিস। প্রথমত, আমি প্লেয়ারের সাথে ডিল করি, তারপর পিসি (বা ডিলার, নাম অনুসারে আমি একটু পিছনে যাই), এবং তারপর আবার ফিরে যাই। showback
সেট করার জন্য আমি কার্ডের ফলাফল অবজেক্টটিও পরিবর্তন করি যাতে আমি ডিলারের জন্য কার্ডের পিছনে রেন্ডার করতে পারি।
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
কেবল একটি ধ্রুবক:
const BACK_CARD = "https://deckofcardsapi.com/static/img/back.png";
সুতরাং, এই মুহুর্তে, আমি অ্যাপটি আঘাত করতে পারি এবং একটি ব্ল্যাকজ্যাক হাত পেতে পারি:
নীচে, আমি বর্তমান অবস্থা প্রদর্শন করতে একটি ডিভ ব্যবহার করেছি:
আমার যুক্তি ছিল এরকম:
প্রথমে প্লেয়ারের উপর ফোকাস করা যাক। আঘাত করতে, আমরা কেবল একটি কার্ড যোগ করি:
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; },
বক্ষ চেকিং একটু জটিল ছিল। আমি হাতের জন্য 'গণনা' পেতে একটি ফাংশন তৈরি করেছি, কিন্তু ব্ল্যাকজ্যাকে, Aces 1 বা 11 হতে পারে।
আমি বুঝতে পেরেছি (এবং আশা করি আমি ঠিকই বলেছি), আপনার কখনই দুটি 'উচ্চ' টেপ থাকতে পারে না, তাই আমার ফাংশন একটি lowCount
এবং highCount
মান প্রদান করে যেখানে উচ্চ সংস্করণের জন্য, যদি একটি Ace বিদ্যমান থাকে তবে এটি 11 হিসাবে গণনা করা হয়, কিন্তু শুধুমাত্র এক. এখানে সেই যুক্তি আছে:
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 }; },
যদি প্লেয়ার হাস্ট হয়, আমরা গেমটি শেষ করি এবং ব্যবহারকারীকে আবার শুরু করতে দিই। যদি তারা দাঁড়ায়, ডিলারের দায়িত্ব নেওয়ার সময় এসেছে। এই যুক্তিটি সহজ ছিল - 17 এর নিচে থাকাকালীন আঘাত করুন এবং হয় বক্ষ বা দাঁড়ানো।
এটিকে আরও উত্তেজনাপূর্ণ করার জন্য, আমি একটি পরিবর্তনশীল এবং অ্যাসিঙ্ক ফাংশন ব্যবহার করেছি, ডিলারের ক্রিয়াগুলিকে delay
করার জন্য, যাতে আপনি তাদের (একরকম) রিয়েল-টাইমে খেলতে দেখতে পারেন৷ এখানে ডিলারের যুক্তি:
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; } } }
FYI, pcText
সাদা স্ট্যাটাস এলাকায় গেম মেসেজ সেট করার উপায় হিসেবে ব্যবহার করা হয়।
এবং মূলত - এটাই। আপনি যদি এটি নিজে খেলতে চান, নীচের কোডপেনটি দেখুন এবং নির্দ্বিধায় এটিকে কাঁটাচামচ করতে এবং উন্নতিগুলি যোগ করুন: