/** 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"));