/** Constructs a deck of playing cards. */ function DeckOfCards() { /* Fields */ const thisDeck = this; const maxSize = Card.RANK_COUNT * Card.SUIT_COUNT + DeckOfCards.JOKER_COUNT; let deck = new Deque(maxSize); /* Initialization */ for (let suit = 1; suit <= Card.SUIT_COUNT; suit++) { for (let i = 0, rank=2; i < Card.RANK_COUNT; i++, rank++) { const card = new Card(suit, rank); deck.push(card); } } for (let j = 0; j < DeckOfCards.JOKER_COUNT; j++) { deck.push(new Card(0, 0)); // Jokers } /* Methods */ thisDeck.shuffle = function() { const currSize = deck.getSize(); const shuffledDeck = new Deque(maxSize); for (let n = currSize; n > 0; n--) { const r = Math.random() * n; for (let i = 1; i < r; i++) { deck.unshift(deck.pop()); } shuffledDeck.push(deck.pop()); } deck = shuffledDeck; return thisDeck; }; thisDeck.toString = function(lang) { let str = ""; for (let i = 0; i < deck.getSize(); i++) { if (i > 0) str += ", "; let card = deck.shift(); str += card.toString(lang); deck.push(card); } return str; }; } DeckOfCards.JOKER_COUNT = 2; Object.freeze(DeckOfCards); /** Constructs a double-ended queue */ function Deque(maxSize) { /* Fields */ const thisDeque = this; const deque = []; let size = 0; /* Properties */ Object.defineProperty(thisDeque, "maxSize", { value: Number.isFinite(maxSize) && maxSize > 0 ? maxSize : Infinity, writable: false }); /* Methods */ thisDeque.getSize = function() { return size; }; thisDeque.isEmpty = function() { return size === 0; }; thisDeque.push = function(element) { if (size < thisDeque.maxSize) { deque.push(element); size++; } return thisDeque; }; thisDeque.pop = function() { let element; if (size > 0) { element = deque.pop(); size--; } return element; }; thisDeque.shift = function() { let element; if (size > 0) { element = deque.shift(); size--; } return element; }; thisDeque.unshift = function(element) { if (size < thisDeque.maxSize) { deque.unshift(element); size++; } return thisDeque; }; } Object.freeze(Deque); /** Constructs a playing card. */ function Card(suit, rank) { const thisCard = this; /* Properties */ Object.defineProperty(thisCard, "rank", { value: Number.isInteger(rank) && rank > 0 && rank < Card.RANK_NAMES.length ? rank : 0, writable: false }); Object.defineProperty(thisCard, "suit", { value: Number.isInteger(suit) && suit > 0 && suit <= Card.SUIT_COUNT ? suit : 0, writable: false }); /* Methods */ thisCard.compare = function(otherCard) { return Card.compare(thisCard, otherCard); }; thisCard.toString = function(lang) { return Card.toString(thisCard, lang); }; } /* Static properties of Card */ Card.RANK_COUNT = 13; Card.SUIT_COUNT = 4; Card.DEFAULT_LANGUAGE = "French"; Card.RANK_NAMES = Object.freeze([ "Joker","Ace","2","3","4","5","6","7", "8","9","10","Jack","King","Queen","King","Ace" ]); Card.SUIT_NAMES = Object.freeze({ French: [null,"Hearts","Diamonds","Clubs","Spades"], German: [null,"Hearts","Bells","Acorns","Leaves"] }); /* Static methods of Card */ Card.compare = function(c1, c2) { return c1.rank - c2.rank; } Card.toString = function(card, lang) { if (!Card.SUIT_NAMES.hasOwnProperty(lang)) lang = Card.DEFAULT_LANGUAGE; let str = Card.RANK_NAMES[card.rank]; if (card.suit > 0) str += " of " + Card.SUIT_NAMES[lang][card.suit]; return str; }; Object.freeze(Card); alert((new DeckOfCards()).shuffle().toString("German"));