@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.
Files changed (48) hide show
  1. package/README.md +722 -0
  2. package/as-test.config.js +36 -0
  3. package/asconfig.json +22 -0
  4. package/assembly/__tests__/blackjack/actions/common.spec.ts +180 -0
  5. package/assembly/__tests__/blackjack/actions/dealer_scenarios.spec.ts +452 -0
  6. package/assembly/__tests__/blackjack/actions/double.spec.ts +128 -0
  7. package/assembly/__tests__/blackjack/actions/edge_cases.spec.ts +1041 -0
  8. package/assembly/__tests__/blackjack/actions/insurance.spec.ts +39 -0
  9. package/assembly/__tests__/blackjack/actions/split.spec.ts +96 -0
  10. package/assembly/__tests__/blackjack/actions/stand.spec.ts +103 -0
  11. package/assembly/__tests__/blackjack/actions/surrender.spec.ts +89 -0
  12. package/assembly/__tests__/blackjack/actions/test.ts +18 -0
  13. package/assembly/__tests__/blackjack/rules.spec.ts +231 -0
  14. package/assembly/__tests__/deck/deck.spec.ts +551 -0
  15. package/assembly/__tests__/deck/shoe.spec.ts +410 -0
  16. package/assembly/__tests__/poker/betting_round.spec.ts +103 -0
  17. package/assembly/__tests__/poker/omaha.spec.ts +171 -0
  18. package/assembly/__tests__/poker/pots.spec.ts +255 -0
  19. package/assembly/__tests__/poker/showdown.spec.ts +324 -0
  20. package/assembly/__tests__/poker/six_plus.spec.ts +152 -0
  21. package/assembly/__tests__/poker/stakes.spec.ts +384 -0
  22. package/assembly/__tests__/poker/stud.spec.ts +190 -0
  23. package/assembly/__tests__/poker/test.ts +13 -0
  24. package/assembly/__tests__/test.ts +11 -0
  25. package/assembly/blackjack/actions.ts +191 -0
  26. package/assembly/blackjack/blackjack.ts +571 -0
  27. package/assembly/blackjack/rules.ts +11 -0
  28. package/assembly/cardgames.ts +314 -0
  29. package/assembly/cards.ts +314 -0
  30. package/assembly/cashgames/cash_game_types.ts +142 -0
  31. package/assembly/cashgames/cash_game_utils.ts +223 -0
  32. package/assembly/cashgames/index.ts +10 -0
  33. package/assembly/deck/deck.ts +744 -0
  34. package/assembly/deck/index.ts +9 -0
  35. package/assembly/index.ts +28 -0
  36. package/assembly/poker/index.ts +17 -0
  37. package/assembly/poker/omaha_evaluator.ts +121 -0
  38. package/assembly/poker/poker_game_types.ts +233 -0
  39. package/assembly/poker/poker_game_utils.ts +671 -0
  40. package/assembly/poker/showdown.ts +106 -0
  41. package/assembly/poker/showdown_evaluator.ts +225 -0
  42. package/assembly/poker/six_plus_showdown.ts +96 -0
  43. package/assembly/poker/stud_evaluator.ts +60 -0
  44. package/assembly/poker/variant_utils.ts +51 -0
  45. package/assembly/poker/variants.ts +182 -0
  46. package/assembly/poker.ts +307 -0
  47. package/package.json +51 -0
  48. 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
+