Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"build-ts": "tsc && npm pack",
"test0": "mocha -r ts-node/register test/games/rincala.test.ts",
"test": "mocha -r ts-node/register test/**/*.test.ts",
"testc": "mocha -r ts-node/register test/common/*.test.ts",
"lint": "npx eslint .",
"dist-dev": "rimraf dist && webpack",
"dist-prod": "rimraf dist && webpack --mode=production",
Expand Down
43 changes: 35 additions & 8 deletions src/common/decktet/Card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ type Params = {
personality?: boolean;
event?: boolean;
location?: boolean;
deck?: number;
};

export const cardSortAsc = (a: Card, b: Card): number => {
Expand Down Expand Up @@ -51,6 +52,7 @@ export class Card {
private readonly _personality: boolean = false;
private readonly _event: boolean = false;
private readonly _location: boolean = false;
private readonly _deck: number = 0;
private _plain: string|undefined;

constructor(params: Params) {
Expand All @@ -66,6 +68,9 @@ export class Card {
if (params.location !== undefined) {
this._location = params.location;
}
if (params.deck !== undefined) {
this._deck = params.deck;
}
}

public get name(): string {
Expand All @@ -86,9 +91,12 @@ export class Card {
public get location(): boolean {
return this._location;
}
public get deck(): number {
return this._deck;
}

public get uid(): string {
return [this.rank.uid, ...this.suits.map(s => s.uid)].join("");
return [this.rank.uid, ...this.suits.map(s => s.uid), (this._deck > 0 ? this._deck : "")].join("");
}

public setPlain(plain: string|undefined): Card {
Expand Down Expand Up @@ -145,7 +153,8 @@ export class Card {
nudge: {
dx: 250,
dy: -250,
}
},
orientation: "vertical",
});
}
const nudges: [number,number][] = [[-250, -250], [-250, 250], [250, 250]];
Expand All @@ -158,31 +167,49 @@ export class Card {
nudge: {
dx: nudge[0],
dy: nudge[1],
}
},
orientation: "vertical",
});
}
return glyph;
}

public clone(): Card {
return new Card({name: this.name, rank: this.rank, suits: [...this.suits.map(s => s.clone())], personality: this.personality, location: this.location, event: this.event});
return new Card({name: this.name, rank: this.rank, suits: [...this.suits.map(s => s.clone())], personality: this.personality, location: this.location, event: this.event, deck: this.deck});
}

public cloneForDeck(deck: number): Card {
return new Card({name: this.name, rank: this.rank, suits: [...this.suits.map(s => s.clone())], personality: this.personality, location: this.location, event: this.event, deck: deck});
}

public static deserialize(card: Card|string, allowCustom = false): Card|undefined {
let strDeck: number = 0;
if (typeof card === "string") {
const found = [...cardsBasic, ...cardsExtended].find(c => c.uid === card.toUpperCase());
let found: Card|undefined;
if (card.length > 1 && card.charAt(card.length - 1).match(/\d/)) {
strDeck = parseInt(card.charAt(card.length - 1),10);
found = [...cardsBasic, ...cardsExtended].find(c => c.uid === card.toUpperCase().substring(0,card.length - 1));
if (found)
return new Card({name: found._name, rank: Component.deserialize(found._rank)!, suits: [...found._suits.map(s => Component.deserialize(s)!)], personality: found._personality, location: found._location, event: found.event, deck: strDeck});
} else {
found = [...cardsBasic, ...cardsExtended].find(c => c.uid === card.toUpperCase());
}
if (allowCustom && found === undefined) {
const [strRank, ...strSuits] = card.split("");
let [strRank, ...strSuits] = card.split("");
if (card.length > 1 && card.charAt(card.length - 1).match(/\d/)) {
strDeck = parseInt(card.charAt(card.length - 1),10);
[strRank, ...strSuits] = card.substring(0,card.length - 1).split("");
}
const rank = Component.deserialize(strRank);
const suits = strSuits.map(s => Component.deserialize(s));
if (rank === undefined || suits.includes(undefined)) {
return undefined;
}
return new Card({name: "_custom", rank, suits: (suits as Component[]).sort((a,b) => a.seq - b.seq)});
return new Card({name: "_custom", rank, suits: (suits as Component[]).sort((a,b) => a.seq - b.seq), deck: strDeck});
}
return found;
}
return new Card({name: card._name, rank: Component.deserialize(card._rank)!, suits: [...card._suits.map(s => Component.deserialize(s)!)], personality: card._personality, location: card._location, event: card._event});
return new Card({name: card._name, rank: Component.deserialize(card._rank)!, suits: [...card._suits.map(s => Component.deserialize(s)!)], personality: card._personality, location: card._location, event: card._event, deck: strDeck});
}
}

