Skip to content

Instantly share code, notes, and snippets.

@Sans3108
Created April 14, 2024 15:48
Show Gist options
  • Save Sans3108/13b44a836177d54a49271bbb22dcafc2 to your computer and use it in GitHub Desktop.
Save Sans3108/13b44a836177d54a49271bbb22dcafc2 to your computer and use it in GitHub Desktop.
Cards
import { randomInt } from 'crypto';
type DeepRequired<T extends object> = Required<{
[P in keyof T]: T[P] extends object | undefined ? DeepRequired<Required<T[P]>> : T[P];
}>;
enum CardSuit {
hearts = '♥',
diamonds = '♦',
clubs = '♣',
spades = '♠'
}
enum CardValue {
ace = 'A',
two = '2',
three = '3',
four = '4',
five = '5',
six = '6',
seven = '7',
eight = '8',
nine = '9',
ten = '10',
jack = 'J',
queen = 'Q',
king = 'K'
}
class Card {
public readonly value: CardValue;
public readonly suit: CardSuit;
constructor(value: keyof typeof CardValue, suit: keyof typeof CardSuit) {
this.value = CardValue[value];
this.suit = CardSuit[suit];
}
toString() {
return `${this.value}${this.suit}`;
}
}
abstract class CardCointainer {
protected _cardContainer: Card[] = [];
constructor() {}
protected static _shuffleArray<T>(array: T[]) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(randomInt(0, i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
}
protected _isContainerEmpty(): boolean {
return this._cardContainer.length === 0;
}
protected _isContainerFull(): boolean {
return this._cardContainer.length === 52;
}
protected _containerHasCard(card: Card): boolean {
return this._cardContainer.includes(card);
}
/**
* Adds a card to the beginning of the container array.
*/
public addCardStart(card: Card): boolean {
if (this._isContainerFull() || this._containerHasCard(card)) return false;
this._cardContainer.unshift(card);
return true;
}
/**
* Adds a card at the end of the container array.
*/
public addCardEnd(card: Card): boolean {
if (this._isContainerFull() || this._containerHasCard(card)) return false;
this._cardContainer.push(card);
return true;
}
/**
* Adds a card in the container array at a random position.
*/
public addCardRandom(card: Card): boolean {
if (this._isContainerFull() || this._containerHasCard(card)) return false;
const randomIndex = randomInt(0, this._cardContainer.length);
this._cardContainer.splice(randomIndex, 0, card);
return true;
}
/**
* Removes a card from the beginning of the container array and returns it.
*/
public removeCardStart(): Card | null {
if (this._isContainerEmpty()) return null;
return this._cardContainer.shift()!;
}
/**
* Removes a card from the end of the container array and returns it.
*/
public removeCardEnd(): Card | null {
if (this._isContainerEmpty()) return null;
return this._cardContainer.pop()!;
}
/**
* Removes a random card from the container array and returns it.
*/
public removeCardRandom(): Card | null {
if (this._isContainerEmpty()) return null;
const randomIndex = randomInt(0, this._cardContainer.length);
return this._cardContainer.splice(randomIndex, 1)[0];
}
public get cards() {
return Object.freeze([...this._cardContainer]);
}
}
interface DeckOptions {
populate?: boolean;
shuffle?: boolean;
}
class Deck extends CardCointainer {
private readonly opts: DeepRequired<DeckOptions>;
constructor(options: DeckOptions = {}) {
super();
this.opts = {
populate: options.populate ?? true,
shuffle: options.shuffle ?? false
};
const { populate, shuffle } = this.opts;
if (populate) this._resetDeck(shuffle);
}
private _clearDeck(): this {
this._cardContainer = [];
return this;
}
private _resetDeck(shuffle = false): this {
this._clearDeck();
for (const suit in CardSuit) {
for (const value in CardValue) {
const [v, s] = [value as keyof typeof CardValue, suit as keyof typeof CardSuit];
this._cardContainer.push(new Card(v, s));
}
}
if (shuffle) this.shuffle();
return this;
}
public shuffle(): this {
if (this._isContainerEmpty()) return this;
CardCointainer._shuffleArray(this._cardContainer);
return this;
}
}
class Player extends CardCointainer {
public readonly name: string;
constructor(name: string, cards: Card[] = []) {
super();
this.name = name;
if (cards[0]) {
for (const card of cards) {
this.addCardEnd(card);
}
}
}
public giveCard(card: Card): boolean {
return this.addCardEnd(card);
}
public takeCard(cardIndex: number): Card | null;
public takeCard(card: Card): Card | null;
public takeCard(cardOrIndex: Card | number): Card | null {
if (this._isContainerEmpty()) return null;
if (typeof cardOrIndex === 'number') {
if (this._cardContainer[cardOrIndex]) {
return this._cardContainer.splice(cardOrIndex, 1)[0];
}
} else {
if (this._containerHasCard(cardOrIndex)) {
return this._cardContainer.splice(this._cardContainer.indexOf(cardOrIndex), 1)[0];
}
}
return null;
}
}
@Sans3108
Copy link
Author

I was bored

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment