@arcanahq/cardgames 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +722 -0
- package/as-test.config.js +36 -0
- package/asconfig.json +22 -0
- package/assembly/__tests__/blackjack/actions/common.spec.ts +180 -0
- package/assembly/__tests__/blackjack/actions/dealer_scenarios.spec.ts +452 -0
- package/assembly/__tests__/blackjack/actions/double.spec.ts +128 -0
- package/assembly/__tests__/blackjack/actions/edge_cases.spec.ts +1041 -0
- package/assembly/__tests__/blackjack/actions/insurance.spec.ts +39 -0
- package/assembly/__tests__/blackjack/actions/split.spec.ts +96 -0
- package/assembly/__tests__/blackjack/actions/stand.spec.ts +103 -0
- package/assembly/__tests__/blackjack/actions/surrender.spec.ts +89 -0
- package/assembly/__tests__/blackjack/actions/test.ts +18 -0
- package/assembly/__tests__/blackjack/rules.spec.ts +231 -0
- package/assembly/__tests__/deck/deck.spec.ts +551 -0
- package/assembly/__tests__/deck/shoe.spec.ts +410 -0
- package/assembly/__tests__/poker/betting_round.spec.ts +103 -0
- package/assembly/__tests__/poker/omaha.spec.ts +171 -0
- package/assembly/__tests__/poker/pots.spec.ts +255 -0
- package/assembly/__tests__/poker/showdown.spec.ts +324 -0
- package/assembly/__tests__/poker/six_plus.spec.ts +152 -0
- package/assembly/__tests__/poker/stakes.spec.ts +384 -0
- package/assembly/__tests__/poker/stud.spec.ts +190 -0
- package/assembly/__tests__/poker/test.ts +13 -0
- package/assembly/__tests__/test.ts +11 -0
- package/assembly/blackjack/actions.ts +191 -0
- package/assembly/blackjack/blackjack.ts +571 -0
- package/assembly/blackjack/rules.ts +11 -0
- package/assembly/cardgames.ts +314 -0
- package/assembly/cards.ts +314 -0
- package/assembly/cashgames/cash_game_types.ts +142 -0
- package/assembly/cashgames/cash_game_utils.ts +223 -0
- package/assembly/cashgames/index.ts +10 -0
- package/assembly/deck/deck.ts +744 -0
- package/assembly/deck/index.ts +9 -0
- package/assembly/index.ts +28 -0
- package/assembly/poker/index.ts +17 -0
- package/assembly/poker/omaha_evaluator.ts +121 -0
- package/assembly/poker/poker_game_types.ts +233 -0
- package/assembly/poker/poker_game_utils.ts +671 -0
- package/assembly/poker/showdown.ts +106 -0
- package/assembly/poker/showdown_evaluator.ts +225 -0
- package/assembly/poker/six_plus_showdown.ts +96 -0
- package/assembly/poker/stud_evaluator.ts +60 -0
- package/assembly/poker/variant_utils.ts +51 -0
- package/assembly/poker/variants.ts +182 -0
- package/assembly/poker.ts +307 -0
- package/package.json +51 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,551 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* Comprehensive tests for deck management utilities
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, test, expect } from "assemblyscript-unittest-framework/assembly";
|
|
7
|
+
import {
|
|
8
|
+
DeckConfig,
|
|
9
|
+
StandardDeckConfig,
|
|
10
|
+
Spanish21DeckConfig,
|
|
11
|
+
ShoeConfig,
|
|
12
|
+
CardIndexMapper,
|
|
13
|
+
deterministicShuffleIndices,
|
|
14
|
+
dealCardByIndex,
|
|
15
|
+
dealCardFromShoe,
|
|
16
|
+
createShuffledDeck,
|
|
17
|
+
createUnshuffledDeck,
|
|
18
|
+
getShuffledIndices,
|
|
19
|
+
dealCards,
|
|
20
|
+
getShuffledShoeIndices,
|
|
21
|
+
dealCardsFromShoe,
|
|
22
|
+
} from "../../deck/deck";
|
|
23
|
+
import { Card, Suit, Rank } from "../../cards";
|
|
24
|
+
|
|
25
|
+
// ============================================================================
|
|
26
|
+
// DeckConfig Tests
|
|
27
|
+
// ============================================================================
|
|
28
|
+
|
|
29
|
+
describe("DeckConfig", () => {
|
|
30
|
+
test("should create standard deck config using static method", () => {
|
|
31
|
+
const config: StandardDeckConfig = DeckConfig.standard();
|
|
32
|
+
expect(config.totalCards).equal(52);
|
|
33
|
+
expect(config.deckSize).equal(52); // Backward compatibility
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("should create Spanish 21 deck config using static method", () => {
|
|
37
|
+
const config: Spanish21DeckConfig = DeckConfig.spanish21();
|
|
38
|
+
expect(config.totalCards).equal(48);
|
|
39
|
+
expect(config.deckSize).equal(48); // Backward compatibility
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("should convert index to card using fromIndex", () => {
|
|
43
|
+
const config: StandardDeckConfig = DeckConfig.standard();
|
|
44
|
+
const card = config.fromIndex(0);
|
|
45
|
+
if (card !== null) {
|
|
46
|
+
expect(card.rank).equal(Rank.TWO);
|
|
47
|
+
expect(card.suit).equal(Suit.SPADES);
|
|
48
|
+
} else {
|
|
49
|
+
expect(false).equal(true); // Should not be null
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("should convert card to index using toIndex", () => {
|
|
54
|
+
const config: StandardDeckConfig = DeckConfig.standard();
|
|
55
|
+
const card = new Card(Suit.SPADES, Rank.TWO);
|
|
56
|
+
const index: i32 = config.toIndex(card);
|
|
57
|
+
expect(index).equal(0);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("should handle Spanish 21 fromIndex", () => {
|
|
61
|
+
const config: Spanish21DeckConfig = DeckConfig.spanish21();
|
|
62
|
+
const card = config.fromIndex(0);
|
|
63
|
+
if (card !== null) {
|
|
64
|
+
// Should not be a 10
|
|
65
|
+
expect(card.rank == Rank.TEN).equal(false);
|
|
66
|
+
} else {
|
|
67
|
+
expect(false).equal(true); // Should not be null
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("should handle Spanish 21 toIndex - returns -1 for 10s", () => {
|
|
72
|
+
const config: Spanish21DeckConfig = DeckConfig.spanish21();
|
|
73
|
+
const tenCard = new Card(Suit.SPADES, Rank.TEN);
|
|
74
|
+
const index: i32 = config.toIndex(tenCard);
|
|
75
|
+
expect(index).equal(-1); // 10s not in Spanish 21
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("StandardDeckConfig should be instance of DeckConfig", () => {
|
|
79
|
+
const config = new StandardDeckConfig();
|
|
80
|
+
expect(config.totalCards).equal(52);
|
|
81
|
+
const card = config.fromIndex(0);
|
|
82
|
+
if (card !== null) {
|
|
83
|
+
expect(card.rank).equal(Rank.TWO);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("Spanish21DeckConfig should be instance of DeckConfig", () => {
|
|
88
|
+
const config = new Spanish21DeckConfig();
|
|
89
|
+
expect(config.totalCards).equal(48);
|
|
90
|
+
const card = config.fromIndex(0);
|
|
91
|
+
if (card !== null) {
|
|
92
|
+
expect(card.rank == Rank.TEN).equal(false);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// ============================================================================
|
|
98
|
+
// CardIndexMapper Tests
|
|
99
|
+
// ============================================================================
|
|
100
|
+
|
|
101
|
+
describe("CardIndexMapper", () => {
|
|
102
|
+
test("should convert index to card for standard deck", () => {
|
|
103
|
+
const standardConfig = DeckConfig.standard();
|
|
104
|
+
// Index 0 = 2 of Spades
|
|
105
|
+
const card = CardIndexMapper.indexToCard(0, standardConfig);
|
|
106
|
+
expect(card !== null).equal(true);
|
|
107
|
+
if (card !== null) {
|
|
108
|
+
expect(card.suit).equal(Suit.SPADES);
|
|
109
|
+
expect(card.rank).equal(Rank.TWO);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Index 12 = Ace of Spades
|
|
113
|
+
const card2 = CardIndexMapper.indexToCard(12, standardConfig);
|
|
114
|
+
expect(card2 !== null).equal(true);
|
|
115
|
+
if (card2 !== null) {
|
|
116
|
+
expect(card2.suit).equal(Suit.SPADES);
|
|
117
|
+
expect(card2.rank).equal(Rank.ACE);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Index 13 = 2 of Hearts
|
|
121
|
+
const card3 = CardIndexMapper.indexToCard(13, standardConfig);
|
|
122
|
+
expect(card3 !== null).equal(true);
|
|
123
|
+
if (card3 !== null) {
|
|
124
|
+
expect(card3.suit).equal(Suit.HEARTS);
|
|
125
|
+
expect(card3.rank).equal(Rank.TWO);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Index 51 = Ace of Clubs
|
|
129
|
+
const card4 = CardIndexMapper.indexToCard(51, standardConfig);
|
|
130
|
+
expect(card4 !== null).equal(true);
|
|
131
|
+
if (card4 !== null) {
|
|
132
|
+
expect(card4.suit).equal(Suit.CLUBS);
|
|
133
|
+
expect(card4.rank).equal(Rank.ACE);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test("should convert card to index for standard deck", () => {
|
|
138
|
+
const standardConfig = DeckConfig.standard();
|
|
139
|
+
const card = new Card(Suit.SPADES, Rank.TWO);
|
|
140
|
+
const index = CardIndexMapper.cardToIndex(card, standardConfig);
|
|
141
|
+
expect(index).equal(0);
|
|
142
|
+
|
|
143
|
+
const card2 = new Card(Suit.SPADES, Rank.ACE);
|
|
144
|
+
const index2 = CardIndexMapper.cardToIndex(card2, standardConfig);
|
|
145
|
+
expect(index2).equal(12);
|
|
146
|
+
|
|
147
|
+
const card3 = new Card(Suit.HEARTS, Rank.TWO);
|
|
148
|
+
const index3 = CardIndexMapper.cardToIndex(card3, standardConfig);
|
|
149
|
+
expect(index3).equal(13);
|
|
150
|
+
|
|
151
|
+
const card4 = new Card(Suit.CLUBS, Rank.ACE);
|
|
152
|
+
const index4 = CardIndexMapper.cardToIndex(card4, standardConfig);
|
|
153
|
+
expect(index4).equal(51);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test("should convert index to card for Spanish 21 deck", () => {
|
|
157
|
+
const spanishConfig = DeckConfig.spanish21();
|
|
158
|
+
// Index 0 = 2 of Spades (same as standard)
|
|
159
|
+
const card = CardIndexMapper.indexToCard(0, spanishConfig);
|
|
160
|
+
expect(card !== null).equal(true);
|
|
161
|
+
if (card !== null) {
|
|
162
|
+
expect(card.suit).equal(Suit.SPADES);
|
|
163
|
+
expect(card.rank).equal(Rank.TWO);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Index 11 = Ace of Spades (no 10, so Ace is at 11 instead of 12)
|
|
167
|
+
const card2 = CardIndexMapper.indexToCard(11, spanishConfig);
|
|
168
|
+
expect(card2 !== null).equal(true);
|
|
169
|
+
if (card2 !== null) {
|
|
170
|
+
expect(card2.suit).equal(Suit.SPADES);
|
|
171
|
+
expect(card2.rank).equal(Rank.ACE);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Index 47 = Ace of Clubs (last card)
|
|
175
|
+
const card3 = CardIndexMapper.indexToCard(47, spanishConfig);
|
|
176
|
+
expect(card3 !== null).equal(true);
|
|
177
|
+
if (card3 !== null) {
|
|
178
|
+
expect(card3.suit).equal(Suit.CLUBS);
|
|
179
|
+
expect(card3.rank).equal(Rank.ACE);
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test("should convert card to index for Spanish 21 deck", () => {
|
|
184
|
+
const spanishConfig = DeckConfig.spanish21();
|
|
185
|
+
const card = new Card(Suit.SPADES, Rank.TWO);
|
|
186
|
+
const index = CardIndexMapper.cardToIndex(card, spanishConfig);
|
|
187
|
+
expect(index).equal(0);
|
|
188
|
+
|
|
189
|
+
const card2 = new Card(Suit.SPADES, Rank.ACE);
|
|
190
|
+
const index2 = CardIndexMapper.cardToIndex(card2, spanishConfig);
|
|
191
|
+
expect(index2).equal(11); // No 10, so Ace is at 11
|
|
192
|
+
|
|
193
|
+
// 10 should not be in Spanish 21 deck
|
|
194
|
+
const card3 = new Card(Suit.SPADES, Rank.TEN);
|
|
195
|
+
const index3 = CardIndexMapper.cardToIndex(card3, spanishConfig);
|
|
196
|
+
expect(index3).equal(-1); // Invalid
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test("should handle invalid indices", () => {
|
|
200
|
+
const standardConfig = DeckConfig.standard();
|
|
201
|
+
const spanishConfig = DeckConfig.spanish21();
|
|
202
|
+
const card = CardIndexMapper.indexToCard(-1, standardConfig);
|
|
203
|
+
expect(card === null).equal(true);
|
|
204
|
+
|
|
205
|
+
const card2 = CardIndexMapper.indexToCard(52, standardConfig);
|
|
206
|
+
expect(card2 === null).equal(true);
|
|
207
|
+
|
|
208
|
+
const card3 = CardIndexMapper.indexToCard(48, spanishConfig);
|
|
209
|
+
expect(card3 === null).equal(true);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
test("should create unshuffled deck indices for standard deck", () => {
|
|
213
|
+
const standardConfig = DeckConfig.standard();
|
|
214
|
+
const indices = CardIndexMapper.createUnshuffledDeckIndices(standardConfig);
|
|
215
|
+
expect(indices.length).equal(52);
|
|
216
|
+
expect(indices[0]).equal(0);
|
|
217
|
+
expect(indices[12]).equal(12);
|
|
218
|
+
expect(indices[51]).equal(51);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
test("should create unshuffled deck indices for Spanish 21 deck", () => {
|
|
222
|
+
const spanishConfig = DeckConfig.spanish21();
|
|
223
|
+
const indices = CardIndexMapper.createUnshuffledDeckIndices(spanishConfig);
|
|
224
|
+
expect(indices.length).equal(48);
|
|
225
|
+
expect(indices[0]).equal(0);
|
|
226
|
+
expect(indices[11]).equal(11); // Ace of Spades
|
|
227
|
+
expect(indices[47]).equal(47); // Ace of Clubs
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
test("should round-trip convert card to index and back", () => {
|
|
231
|
+
const standardConfig = DeckConfig.standard();
|
|
232
|
+
const originalCard = new Card(Suit.HEARTS, Rank.KING);
|
|
233
|
+
const index = CardIndexMapper.cardToIndex(originalCard, standardConfig);
|
|
234
|
+
expect(index).greaterThanOrEqual(0);
|
|
235
|
+
|
|
236
|
+
const convertedCard = CardIndexMapper.indexToCard(index, standardConfig);
|
|
237
|
+
expect(convertedCard !== null).equal(true);
|
|
238
|
+
if (convertedCard !== null) {
|
|
239
|
+
expect(convertedCard.suit).equal(originalCard.suit);
|
|
240
|
+
expect(convertedCard.rank).equal(originalCard.rank);
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
// ============================================================================
|
|
246
|
+
// deterministicShuffleIndices Tests
|
|
247
|
+
// ============================================================================
|
|
248
|
+
|
|
249
|
+
describe("deterministicShuffleIndices", () => {
|
|
250
|
+
test("should shuffle indices deterministically", () => {
|
|
251
|
+
const config = DeckConfig.standard();
|
|
252
|
+
const indices = CardIndexMapper.createUnshuffledDeckIndices(config);
|
|
253
|
+
|
|
254
|
+
const shuffled1 = deterministicShuffleIndices(indices, "test-id", "test-salt", 0);
|
|
255
|
+
const shuffled2 = deterministicShuffleIndices(indices, "test-id", "test-salt", 0);
|
|
256
|
+
|
|
257
|
+
// Same seed should produce same shuffle
|
|
258
|
+
expect(shuffled1.length).equal(shuffled2.length);
|
|
259
|
+
for (let i = 0; i < shuffled1.length; i++) {
|
|
260
|
+
expect(shuffled1[i]).equal(shuffled2[i]);
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
test("should produce different shuffles with different seeds", () => {
|
|
265
|
+
const config = DeckConfig.standard();
|
|
266
|
+
const indices = CardIndexMapper.createUnshuffledDeckIndices(config);
|
|
267
|
+
|
|
268
|
+
const shuffled1 = deterministicShuffleIndices(indices, "test-id-1", "test-salt", 0);
|
|
269
|
+
const shuffled2 = deterministicShuffleIndices(indices, "test-id-2", "test-salt", 0);
|
|
270
|
+
|
|
271
|
+
// Different seeds should produce different shuffles
|
|
272
|
+
let different = false;
|
|
273
|
+
for (let i = 0; i < shuffled1.length; i++) {
|
|
274
|
+
if (shuffled1[i] != shuffled2[i]) {
|
|
275
|
+
different = true;
|
|
276
|
+
break;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
expect(different).equal(true);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
test("should shuffle Spanish 21 deck", () => {
|
|
283
|
+
const config = DeckConfig.spanish21();
|
|
284
|
+
const indices = CardIndexMapper.createUnshuffledDeckIndices(config);
|
|
285
|
+
|
|
286
|
+
const shuffled = deterministicShuffleIndices(indices, "test-id", "test-salt", 0);
|
|
287
|
+
expect(shuffled.length).equal(48);
|
|
288
|
+
|
|
289
|
+
// Verify all indices are present (0-47)
|
|
290
|
+
const present = new Array<bool>(48);
|
|
291
|
+
for (let i = 0; i < 48; i++) {
|
|
292
|
+
present[i] = false;
|
|
293
|
+
}
|
|
294
|
+
for (let i = 0; i < shuffled.length; i++) {
|
|
295
|
+
if (shuffled[i] >= 0 && shuffled[i] < 48) {
|
|
296
|
+
present[shuffled[i]] = true;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
for (let i = 0; i < 48; i++) {
|
|
300
|
+
expect(present[i]).equal(true);
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
// ============================================================================
|
|
306
|
+
// dealCardByIndex Tests
|
|
307
|
+
// ============================================================================
|
|
308
|
+
|
|
309
|
+
describe("dealCardByIndex", () => {
|
|
310
|
+
test("should deal cards deterministically from standard deck", () => {
|
|
311
|
+
const config = DeckConfig.standard();
|
|
312
|
+
|
|
313
|
+
const card1 = dealCardByIndex("test-id", "test-salt", 0, config);
|
|
314
|
+
const card2 = dealCardByIndex("test-id", "test-salt", 1, config);
|
|
315
|
+
const card3 = dealCardByIndex("test-id", "test-salt", 0, config); // Same as card1
|
|
316
|
+
|
|
317
|
+
// Same index should produce same card
|
|
318
|
+
expect(card1.suit).equal(card3.suit);
|
|
319
|
+
expect(card1.rank).equal(card3.rank);
|
|
320
|
+
|
|
321
|
+
// Different indices should produce different cards (usually)
|
|
322
|
+
// Note: There's a small chance they could be the same, but very unlikely
|
|
323
|
+
const different = card1.suit != card2.suit || card1.rank != card2.rank;
|
|
324
|
+
expect(different).equal(true);
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
test("should deal cards deterministically from Spanish 21 deck", () => {
|
|
328
|
+
const config = DeckConfig.spanish21();
|
|
329
|
+
|
|
330
|
+
const card1 = dealCardByIndex("test-id", "test-salt", 0, config);
|
|
331
|
+
const card2 = dealCardByIndex("test-id", "test-salt", 1, config);
|
|
332
|
+
|
|
333
|
+
// Cards should be valid
|
|
334
|
+
expect(card1 !== null).equal(true);
|
|
335
|
+
expect(card2 !== null).equal(true);
|
|
336
|
+
|
|
337
|
+
// Spanish 21 should never deal a 10
|
|
338
|
+
expect(card1.rank != Rank.TEN).equal(true);
|
|
339
|
+
expect(card2.rank != Rank.TEN).equal(true);
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
test("should handle deck reshuffling", () => {
|
|
343
|
+
const config = DeckConfig.standard();
|
|
344
|
+
|
|
345
|
+
// Deal card 52 (first card of second deck, using seedIndex=1)
|
|
346
|
+
const card52 = dealCardByIndex("test-id", "test-salt", 52, config);
|
|
347
|
+
// Deal card 0 (first card of first deck, using seedIndex=0)
|
|
348
|
+
const card0 = dealCardByIndex("test-id", "test-salt", 0, config);
|
|
349
|
+
|
|
350
|
+
// Verify cards are valid
|
|
351
|
+
expect(card52 !== null).equal(true);
|
|
352
|
+
expect(card0 !== null).equal(true);
|
|
353
|
+
|
|
354
|
+
// Verify reshuffling works (cards should be deterministically different)
|
|
355
|
+
// Note: There's a small chance they could be the same, but the shuffle
|
|
356
|
+
// should produce a different deck order
|
|
357
|
+
const sameCard = card52.suit == card0.suit && card52.rank == card0.rank;
|
|
358
|
+
// This is unlikely but possible, so we just verify the function works
|
|
359
|
+
expect(true).equal(true);
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
test("should produce same cards with same shuffle parameters", () => {
|
|
363
|
+
const config = DeckConfig.standard();
|
|
364
|
+
|
|
365
|
+
const card1 = dealCardByIndex("test-id", "test-salt", 5, config);
|
|
366
|
+
const card2 = dealCardByIndex("test-id", "test-salt", 5, config);
|
|
367
|
+
|
|
368
|
+
expect(card1.suit).equal(card2.suit);
|
|
369
|
+
expect(card1.rank).equal(card2.rank);
|
|
370
|
+
});
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
// ============================================================================
|
|
374
|
+
// createShuffledDeck Tests
|
|
375
|
+
// ============================================================================
|
|
376
|
+
|
|
377
|
+
describe("createShuffledDeck", () => {
|
|
378
|
+
test("should create full shuffled standard deck", () => {
|
|
379
|
+
const config = DeckConfig.standard();
|
|
380
|
+
const deck = createShuffledDeck("test-id", "test-salt", 0, config);
|
|
381
|
+
|
|
382
|
+
expect(deck.length).equal(52);
|
|
383
|
+
|
|
384
|
+
// Verify all cards are valid
|
|
385
|
+
for (let i = 0; i < deck.length; i++) {
|
|
386
|
+
expect(deck[i] !== null).equal(true);
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
test("should create full shuffled Spanish 21 deck", () => {
|
|
391
|
+
const config = DeckConfig.spanish21();
|
|
392
|
+
const deck = createShuffledDeck("test-id", "test-salt", 0, config);
|
|
393
|
+
|
|
394
|
+
expect(deck.length).equal(48);
|
|
395
|
+
|
|
396
|
+
// Verify no 10s
|
|
397
|
+
for (let i = 0; i < deck.length; i++) {
|
|
398
|
+
expect(deck[i].rank != Rank.TEN).equal(true);
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
test("should produce same deck with same parameters", () => {
|
|
403
|
+
const config = DeckConfig.standard();
|
|
404
|
+
const deck1 = createShuffledDeck("test-id", "test-salt", 0, config);
|
|
405
|
+
const deck2 = createShuffledDeck("test-id", "test-salt", 0, config);
|
|
406
|
+
|
|
407
|
+
expect(deck1.length).equal(deck2.length);
|
|
408
|
+
for (let i = 0; i < deck1.length; i++) {
|
|
409
|
+
expect(deck1[i].suit).equal(deck2[i].suit);
|
|
410
|
+
expect(deck1[i].rank).equal(deck2[i].rank);
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
// ============================================================================
|
|
416
|
+
// getShuffledIndices / dealCards (efficient batch) Tests
|
|
417
|
+
// ============================================================================
|
|
418
|
+
|
|
419
|
+
describe("getShuffledIndices and dealCards", () => {
|
|
420
|
+
test("getShuffledIndices returns 52 indices for standard deck", () => {
|
|
421
|
+
const config = DeckConfig.standard();
|
|
422
|
+
const indices = getShuffledIndices("test-id", "test-salt", 0, config);
|
|
423
|
+
expect(indices.length).equal(52);
|
|
424
|
+
const card0 = config.fromIndex(indices[0]);
|
|
425
|
+
expect(card0 !== null).equal(true);
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
test("dealCards returns same cards as dealCardByIndex for first 4", () => {
|
|
429
|
+
const config = DeckConfig.standard();
|
|
430
|
+
const batch = dealCards("test-id", "test-salt", 0, 4, config);
|
|
431
|
+
expect(batch.length).equal(4);
|
|
432
|
+
const c0 = dealCardByIndex("test-id", "test-salt", 0, config);
|
|
433
|
+
const c1 = dealCardByIndex("test-id", "test-salt", 1, config);
|
|
434
|
+
const c2 = dealCardByIndex("test-id", "test-salt", 2, config);
|
|
435
|
+
const c3 = dealCardByIndex("test-id", "test-salt", 3, config);
|
|
436
|
+
expect(batch[0].suit).equal(c0.suit);
|
|
437
|
+
expect(batch[0].rank).equal(c0.rank);
|
|
438
|
+
expect(batch[1].suit).equal(c1.suit);
|
|
439
|
+
expect(batch[1].rank).equal(c1.rank);
|
|
440
|
+
expect(batch[2].suit).equal(c2.suit);
|
|
441
|
+
expect(batch[2].rank).equal(c2.rank);
|
|
442
|
+
expect(batch[3].suit).equal(c3.suit);
|
|
443
|
+
expect(batch[3].rank).equal(c3.rank);
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
test("dealCards crossing deck boundary uses two blocks", () => {
|
|
447
|
+
const config = DeckConfig.standard();
|
|
448
|
+
const batch = dealCards("test-id", "test-salt", 50, 4, config);
|
|
449
|
+
expect(batch.length).equal(4);
|
|
450
|
+
const c50 = dealCardByIndex("test-id", "test-salt", 50, config);
|
|
451
|
+
const c51 = dealCardByIndex("test-id", "test-salt", 51, config);
|
|
452
|
+
const c52 = dealCardByIndex("test-id", "test-salt", 52, config);
|
|
453
|
+
const c53 = dealCardByIndex("test-id", "test-salt", 53, config);
|
|
454
|
+
expect(batch[0].suit).equal(c50.suit);
|
|
455
|
+
expect(batch[0].rank).equal(c50.rank);
|
|
456
|
+
expect(batch[1].suit).equal(c51.suit);
|
|
457
|
+
expect(batch[1].rank).equal(c51.rank);
|
|
458
|
+
expect(batch[2].suit).equal(c52.suit);
|
|
459
|
+
expect(batch[2].rank).equal(c52.rank);
|
|
460
|
+
expect(batch[3].suit).equal(c53.suit);
|
|
461
|
+
expect(batch[3].rank).equal(c53.rank);
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
test("dealCards returns empty for count 0", () => {
|
|
465
|
+
const config = DeckConfig.standard();
|
|
466
|
+
const batch = dealCards("test-id", "test-salt", 0, 0, config);
|
|
467
|
+
expect(batch.length).equal(0);
|
|
468
|
+
});
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
// ============================================================================
|
|
472
|
+
// getShuffledShoeIndices / dealCardsFromShoe Tests
|
|
473
|
+
// ============================================================================
|
|
474
|
+
|
|
475
|
+
describe("getShuffledShoeIndices and dealCardsFromShoe", () => {
|
|
476
|
+
test("getShuffledShoeIndices returns shoeSize indices for single deck", () => {
|
|
477
|
+
const shoeConfig = ShoeConfig.standard(1);
|
|
478
|
+
const indices = getShuffledShoeIndices("test-id", "test-salt", 0, shoeConfig);
|
|
479
|
+
expect(indices.length).equal(52);
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
test("dealCardsFromShoe returns same cards as dealCardFromShoe for first 4", () => {
|
|
483
|
+
const shoeConfig = ShoeConfig.standard(1);
|
|
484
|
+
const batch = dealCardsFromShoe("test-id", "test-salt", 0, 4, shoeConfig);
|
|
485
|
+
expect(batch.length).equal(4);
|
|
486
|
+
const c0 = dealCardFromShoe("test-id", "test-salt", 0, shoeConfig);
|
|
487
|
+
const c1 = dealCardFromShoe("test-id", "test-salt", 1, shoeConfig);
|
|
488
|
+
const c2 = dealCardFromShoe("test-id", "test-salt", 2, shoeConfig);
|
|
489
|
+
const c3 = dealCardFromShoe("test-id", "test-salt", 3, shoeConfig);
|
|
490
|
+
expect(batch[0].suit).equal(c0.suit);
|
|
491
|
+
expect(batch[0].rank).equal(c0.rank);
|
|
492
|
+
expect(batch[1].suit).equal(c1.suit);
|
|
493
|
+
expect(batch[1].rank).equal(c1.rank);
|
|
494
|
+
expect(batch[2].suit).equal(c2.suit);
|
|
495
|
+
expect(batch[2].rank).equal(c2.rank);
|
|
496
|
+
expect(batch[3].suit).equal(c3.suit);
|
|
497
|
+
expect(batch[3].rank).equal(c3.rank);
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
test("dealCardsFromShoe returns empty for count 0", () => {
|
|
501
|
+
const shoeConfig = ShoeConfig.standard(1);
|
|
502
|
+
const batch = dealCardsFromShoe("test-id", "test-salt", 0, 0, shoeConfig);
|
|
503
|
+
expect(batch.length).equal(0);
|
|
504
|
+
});
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
// ============================================================================
|
|
508
|
+
// createUnshuffledDeck Tests
|
|
509
|
+
// ============================================================================
|
|
510
|
+
|
|
511
|
+
describe("createUnshuffledDeck", () => {
|
|
512
|
+
test("should create unshuffled standard deck", () => {
|
|
513
|
+
const config = DeckConfig.standard();
|
|
514
|
+
const deck = createUnshuffledDeck(config);
|
|
515
|
+
|
|
516
|
+
expect(deck.length).equal(52);
|
|
517
|
+
|
|
518
|
+
// First card should be 2 of Spades
|
|
519
|
+
expect(deck[0].suit).equal(Suit.SPADES);
|
|
520
|
+
expect(deck[0].rank).equal(Rank.TWO);
|
|
521
|
+
|
|
522
|
+
// 13th card should be Ace of Spades
|
|
523
|
+
expect(deck[12].suit).equal(Suit.SPADES);
|
|
524
|
+
expect(deck[12].rank).equal(Rank.ACE);
|
|
525
|
+
|
|
526
|
+
// 14th card should be 2 of Hearts
|
|
527
|
+
expect(deck[13].suit).equal(Suit.HEARTS);
|
|
528
|
+
expect(deck[13].rank).equal(Rank.TWO);
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
test("should create unshuffled Spanish 21 deck", () => {
|
|
532
|
+
const config = DeckConfig.spanish21();
|
|
533
|
+
const deck = createUnshuffledDeck(config);
|
|
534
|
+
|
|
535
|
+
expect(deck.length).equal(48);
|
|
536
|
+
|
|
537
|
+
// First card should be 2 of Spades
|
|
538
|
+
expect(deck[0].suit).equal(Suit.SPADES);
|
|
539
|
+
expect(deck[0].rank).equal(Rank.TWO);
|
|
540
|
+
|
|
541
|
+
// 12th card should be Ace of Spades (no 10)
|
|
542
|
+
expect(deck[11].suit).equal(Suit.SPADES);
|
|
543
|
+
expect(deck[11].rank).equal(Rank.ACE);
|
|
544
|
+
|
|
545
|
+
// Verify no 10s
|
|
546
|
+
for (let i = 0; i < deck.length; i++) {
|
|
547
|
+
expect(deck[i].rank != Rank.TEN).equal(true);
|
|
548
|
+
}
|
|
549
|
+
});
|
|
550
|
+
});
|
|
551
|
+
|