Expand Down
48 changes: 45 additions & 3 deletions src/common/decktet/Deck.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,23 @@ import { Card, cardsBasic, cardsExtended } from "./Card";

export class Deck {
private _cards: Card[];
private _decks: number | undefined;

constructor(cards: Card[]) {
this._cards = cards.map(c => new Card(c));
constructor(cards: Card[], decks?: number) {
if (decks === undefined) {
this._cards = cards.map(c => new Card(c));
} else if (decks < 1 || decks > 9) {
throw new Error("Only one to nine decktet decks are supported.");
} else {
const newCards: Card[] = [];
for (let d=1; d <= decks; d++) {
cards.forEach(c => {
newCards.push(c.cloneForDeck(d));
});
}
this._cards = newCards.map(c => new Card(c));
this._decks = decks;
}
}

public get cards(): Card[] {
Expand All @@ -31,6 +45,20 @@ export class Deck {
return this;
}

public addAll(uid: string): Deck {
if (this._decks === undefined) {
throw new Error("Use add() to add cards to a single deck.");
}
let card = uid;
if (card.length > 1 && card.charAt(card.length - 1).match(/\d/)) {
card = card.substring(0,card.length - 2);
}
for (let d=1; d <= this._decks; d++) {
this.add(card + d);
}
return this;
}

public remove(uid: string): Deck {
const idx = this._cards.findIndex(c => c.uid === uid);
if (idx < 0) {
Expand All @@ -40,6 +68,20 @@ export class Deck {
return this;
}

public removeAll(uid: string): Deck {
if (this._decks === undefined) {
throw new Error("Use remove() to remove cards from a single deck.");
}
let card = uid;
if (card.length > 1 && card.charAt(card.length - 1).match(/\d/)) {
card = card.substring(0,card.length - 2);
}
for (let d=1; d <= this._decks; d++) {
this.remove(card + d);
}
return this;
}

public draw(count = 1): Card[] {
const drawn: Card[] = [];
const limit = Math.min(count, this._cards.length);
Expand All @@ -61,4 +103,4 @@ export class Deck {
public static deserialize(deck: Deck): Deck {
return new Deck(deck.cards);
}
}
}
48 changes: 48 additions & 0 deletions test/common/decktet.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/* eslint-disable @typescript-eslint/no-unused-expressions */

import "mocha";
import { expect } from "chai";
//import { Card, Deck, cardSortAsc, cardSortDesc, cardsBasic, cardsExtended } from "../../src/common/decktet";
import { Deck, cardsBasic, cardsExtended } from "../../src/common/decktet";

describe("Decktets", () => {
it ("Still makes a single decktet", () => {
const mydeck = new Deck([...cardsBasic, ...cardsExtended]);
expect(mydeck.size).eq(45);
const card = mydeck.draw(1).map(c => c.uid)[0];
expect(card).eq("1M");
expect(mydeck.size).eq(44);

mydeck.shuffle();
expect(mydeck.size).eq(44);
expect(mydeck.remove("1K")).to.have.deep.property("size", 43);
});

it ("Now makes a double decktet", () => {
const mydeck = new Deck([...cardsBasic, ...cardsExtended],2);
expect(mydeck.size).eq(90);
expect(mydeck.draw(1).map(c => c.uid)[0]).eq("1M1");
expect(mydeck.size).eq(89);
expect(mydeck.draw(1).map(c => c.plain)[0]).eq("Ace Suns");
expect(mydeck.size).eq(88);

mydeck.shuffle();
expect(mydeck.size).eq(88);
expect(mydeck.remove("1K1")).to.have.deep.property("size", 87);
expect(mydeck.remove("1K2")).to.have.deep.property("size", 86);
expect(mydeck.removeAll("1Y")).to.have.deep.property("size", 84);

});

it ("Doesn't blow up when the deck is out", () => {
const mydeck = new Deck([...cardsBasic, ...cardsExtended],3);
expect(mydeck.size).eq(135);
mydeck.draw(134);
expect(mydeck.size).eq(1);
expect(mydeck.draw(1).map(c => c.plain)[0]).eq("Court Suns Waves Wyrms");
expect(mydeck.size).eq(0);
const [card] = mydeck.draw(1);
expect(card).eq(undefined);

});
});