0
\$\begingroup\$

this is similar to another project I did here: JavaScript Deck Builder: Shuffle, Draw Cards Except this script does not shuffle or draw cards from the top of the deck. Instead the script builds a deck, then will randomly choose a card from the deck. The card that is chosen is then removed from the deck and placed into the discard pile. Like my previous post I am looking for critiques or different ways I could approach writing this script.

const cardGame = {
values: [ 'Ace', 2, 3, 4, 5, 6, 7, 8, 9, 10, 'Joker', 'Queen', 'King' ],
suits: [ 'Clubs', 'Spades', 'Hearts', 'Diamonds' ],
deck: [],
discard: [],
pick(arr) {
 const idx = Math.floor(Math.random() * arr.length);
 return arr[idx];
},
buildDeck() {
 const { deck, discard, values, suits } = this;
 //clear deck and discard pile
 deck.splice(0, 52);
 discard.splice(0, 52);
 //regenerate/build deck
 for (let suit of suits) {
 for (let value of values) {
 deck.push({ value, suit });
 }
 }
},
getCard() {
 const { pick, deck } = this;
 return pick(deck);
},
drawCard(numCards = 1) {
 const { deck, discard } = this;
 const drawnCards = [];
 //draw cards
 while (drawnCards.length < numCards) {
 let card = this.getCard();
 //check if card has been drawn
 for (let drawnCard of drawnCards) {
 let matchSuit = card.suit === drawnCard.suit;
 let matchValue = card.value === drawnCard.value;
 if (matchSuit && matchValue) {
 card = this.getCard();
 }
 }
 console.log(card);
 drawnCards.push(card);
 }
 //discard drawn cards from deck
 for (let drawnCard of drawnCards) {
 for (let i = 0; i < deck.length; i++) {
 let card = deck[i];
 let matchSuit = card.suit === drawnCard.suit;
 let matchValue = card.value === drawnCard.value;
 if (matchSuit && matchValue) {
 deck.splice(i, 1);
 discard.push(card);
 }
 }
 }
 console.log(deck.length);
}
};
cardGame.buildDeck();
asked Aug 22, 2021 at 21:56
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

General points

  • To empty an array use the length property eg. Rather than use deck.splice(0, 52); use deck.length = 0;
  • You can simplify the card array and use a number 0 to 51 to represent a card. The card suit is the this.suits[(value / 13 | 0) % 4] and the card value this.values[cardId % 13]
  • the function drawCard lets you draw more than one. It should be called drawCards

Rewrite

The rewrite uses a completely different approach for handling a set (deck of card) of unique items

  • IIFE (Immediately Invoke Function Expression) to encapsulate the deck so that code using the deck can not break the state of the deck.

  • Cards are stored by id (Number 0 - 51) rather than using object.

  • Adding and removing single cards is done using getters and setters. Eg to get a random card const randomCard = deck.random or to get top card const topCard = deck.card

    You can also add a card to the top of the deck deck.card = cardId or add it randomly deck.random = cardId

    Note that cards will not be added if the deck already contains that card.

    Note that if the deck is out of cards getting cards will return undefined

  • deck.drawCards can draw random or from the top of the deck.

  • Cards are defined by unique ID. The IIFE has a deckCount argument to define the number of decks used. This means that when adding cards back to the deck only cards handed out will be accepted back.

    Note max deckCount is set to 12 however this value is arbitrary and can be any value < infinity

const deck = ((deckCount) => {
 deckCount = Math.min(12, Math.max(1, isNaN(deckCount) ? 2 : deckCount | 0));
 const VALUE = "A,2,3,4,5,6,7,8,9,10,J,Q,K".split(",");
 const SUITS = "♣,♠,♥,♦".split(",");
 const deck = [];
 const randIdx = () => Math.random() * deck.length | 0;
 return (Object.freeze({
 nameCard(id) { return VALUE[id % 13] + "" + SUITS[(id / 13 | 0) % 4] },
 handAsStr(hand) { return hand.map(cardId => this.nameCard(cardId)).join(", ") },
 set card(id) { !deck.includes(id) && deck.push(id) },
 set random(id) { !deck.includes(id) && deck.splice(randIdx(), 0, id) },
 get card() { return deck.pop() },
 get random() { return deck.length ? deck.splice(randIdx(), 1)[0] : undefined },
 newDeck() {
 deck.length = 0;
 while(deck.length < 52 * deckCount) { this.card = deck.length }
 return this;
 },
 drawCards(cards = 1, rand = true) {
 var c;
 const drawn = [];
 while (cards-- > 0 && deck.length) { 
 (c = (rand ? this.random : this.card)) !== undefined && drawn.push(c);
 }
 return drawn; 
 },
 get length() { return deck.length },
 })).newDeck();
})("pho");
console.log(deck.handAsStr(deck.drawCards(5)));
console.log(deck.handAsStr(deck.drawCards(5)));
console.log(deck.handAsStr(deck.drawCards(5)));

answered Aug 23, 2021 at 1:18
\$\endgroup\$
2
  • \$\begingroup\$ This is late but I wanted to say thank you for the points you provided in your response and the rewrite. I was unaware of IIFEs, Object.freeze(), getters and setters, and programming concepts such as encapsulation prior to posting my question, but the rewrite you provided made me learn about these different concepts, that I will apply when I write other scripts. The only part of the rewrite which I did not understand was at get random() there is brackets [0] after deck.splice(randIdx(), 1). What are the brackets intended for? \$\endgroup\$ Commented Aug 29, 2021 at 19:15
  • 1
    \$\begingroup\$ @fire_hydrant Array.splice(index, count) returns an array of items spliced from the array. In the example I only remove 1 item deck.splice(randIdx(), 1)[0] thus the [0] means the 1st item in the array of spliced items. Reference Array.splice at MDN \$\endgroup\$ Commented Aug 29, 2021 at 20:11

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.