@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,36 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { createAsTestImports } from "../core/tests/as-test-imports.js";
|
|
5
|
+
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const FALLBACK_HEAP_BASE = 64 * 1024;
|
|
8
|
+
const HEAP_REGEX = /\(global \$~lib\/memory\/__heap_base i32 \(i32\.const (\d+)\)\)/;
|
|
9
|
+
|
|
10
|
+
function resolveHeapBase() {
|
|
11
|
+
const watPath = join(__dirname, ".as-test/output/test.wat");
|
|
12
|
+
if (existsSync(watPath)) {
|
|
13
|
+
try {
|
|
14
|
+
const contents = readFileSync(watPath, "utf8");
|
|
15
|
+
const match = contents.match(HEAP_REGEX);
|
|
16
|
+
if (match) {
|
|
17
|
+
return Number(match[1]);
|
|
18
|
+
}
|
|
19
|
+
} catch {
|
|
20
|
+
// fall through to fallback
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return FALLBACK_HEAP_BASE;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export default {
|
|
27
|
+
include: ["assembly"],
|
|
28
|
+
flags: "--exportRuntime --bindings raw",
|
|
29
|
+
collectCoverage: false,
|
|
30
|
+
output: ".as-test/output",
|
|
31
|
+
temp: ".as-test/tmp",
|
|
32
|
+
imports() {
|
|
33
|
+
return createAsTestImports(__dirname, { outputDir: ".as-test/output" });
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
|
package/asconfig.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"targets": {
|
|
3
|
+
"release": {
|
|
4
|
+
"binaryFile": "build/cardgames.wasm",
|
|
5
|
+
"textFile": "build/cardgames.wat",
|
|
6
|
+
"sourceMap": true,
|
|
7
|
+
"optimizeLevel": 3,
|
|
8
|
+
"shrinkLevel": 2,
|
|
9
|
+
"converge": false,
|
|
10
|
+
"noAssert": false,
|
|
11
|
+
"runtime": "stub",
|
|
12
|
+
"exportRuntime": true,
|
|
13
|
+
"exportStart": false,
|
|
14
|
+
"bindings": "esm",
|
|
15
|
+
"initialMemory": 10,
|
|
16
|
+
"maximumMemory": 256
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"options": {
|
|
20
|
+
"bindings": "esm"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* Common tests for blackjack action processing utilities
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, test, expect } from "assemblyscript-unittest-framework/assembly";
|
|
7
|
+
import {
|
|
8
|
+
AvailableActions,
|
|
9
|
+
calculateAvailableActions,
|
|
10
|
+
shouldDealerHit,
|
|
11
|
+
validateActionPhase,
|
|
12
|
+
validateActiveHand,
|
|
13
|
+
validateCanHit
|
|
14
|
+
} from "../../../blackjack/actions";
|
|
15
|
+
import { BlackjackRules } from "../../../blackjack/rules";
|
|
16
|
+
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// AvailableActions Class Tests
|
|
19
|
+
// ============================================================================
|
|
20
|
+
|
|
21
|
+
describe("AvailableActions", () => {
|
|
22
|
+
test("should initialize with all actions as false", () => {
|
|
23
|
+
const actions = new AvailableActions();
|
|
24
|
+
expect(actions.canStand).equal(false);
|
|
25
|
+
expect(actions.canDouble).equal(false);
|
|
26
|
+
expect(actions.canSplit).equal(false);
|
|
27
|
+
expect(actions.canSurrender).equal(false);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// ============================================================================
|
|
32
|
+
// calculateAvailableActions - General Tests
|
|
33
|
+
// ============================================================================
|
|
34
|
+
|
|
35
|
+
describe("calculateAvailableActions - General", () => {
|
|
36
|
+
test("should not allow any actions when hand is standing", () => {
|
|
37
|
+
const standardRules = BlackjackRules.standard();
|
|
38
|
+
const actions = calculateAvailableActions(
|
|
39
|
+
2,
|
|
40
|
+
false,
|
|
41
|
+
false,
|
|
42
|
+
true, // handIsStanding
|
|
43
|
+
false,
|
|
44
|
+
"PLAYING",
|
|
45
|
+
1,
|
|
46
|
+
true,
|
|
47
|
+
standardRules
|
|
48
|
+
);
|
|
49
|
+
expect(actions.canStand).equal(false);
|
|
50
|
+
expect(actions.canDouble).equal(false);
|
|
51
|
+
expect(actions.canSplit).equal(false);
|
|
52
|
+
expect(actions.canSurrender).equal(false);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("should not allow any actions when hand is busted", () => {
|
|
56
|
+
const standardRules = BlackjackRules.standard();
|
|
57
|
+
const actions = calculateAvailableActions(
|
|
58
|
+
3,
|
|
59
|
+
false,
|
|
60
|
+
false,
|
|
61
|
+
false,
|
|
62
|
+
true, // handIsBusted
|
|
63
|
+
"PLAYING",
|
|
64
|
+
1,
|
|
65
|
+
true,
|
|
66
|
+
standardRules
|
|
67
|
+
);
|
|
68
|
+
expect(actions.canStand).equal(false);
|
|
69
|
+
expect(actions.canDouble).equal(false);
|
|
70
|
+
expect(actions.canSplit).equal(false);
|
|
71
|
+
expect(actions.canSurrender).equal(false);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// ============================================================================
|
|
76
|
+
// shouldDealerHit Tests
|
|
77
|
+
// ============================================================================
|
|
78
|
+
|
|
79
|
+
describe("shouldDealerHit", () => {
|
|
80
|
+
test("should hit when hand value is below stand value", () => {
|
|
81
|
+
expect(shouldDealerHit(16, 17, false, false)).equal(true);
|
|
82
|
+
expect(shouldDealerHit(10, 17, false, false)).equal(true);
|
|
83
|
+
expect(shouldDealerHit(1, 17, false, false)).equal(true);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("should not hit when hand value is above stand value", () => {
|
|
87
|
+
expect(shouldDealerHit(18, 17, false, false)).equal(false);
|
|
88
|
+
expect(shouldDealerHit(20, 17, false, false)).equal(false);
|
|
89
|
+
expect(shouldDealerHit(21, 17, false, false)).equal(false);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test("should not hit on hard 17 when hitOnSoft17 is false", () => {
|
|
93
|
+
expect(shouldDealerHit(17, 17, false, false)).equal(false);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("should hit on soft 17 when hitOnSoft17 is true", () => {
|
|
97
|
+
expect(shouldDealerHit(17, 17, true, true)).equal(true);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test("should not hit on soft 17 when hitOnSoft17 is false", () => {
|
|
101
|
+
expect(shouldDealerHit(17, 17, false, true)).equal(false);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("should not hit on hard 17 even when hitOnSoft17 is true", () => {
|
|
105
|
+
expect(shouldDealerHit(17, 17, true, false)).equal(false);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("should handle custom stand value", () => {
|
|
109
|
+
expect(shouldDealerHit(15, 16, false, false)).equal(true);
|
|
110
|
+
expect(shouldDealerHit(16, 16, false, false)).equal(false);
|
|
111
|
+
expect(shouldDealerHit(17, 16, false, false)).equal(false);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// ============================================================================
|
|
116
|
+
// validateActionPhase Tests
|
|
117
|
+
// ============================================================================
|
|
118
|
+
|
|
119
|
+
describe("validateActionPhase", () => {
|
|
120
|
+
test("should not throw when phase matches", () => {
|
|
121
|
+
validateActionPhase("PLAYING", "HIT", "PLAYING");
|
|
122
|
+
// If we get here, no error was thrown
|
|
123
|
+
expect(true).equal(true);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// Note: AssemblyScript doesn't support try-catch, so we test that
|
|
127
|
+
// the function aborts on invalid input by checking the contract behavior
|
|
128
|
+
// In a real scenario, invalid phase would cause the contract to abort
|
|
129
|
+
test("should validate phase correctly", () => {
|
|
130
|
+
// Valid phase - should not abort
|
|
131
|
+
validateActionPhase("PLAYING", "HIT", "PLAYING");
|
|
132
|
+
expect(true).equal(true);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// ============================================================================
|
|
137
|
+
// validateActiveHand Tests
|
|
138
|
+
// ============================================================================
|
|
139
|
+
|
|
140
|
+
describe("validateActiveHand", () => {
|
|
141
|
+
test("should not throw when hand index is valid", () => {
|
|
142
|
+
validateActiveHand(0, 1);
|
|
143
|
+
validateActiveHand(1, 2);
|
|
144
|
+
validateActiveHand(0, 5);
|
|
145
|
+
// If we get here, no error was thrown
|
|
146
|
+
expect(true).equal(true);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// Note: AssemblyScript doesn't support try-catch, so we test that
|
|
150
|
+
// the function aborts on invalid input by checking the contract behavior
|
|
151
|
+
// In a real scenario, invalid hand index would cause the contract to abort
|
|
152
|
+
test("should validate hand index correctly", () => {
|
|
153
|
+
// Valid indices - should not abort
|
|
154
|
+
validateActiveHand(0, 1);
|
|
155
|
+
validateActiveHand(1, 2);
|
|
156
|
+
expect(true).equal(true);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// ============================================================================
|
|
161
|
+
// validateCanHit Tests
|
|
162
|
+
// ============================================================================
|
|
163
|
+
|
|
164
|
+
describe("validateCanHit", () => {
|
|
165
|
+
test("should not throw when hand can be hit", () => {
|
|
166
|
+
validateCanHit(false, false, false);
|
|
167
|
+
// If we get here, no error was thrown
|
|
168
|
+
expect(true).equal(true);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// Note: AssemblyScript doesn't support try-catch, so we test that
|
|
172
|
+
// the function aborts on invalid input by checking the contract behavior
|
|
173
|
+
// In a real scenario, invalid hit would cause the contract to abort
|
|
174
|
+
test("should validate hit conditions correctly", () => {
|
|
175
|
+
// Valid conditions - should not abort
|
|
176
|
+
validateCanHit(false, false, false);
|
|
177
|
+
expect(true).equal(true);
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
|
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* Tests for dealer hole card scenarios
|
|
4
|
+
*
|
|
5
|
+
* Tests various dealer showing/hole card combinations and their interactions
|
|
6
|
+
* with insurance, player blackjack, and game outcomes
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { describe, test, expect } from "assemblyscript-unittest-framework/assembly";
|
|
10
|
+
import {
|
|
11
|
+
shouldOfferInsurance,
|
|
12
|
+
calculateAvailableActions
|
|
13
|
+
} from "../../../blackjack/actions";
|
|
14
|
+
import { BlackjackRules } from "../../../blackjack/rules";
|
|
15
|
+
import {
|
|
16
|
+
isBlackjack,
|
|
17
|
+
calculateBlackjackHandValue
|
|
18
|
+
} from "../../../blackjack/blackjack";
|
|
19
|
+
import { Card, Rank, Suit } from "../../../cards";
|
|
20
|
+
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// Helper Functions
|
|
23
|
+
// ============================================================================
|
|
24
|
+
|
|
25
|
+
function createCard(rank: string, suit: string = Suit.SPADES): Card {
|
|
26
|
+
return new Card(suit, rank);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function createDealerHand(upCard: Card, holeCard: Card): Card[] {
|
|
30
|
+
return [upCard, holeCard];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function createPlayerHand(card1: Card, card2: Card): Card[] {
|
|
34
|
+
return [card1, card2];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ============================================================================
|
|
38
|
+
// Dealer Has Ace Showing, 10-K Down (Blackjack)
|
|
39
|
+
// ============================================================================
|
|
40
|
+
|
|
41
|
+
describe("Dealer Scenarios - Ace Showing, 10-K Down (Blackjack)", () => {
|
|
42
|
+
test("should offer insurance when dealer shows ace", () => {
|
|
43
|
+
const rules = BlackjackRules.standard();
|
|
44
|
+
const dealerUpCard = createCard(Rank.ACE);
|
|
45
|
+
expect(shouldOfferInsurance(dealerUpCard.rank, rules)).equal(true);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("dealer has blackjack with A showing and K down - player accepts insurance, player has blackjack", () => {
|
|
49
|
+
const dealerHand = createDealerHand(
|
|
50
|
+
createCard(Rank.ACE),
|
|
51
|
+
createCard(Rank.KING)
|
|
52
|
+
);
|
|
53
|
+
const playerHand = createPlayerHand(
|
|
54
|
+
createCard(Rank.ACE),
|
|
55
|
+
createCard(Rank.KING)
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
// Verify dealer has blackjack
|
|
59
|
+
expect(isBlackjack(dealerHand)).equal(true);
|
|
60
|
+
expect(calculateBlackjackHandValue(dealerHand)).equal(21);
|
|
61
|
+
|
|
62
|
+
// Verify player has blackjack
|
|
63
|
+
expect(isBlackjack(playerHand)).equal(true);
|
|
64
|
+
expect(calculateBlackjackHandValue(playerHand)).equal(21);
|
|
65
|
+
|
|
66
|
+
// Insurance should be offered
|
|
67
|
+
const rules = BlackjackRules.standard();
|
|
68
|
+
expect(shouldOfferInsurance(dealerHand[0].rank, rules)).equal(true);
|
|
69
|
+
|
|
70
|
+
// If player accepts insurance and dealer has blackjack:
|
|
71
|
+
// - Insurance pays 2:1 (player wins insurance bet)
|
|
72
|
+
// - Main bet pushes (both have blackjack)
|
|
73
|
+
// Player should be able to continue or the hand should be resolved
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("dealer has blackjack with A showing and K down - player accepts insurance, player does not have blackjack", () => {
|
|
77
|
+
const dealerHand = createDealerHand(
|
|
78
|
+
createCard(Rank.ACE),
|
|
79
|
+
createCard(Rank.KING)
|
|
80
|
+
);
|
|
81
|
+
const playerHand = createPlayerHand(
|
|
82
|
+
createCard(Rank.TEN),
|
|
83
|
+
createCard(Rank.SEVEN)
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
// Verify dealer has blackjack
|
|
87
|
+
expect(isBlackjack(dealerHand)).equal(true);
|
|
88
|
+
expect(calculateBlackjackHandValue(dealerHand)).equal(21);
|
|
89
|
+
|
|
90
|
+
// Verify player does not have blackjack
|
|
91
|
+
expect(isBlackjack(playerHand)).equal(false);
|
|
92
|
+
expect(calculateBlackjackHandValue(playerHand)).equal(17);
|
|
93
|
+
|
|
94
|
+
// Insurance should be offered
|
|
95
|
+
const rules = BlackjackRules.standard();
|
|
96
|
+
expect(shouldOfferInsurance(dealerHand[0].rank, rules)).equal(true);
|
|
97
|
+
|
|
98
|
+
// If player accepts insurance and dealer has blackjack:
|
|
99
|
+
// - Insurance pays 2:1 (player wins insurance bet)
|
|
100
|
+
// - Main bet loses (dealer blackjack beats player)
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("dealer has blackjack with A showing and K down - player denies insurance, player has blackjack", () => {
|
|
104
|
+
const dealerHand = createDealerHand(
|
|
105
|
+
createCard(Rank.ACE),
|
|
106
|
+
createCard(Rank.KING)
|
|
107
|
+
);
|
|
108
|
+
const playerHand = createPlayerHand(
|
|
109
|
+
createCard(Rank.ACE),
|
|
110
|
+
createCard(Rank.KING)
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
// Verify dealer has blackjack
|
|
114
|
+
expect(isBlackjack(dealerHand)).equal(true);
|
|
115
|
+
|
|
116
|
+
// Verify player has blackjack
|
|
117
|
+
expect(isBlackjack(playerHand)).equal(true);
|
|
118
|
+
|
|
119
|
+
// Insurance should be offered
|
|
120
|
+
const rules = BlackjackRules.standard();
|
|
121
|
+
expect(shouldOfferInsurance(dealerHand[0].rank, rules)).equal(true);
|
|
122
|
+
|
|
123
|
+
// If player denies insurance and dealer has blackjack:
|
|
124
|
+
// - No insurance payout
|
|
125
|
+
// - Main bet pushes (both have blackjack)
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test("dealer has blackjack with A showing and K down - player denies insurance, player does not have blackjack", () => {
|
|
129
|
+
const dealerHand = createDealerHand(
|
|
130
|
+
createCard(Rank.ACE),
|
|
131
|
+
createCard(Rank.KING)
|
|
132
|
+
);
|
|
133
|
+
const playerHand = createPlayerHand(
|
|
134
|
+
createCard(Rank.TEN),
|
|
135
|
+
createCard(Rank.SEVEN)
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
// Verify dealer has blackjack
|
|
139
|
+
expect(isBlackjack(dealerHand)).equal(true);
|
|
140
|
+
|
|
141
|
+
// Verify player does not have blackjack
|
|
142
|
+
expect(isBlackjack(playerHand)).equal(false);
|
|
143
|
+
|
|
144
|
+
// Insurance should be offered
|
|
145
|
+
const rules = BlackjackRules.standard();
|
|
146
|
+
expect(shouldOfferInsurance(dealerHand[0].rank, rules)).equal(true);
|
|
147
|
+
|
|
148
|
+
// If player denies insurance and dealer has blackjack:
|
|
149
|
+
// - No insurance payout
|
|
150
|
+
// - Main bet loses (dealer blackjack beats player)
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test("dealer has blackjack with A showing and Q down", () => {
|
|
154
|
+
const dealerHand = createDealerHand(
|
|
155
|
+
createCard(Rank.ACE),
|
|
156
|
+
createCard(Rank.QUEEN)
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
expect(isBlackjack(dealerHand)).equal(true);
|
|
160
|
+
expect(calculateBlackjackHandValue(dealerHand)).equal(21);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test("dealer has blackjack with A showing and J down", () => {
|
|
164
|
+
const dealerHand = createDealerHand(
|
|
165
|
+
createCard(Rank.ACE),
|
|
166
|
+
createCard(Rank.JACK)
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
expect(isBlackjack(dealerHand)).equal(true);
|
|
170
|
+
expect(calculateBlackjackHandValue(dealerHand)).equal(21);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test("dealer has blackjack with A showing and TEN down", () => {
|
|
174
|
+
const dealerHand = createDealerHand(
|
|
175
|
+
createCard(Rank.ACE),
|
|
176
|
+
createCard(Rank.TEN)
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
expect(isBlackjack(dealerHand)).equal(true);
|
|
180
|
+
expect(calculateBlackjackHandValue(dealerHand)).equal(21);
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// ============================================================================
|
|
185
|
+
// Dealer Has K Showing, A Down (Blackjack)
|
|
186
|
+
// ============================================================================
|
|
187
|
+
|
|
188
|
+
describe("Dealer Scenarios - K Showing, A Down (Blackjack)", () => {
|
|
189
|
+
test("dealer has blackjack with K showing and A down - player has blackjack", () => {
|
|
190
|
+
const dealerHand = createDealerHand(
|
|
191
|
+
createCard(Rank.KING),
|
|
192
|
+
createCard(Rank.ACE)
|
|
193
|
+
);
|
|
194
|
+
const playerHand = createPlayerHand(
|
|
195
|
+
createCard(Rank.ACE),
|
|
196
|
+
createCard(Rank.KING)
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
// Verify dealer has blackjack
|
|
200
|
+
expect(isBlackjack(dealerHand)).equal(true);
|
|
201
|
+
expect(calculateBlackjackHandValue(dealerHand)).equal(21);
|
|
202
|
+
|
|
203
|
+
// Verify player has blackjack
|
|
204
|
+
expect(isBlackjack(playerHand)).equal(true);
|
|
205
|
+
expect(calculateBlackjackHandValue(playerHand)).equal(21);
|
|
206
|
+
|
|
207
|
+
// Insurance should NOT be offered (dealer doesn't show ace)
|
|
208
|
+
const rules = BlackjackRules.standard();
|
|
209
|
+
expect(shouldOfferInsurance(dealerHand[0].rank, rules)).equal(false);
|
|
210
|
+
|
|
211
|
+
// When dealer has blackjack but doesn't show ace:
|
|
212
|
+
// - No insurance offered
|
|
213
|
+
// - If player has blackjack: push
|
|
214
|
+
// - If player doesn't have blackjack: player loses
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
test("dealer has blackjack with K showing and A down - player does not have blackjack", () => {
|
|
218
|
+
const dealerHand = createDealerHand(
|
|
219
|
+
createCard(Rank.KING),
|
|
220
|
+
createCard(Rank.ACE)
|
|
221
|
+
);
|
|
222
|
+
const playerHand = createPlayerHand(
|
|
223
|
+
createCard(Rank.TEN),
|
|
224
|
+
createCard(Rank.SEVEN)
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
// Verify dealer has blackjack
|
|
228
|
+
expect(isBlackjack(dealerHand)).equal(true);
|
|
229
|
+
expect(calculateBlackjackHandValue(dealerHand)).equal(21);
|
|
230
|
+
|
|
231
|
+
// Verify player does not have blackjack
|
|
232
|
+
expect(isBlackjack(playerHand)).equal(false);
|
|
233
|
+
expect(calculateBlackjackHandValue(playerHand)).equal(17);
|
|
234
|
+
|
|
235
|
+
// Insurance should NOT be offered (dealer doesn't show ace)
|
|
236
|
+
const rules = BlackjackRules.standard();
|
|
237
|
+
expect(shouldOfferInsurance(dealerHand[0].rank, rules)).equal(false);
|
|
238
|
+
|
|
239
|
+
// When dealer has blackjack but doesn't show ace:
|
|
240
|
+
// - No insurance offered
|
|
241
|
+
// - Player loses main bet
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test("dealer has blackjack with Q showing and A down", () => {
|
|
245
|
+
const dealerHand = createDealerHand(
|
|
246
|
+
createCard(Rank.QUEEN),
|
|
247
|
+
createCard(Rank.ACE)
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
expect(isBlackjack(dealerHand)).equal(true);
|
|
251
|
+
expect(calculateBlackjackHandValue(dealerHand)).equal(21);
|
|
252
|
+
|
|
253
|
+
const rules = BlackjackRules.standard();
|
|
254
|
+
expect(shouldOfferInsurance(dealerHand[0].rank, rules)).equal(false);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
test("dealer has blackjack with J showing and A down", () => {
|
|
258
|
+
const dealerHand = createDealerHand(
|
|
259
|
+
createCard(Rank.JACK),
|
|
260
|
+
createCard(Rank.ACE)
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
expect(isBlackjack(dealerHand)).equal(true);
|
|
264
|
+
expect(calculateBlackjackHandValue(dealerHand)).equal(21);
|
|
265
|
+
|
|
266
|
+
const rules = BlackjackRules.standard();
|
|
267
|
+
expect(shouldOfferInsurance(dealerHand[0].rank, rules)).equal(false);
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
// ============================================================================
|
|
272
|
+
// Dealer Has A Showing, Not 10-K Down (No Blackjack)
|
|
273
|
+
// ============================================================================
|
|
274
|
+
|
|
275
|
+
describe("Dealer Scenarios - Ace Showing, Not 10-K Down (No Blackjack)", () => {
|
|
276
|
+
test("dealer has A showing and 9 down (no blackjack) - player accepts insurance, player has blackjack", () => {
|
|
277
|
+
const dealerHand = createDealerHand(
|
|
278
|
+
createCard(Rank.ACE),
|
|
279
|
+
createCard(Rank.NINE)
|
|
280
|
+
);
|
|
281
|
+
const playerHand = createPlayerHand(
|
|
282
|
+
createCard(Rank.ACE),
|
|
283
|
+
createCard(Rank.KING)
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
// Verify dealer does NOT have blackjack
|
|
287
|
+
expect(isBlackjack(dealerHand)).equal(false);
|
|
288
|
+
expect(calculateBlackjackHandValue(dealerHand)).equal(20);
|
|
289
|
+
|
|
290
|
+
// Verify player has blackjack
|
|
291
|
+
expect(isBlackjack(playerHand)).equal(true);
|
|
292
|
+
expect(calculateBlackjackHandValue(playerHand)).equal(21);
|
|
293
|
+
|
|
294
|
+
// Insurance should be offered
|
|
295
|
+
const rules = BlackjackRules.standard();
|
|
296
|
+
expect(shouldOfferInsurance(dealerHand[0].rank, rules)).equal(true);
|
|
297
|
+
|
|
298
|
+
// If player accepts insurance and dealer does NOT have blackjack:
|
|
299
|
+
// - Insurance loses (player loses insurance bet)
|
|
300
|
+
// - Main bet: player blackjack beats dealer (player wins 3:2)
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
test("dealer has A showing and 9 down (no blackjack) - player accepts insurance, player does not have blackjack", () => {
|
|
304
|
+
const dealerHand = createDealerHand(
|
|
305
|
+
createCard(Rank.ACE),
|
|
306
|
+
createCard(Rank.NINE)
|
|
307
|
+
);
|
|
308
|
+
const playerHand = createPlayerHand(
|
|
309
|
+
createCard(Rank.TEN),
|
|
310
|
+
createCard(Rank.SEVEN)
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
// Verify dealer does NOT have blackjack
|
|
314
|
+
expect(isBlackjack(dealerHand)).equal(false);
|
|
315
|
+
expect(calculateBlackjackHandValue(dealerHand)).equal(20);
|
|
316
|
+
|
|
317
|
+
// Verify player does not have blackjack
|
|
318
|
+
expect(isBlackjack(playerHand)).equal(false);
|
|
319
|
+
expect(calculateBlackjackHandValue(playerHand)).equal(17);
|
|
320
|
+
|
|
321
|
+
// Insurance should be offered
|
|
322
|
+
const rules = BlackjackRules.standard();
|
|
323
|
+
expect(shouldOfferInsurance(dealerHand[0].rank, rules)).equal(true);
|
|
324
|
+
|
|
325
|
+
// If player accepts insurance and dealer does NOT have blackjack:
|
|
326
|
+
// - Insurance loses (player loses insurance bet)
|
|
327
|
+
// - Main bet: game continues (dealer has 20, player has 17)
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
test("dealer has A showing and 9 down (no blackjack) - player denies insurance, player has blackjack", () => {
|
|
331
|
+
const dealerHand = createDealerHand(
|
|
332
|
+
createCard(Rank.ACE),
|
|
333
|
+
createCard(Rank.NINE)
|
|
334
|
+
);
|
|
335
|
+
const playerHand = createPlayerHand(
|
|
336
|
+
createCard(Rank.ACE),
|
|
337
|
+
createCard(Rank.KING)
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
// Verify dealer does NOT have blackjack
|
|
341
|
+
expect(isBlackjack(dealerHand)).equal(false);
|
|
342
|
+
expect(calculateBlackjackHandValue(dealerHand)).equal(20);
|
|
343
|
+
|
|
344
|
+
// Verify player has blackjack
|
|
345
|
+
expect(isBlackjack(playerHand)).equal(true);
|
|
346
|
+
expect(calculateBlackjackHandValue(playerHand)).equal(21);
|
|
347
|
+
|
|
348
|
+
// Insurance should be offered
|
|
349
|
+
const rules = BlackjackRules.standard();
|
|
350
|
+
expect(shouldOfferInsurance(dealerHand[0].rank, rules)).equal(true);
|
|
351
|
+
|
|
352
|
+
// If player denies insurance and dealer does NOT have blackjack:
|
|
353
|
+
// - No insurance bet
|
|
354
|
+
// - Main bet: player blackjack beats dealer (player wins 3:2)
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
test("dealer has A showing and 9 down (no blackjack) - player denies insurance, player does not have blackjack", () => {
|
|
358
|
+
const dealerHand = createDealerHand(
|
|
359
|
+
createCard(Rank.ACE),
|
|
360
|
+
createCard(Rank.NINE)
|
|
361
|
+
);
|
|
362
|
+
const playerHand = createPlayerHand(
|
|
363
|
+
createCard(Rank.TEN),
|
|
364
|
+
createCard(Rank.SEVEN)
|
|
365
|
+
);
|
|
366
|
+
|
|
367
|
+
// Verify dealer does NOT have blackjack
|
|
368
|
+
expect(isBlackjack(dealerHand)).equal(false);
|
|
369
|
+
expect(calculateBlackjackHandValue(dealerHand)).equal(20);
|
|
370
|
+
|
|
371
|
+
// Verify player does not have blackjack
|
|
372
|
+
expect(isBlackjack(playerHand)).equal(false);
|
|
373
|
+
expect(calculateBlackjackHandValue(playerHand)).equal(17);
|
|
374
|
+
|
|
375
|
+
// Insurance should be offered
|
|
376
|
+
const rules = BlackjackRules.standard();
|
|
377
|
+
expect(shouldOfferInsurance(dealerHand[0].rank, rules)).equal(true);
|
|
378
|
+
|
|
379
|
+
// If player denies insurance and dealer does NOT have blackjack:
|
|
380
|
+
// - No insurance bet
|
|
381
|
+
// - Main bet: game continues (dealer has 20, player has 17)
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
test("dealer has A showing and 8 down (no blackjack)", () => {
|
|
385
|
+
const dealerHand = createDealerHand(
|
|
386
|
+
createCard(Rank.ACE),
|
|
387
|
+
createCard(Rank.EIGHT)
|
|
388
|
+
);
|
|
389
|
+
|
|
390
|
+
expect(isBlackjack(dealerHand)).equal(false);
|
|
391
|
+
expect(calculateBlackjackHandValue(dealerHand)).equal(19);
|
|
392
|
+
|
|
393
|
+
const rules = BlackjackRules.standard();
|
|
394
|
+
expect(shouldOfferInsurance(dealerHand[0].rank, rules)).equal(true);
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
test("dealer has A showing and 7 down (no blackjack)", () => {
|
|
398
|
+
const dealerHand = createDealerHand(
|
|
399
|
+
createCard(Rank.ACE),
|
|
400
|
+
createCard(Rank.SEVEN)
|
|
401
|
+
);
|
|
402
|
+
|
|
403
|
+
expect(isBlackjack(dealerHand)).equal(false);
|
|
404
|
+
expect(calculateBlackjackHandValue(dealerHand)).equal(18);
|
|
405
|
+
|
|
406
|
+
const rules = BlackjackRules.standard();
|
|
407
|
+
expect(shouldOfferInsurance(dealerHand[0].rank, rules)).equal(true);
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
test("dealer has A showing and 2 down (no blackjack)", () => {
|
|
411
|
+
const dealerHand = createDealerHand(
|
|
412
|
+
createCard(Rank.ACE),
|
|
413
|
+
createCard(Rank.TWO)
|
|
414
|
+
);
|
|
415
|
+
|
|
416
|
+
expect(isBlackjack(dealerHand)).equal(false);
|
|
417
|
+
expect(calculateBlackjackHandValue(dealerHand)).equal(13);
|
|
418
|
+
|
|
419
|
+
const rules = BlackjackRules.standard();
|
|
420
|
+
expect(shouldOfferInsurance(dealerHand[0].rank, rules)).equal(true);
|
|
421
|
+
});
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
// ============================================================================
|
|
425
|
+
// Additional Edge Cases
|
|
426
|
+
// ============================================================================
|
|
427
|
+
|
|
428
|
+
describe("Dealer Scenarios - Edge Cases", () => {
|
|
429
|
+
test("dealer has A showing and A down (soft 12, no blackjack)", () => {
|
|
430
|
+
const dealerHand = createDealerHand(
|
|
431
|
+
createCard(Rank.ACE),
|
|
432
|
+
createCard(Rank.ACE)
|
|
433
|
+
);
|
|
434
|
+
|
|
435
|
+
expect(isBlackjack(dealerHand)).equal(false);
|
|
436
|
+
expect(calculateBlackjackHandValue(dealerHand)).equal(12);
|
|
437
|
+
|
|
438
|
+
const rules = BlackjackRules.standard();
|
|
439
|
+
expect(shouldOfferInsurance(dealerHand[0].rank, rules)).equal(true);
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
test("insurance not offered when insurance is disabled in rules", () => {
|
|
443
|
+
const dealerHand = createDealerHand(
|
|
444
|
+
createCard(Rank.ACE),
|
|
445
|
+
createCard(Rank.KING)
|
|
446
|
+
);
|
|
447
|
+
|
|
448
|
+
const rules = new BlackjackRules(17, false, 4, false, true, false, false); // insuranceOffered = false
|
|
449
|
+
expect(shouldOfferInsurance(dealerHand[0].rank, rules)).equal(false);
|
|
450
|
+
});
|
|
451
|
+
});
|
|
452
|
+
|