Created
April 14, 2024 15:48
-
-
Save Sans3108/13b44a836177d54a49271bbb22dcafc2 to your computer and use it in GitHub Desktop.
Cards
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I was bored