@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,182 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* Poker Variant Configuration
|
|
4
|
+
*
|
|
5
|
+
* Defines configurations for different poker variants to make the library
|
|
6
|
+
* flexible enough to support Hold'em, Stud, Six-Plus, and other variants.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Poker variant types
|
|
11
|
+
*/
|
|
12
|
+
export class PokerVariant {
|
|
13
|
+
static readonly TEXAS_HOLDEM: string = "TEXAS_HOLDEM";
|
|
14
|
+
static readonly OMAHA: string = "OMAHA";
|
|
15
|
+
static readonly OMAHA_HI_LO: string = "OMAHA_HI_LO";
|
|
16
|
+
static readonly SEVEN_CARD_STUD: string = "SEVEN_CARD_STUD";
|
|
17
|
+
static readonly FIVE_CARD_STUD: string = "FIVE_CARD_STUD";
|
|
18
|
+
static readonly RAZZ: string = "RAZZ"; // Lowball 7-card stud
|
|
19
|
+
static readonly SIX_PLUS_HOLDEM: string = "SIX_PLUS_HOLDEM";
|
|
20
|
+
static readonly SHORT_DECK_HOLDEM: string = "SIX_PLUS_HOLDEM"; // Alias
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Deck configuration for poker variants
|
|
25
|
+
*/
|
|
26
|
+
export class PokerDeckConfig {
|
|
27
|
+
static readonly STANDARD_52: string = "STANDARD_52"; // Standard 52-card deck
|
|
28
|
+
static readonly SIX_PLUS_36: string = "SIX_PLUS_36"; // 36-card deck (removes 2-5)
|
|
29
|
+
static readonly SHORT_DECK_36: string = "SIX_PLUS_36"; // Alias
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Hand structure configuration
|
|
34
|
+
*/
|
|
35
|
+
export class HandStructure {
|
|
36
|
+
holeCards: i32 = 0; // Number of hole cards dealt to each player
|
|
37
|
+
communityCards: i32 = 0; // Number of community cards
|
|
38
|
+
cardsToUse: i32 = 5; // Number of cards to use for best hand (usually 5)
|
|
39
|
+
mustUseHoleCards: i32 = 0; // Number of hole cards that must be used (0 = any, 2 = Omaha)
|
|
40
|
+
|
|
41
|
+
constructor(
|
|
42
|
+
holeCards: i32 = 0,
|
|
43
|
+
communityCards: i32 = 0,
|
|
44
|
+
cardsToUse: i32 = 5,
|
|
45
|
+
mustUseHoleCards: i32 = 0
|
|
46
|
+
) {
|
|
47
|
+
this.holeCards = holeCards;
|
|
48
|
+
this.communityCards = communityCards;
|
|
49
|
+
this.cardsToUse = cardsToUse;
|
|
50
|
+
this.mustUseHoleCards = mustUseHoleCards;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
clone(): HandStructure {
|
|
54
|
+
return new HandStructure(
|
|
55
|
+
this.holeCards,
|
|
56
|
+
this.communityCards,
|
|
57
|
+
this.cardsToUse,
|
|
58
|
+
this.mustUseHoleCards
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Poker variant configuration
|
|
65
|
+
*/
|
|
66
|
+
export class PokerVariantConfig {
|
|
67
|
+
variant: string = "";
|
|
68
|
+
deckConfig: string = "";
|
|
69
|
+
handStructure: HandStructure;
|
|
70
|
+
usesLowball: bool = false; // Whether this is a lowball variant (lowest hand wins)
|
|
71
|
+
usesHighLow: bool = false; // Whether this splits pot between high and low
|
|
72
|
+
|
|
73
|
+
constructor(
|
|
74
|
+
variant: string,
|
|
75
|
+
deckConfig: string = PokerDeckConfig.STANDARD_52,
|
|
76
|
+
handStructure: HandStructure = new HandStructure(),
|
|
77
|
+
usesLowball: bool = false,
|
|
78
|
+
usesHighLow: bool = false
|
|
79
|
+
) {
|
|
80
|
+
this.variant = variant;
|
|
81
|
+
this.deckConfig = deckConfig;
|
|
82
|
+
this.handStructure = handStructure;
|
|
83
|
+
this.usesLowball = usesLowball;
|
|
84
|
+
this.usesHighLow = usesHighLow;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
clone(): PokerVariantConfig {
|
|
88
|
+
return new PokerVariantConfig(
|
|
89
|
+
this.variant,
|
|
90
|
+
this.deckConfig,
|
|
91
|
+
this.handStructure.clone(),
|
|
92
|
+
this.usesLowball,
|
|
93
|
+
this.usesHighLow
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Predefined variant configurations
|
|
100
|
+
*/
|
|
101
|
+
export class VariantConfigs {
|
|
102
|
+
/**
|
|
103
|
+
* Texas Hold'em: 2 hole cards, 5 community cards, use any 5
|
|
104
|
+
*/
|
|
105
|
+
static texasHoldem(): PokerVariantConfig {
|
|
106
|
+
return new PokerVariantConfig(
|
|
107
|
+
PokerVariant.TEXAS_HOLDEM,
|
|
108
|
+
PokerDeckConfig.STANDARD_52,
|
|
109
|
+
new HandStructure(2, 5, 5, 0)
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Omaha: 4 hole cards, 5 community cards, must use exactly 2 hole cards
|
|
115
|
+
*/
|
|
116
|
+
static omaha(): PokerVariantConfig {
|
|
117
|
+
return new PokerVariantConfig(
|
|
118
|
+
PokerVariant.OMAHA,
|
|
119
|
+
PokerDeckConfig.STANDARD_52,
|
|
120
|
+
new HandStructure(4, 5, 5, 2)
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Omaha Hi-Lo: Same as Omaha but splits pot between high and low
|
|
126
|
+
*/
|
|
127
|
+
static omahaHiLo(): PokerVariantConfig {
|
|
128
|
+
return new PokerVariantConfig(
|
|
129
|
+
PokerVariant.OMAHA_HI_LO,
|
|
130
|
+
PokerDeckConfig.STANDARD_52,
|
|
131
|
+
new HandStructure(4, 5, 5, 2),
|
|
132
|
+
false,
|
|
133
|
+
true
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* 7-Card Stud: 7 cards per player, no community cards, use best 5
|
|
139
|
+
*/
|
|
140
|
+
static sevenCardStud(): PokerVariantConfig {
|
|
141
|
+
return new PokerVariantConfig(
|
|
142
|
+
PokerVariant.SEVEN_CARD_STUD,
|
|
143
|
+
PokerDeckConfig.STANDARD_52,
|
|
144
|
+
new HandStructure(7, 0, 5, 0)
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* 5-Card Stud: 5 cards per player, no community cards, use all 5
|
|
150
|
+
*/
|
|
151
|
+
static fiveCardStud(): PokerVariantConfig {
|
|
152
|
+
return new PokerVariantConfig(
|
|
153
|
+
PokerVariant.FIVE_CARD_STUD,
|
|
154
|
+
PokerDeckConfig.STANDARD_52,
|
|
155
|
+
new HandStructure(5, 0, 5, 0)
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Razz: 7-card stud lowball (lowest hand wins)
|
|
161
|
+
*/
|
|
162
|
+
static razz(): PokerVariantConfig {
|
|
163
|
+
return new PokerVariantConfig(
|
|
164
|
+
PokerVariant.RAZZ,
|
|
165
|
+
PokerDeckConfig.STANDARD_52,
|
|
166
|
+
new HandStructure(7, 0, 5, 0),
|
|
167
|
+
true // Lowball
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Six-Plus Hold'em: 2 hole cards, 5 community cards, 36-card deck
|
|
173
|
+
*/
|
|
174
|
+
static sixPlusHoldem(): PokerVariantConfig {
|
|
175
|
+
return new PokerVariantConfig(
|
|
176
|
+
PokerVariant.SIX_PLUS_HOLDEM,
|
|
177
|
+
PokerDeckConfig.SIX_PLUS_36,
|
|
178
|
+
new HandStructure(2, 5, 5, 0)
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* Poker Hand Evaluation Utilities
|
|
4
|
+
*
|
|
5
|
+
* Provides utilities for evaluating and comparing poker hands (5-card hands)
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { Card, Rank, HandType, HandRank } from "./cards";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Evaluate a poker hand from hole cards and community cards
|
|
12
|
+
* Returns the best 5-card hand rank
|
|
13
|
+
*/
|
|
14
|
+
export function evaluateHand(holeCards: Card[], community: Card[]): HandRank {
|
|
15
|
+
// Combine all cards
|
|
16
|
+
const allCards = new Array<Card>(holeCards.length + community.length);
|
|
17
|
+
for (let i = 0; i < holeCards.length; i++) {
|
|
18
|
+
allCards[i] = holeCards[i];
|
|
19
|
+
}
|
|
20
|
+
for (let i = 0; i < community.length; i++) {
|
|
21
|
+
allCards[holeCards.length + i] = community[i];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Need at least 5 cards total
|
|
25
|
+
if (allCards.length < 5) {
|
|
26
|
+
return new HandRank(HandType.HIGH_CARD, []);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Try all combinations of 5 cards from allCards
|
|
30
|
+
let bestRank: HandRank | null = null;
|
|
31
|
+
|
|
32
|
+
const n = allCards.length;
|
|
33
|
+
// Generate all combinations of 5 cards
|
|
34
|
+
for (let i = 0; i < n - 4; i++) {
|
|
35
|
+
for (let j = i + 1; j < n - 3; j++) {
|
|
36
|
+
for (let k = j + 1; k < n - 2; k++) {
|
|
37
|
+
for (let l = k + 1; l < n - 1; l++) {
|
|
38
|
+
for (let m = l + 1; m < n; m++) {
|
|
39
|
+
const fiveCards = new Array<Card>(5);
|
|
40
|
+
fiveCards[0] = allCards[i];
|
|
41
|
+
fiveCards[1] = allCards[j];
|
|
42
|
+
fiveCards[2] = allCards[k];
|
|
43
|
+
fiveCards[3] = allCards[l];
|
|
44
|
+
fiveCards[4] = allCards[m];
|
|
45
|
+
|
|
46
|
+
const rank = evaluateFiveCards(fiveCards);
|
|
47
|
+
if (bestRank === null || rank.compare(bestRank) > 0) {
|
|
48
|
+
bestRank = rank;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return bestRank !== null ? bestRank : new HandRank(HandType.HIGH_CARD, []);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Evaluate exactly 5 cards
|
|
61
|
+
*/
|
|
62
|
+
function evaluateFiveCards(cards: Card[]): HandRank {
|
|
63
|
+
if (cards.length !== 5) {
|
|
64
|
+
return new HandRank(HandType.HIGH_CARD, []);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Count ranks and suits
|
|
68
|
+
const rankCounts = new Map<string, i32>();
|
|
69
|
+
const suitCounts = new Map<string, i32>();
|
|
70
|
+
const rankValues = new Array<i32>(5);
|
|
71
|
+
|
|
72
|
+
for (let i = 0; i < 5; i++) {
|
|
73
|
+
const rank = cards[i].rank;
|
|
74
|
+
const suit = cards[i].suit;
|
|
75
|
+
const value = cards[i].getValue();
|
|
76
|
+
|
|
77
|
+
rankValues[i] = value;
|
|
78
|
+
|
|
79
|
+
rankCounts.set(rank, (rankCounts.has(rank) ? rankCounts.get(rank) : 0) + 1);
|
|
80
|
+
suitCounts.set(suit, (suitCounts.has(suit) ? suitCounts.get(suit) : 0) + 1);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Sort rank values descending
|
|
84
|
+
rankValues.sort((a, b) => b - a);
|
|
85
|
+
|
|
86
|
+
// Check for flush
|
|
87
|
+
let isFlush = false;
|
|
88
|
+
const suits = suitCounts.keys();
|
|
89
|
+
for (let i = 0; i < suits.length; i++) {
|
|
90
|
+
if (suitCounts.get(suits[i]) === 5) {
|
|
91
|
+
isFlush = true;
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Check for straight
|
|
97
|
+
let isStraight = false;
|
|
98
|
+
let straightHigh = 0;
|
|
99
|
+
|
|
100
|
+
// Check normal straight
|
|
101
|
+
let consecutive = 1;
|
|
102
|
+
for (let i = 1; i < 5; i++) {
|
|
103
|
+
if (rankValues[i] === rankValues[i - 1] - 1) {
|
|
104
|
+
consecutive++;
|
|
105
|
+
if (consecutive === 5) {
|
|
106
|
+
isStraight = true;
|
|
107
|
+
straightHigh = rankValues[i - 4];
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
} else if (rankValues[i] !== rankValues[i - 1]) {
|
|
111
|
+
consecutive = 1;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Check A-2-3-4-5 straight (wheel)
|
|
116
|
+
if (!isStraight) {
|
|
117
|
+
const hasAce = rankValues[0] === 14;
|
|
118
|
+
const hasTwo = rankValues[1] === 2 || rankValues[2] === 2 || rankValues[3] === 2 || rankValues[4] === 2;
|
|
119
|
+
const hasThree = rankValues[1] === 3 || rankValues[2] === 3 || rankValues[3] === 3 || rankValues[4] === 3;
|
|
120
|
+
const hasFour = rankValues[1] === 4 || rankValues[2] === 4 || rankValues[3] === 4 || rankValues[4] === 4;
|
|
121
|
+
const hasFive = rankValues[1] === 5 || rankValues[2] === 5 || rankValues[3] === 5 || rankValues[4] === 5;
|
|
122
|
+
|
|
123
|
+
if (hasAce && hasTwo && hasThree && hasFour && hasFive) {
|
|
124
|
+
isStraight = true;
|
|
125
|
+
straightHigh = 5; // Ace plays low in wheel
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Count pairs, trips, quads
|
|
130
|
+
const counts = new Array<i32>(0);
|
|
131
|
+
const countValues = new Map<i32, i32>();
|
|
132
|
+
const rankCountValues = rankCounts.values();
|
|
133
|
+
for (let i = 0; i < rankCountValues.length; i++) {
|
|
134
|
+
const count = rankCountValues[i];
|
|
135
|
+
counts.push(count);
|
|
136
|
+
if (count === 2 || count === 3 || count === 4) {
|
|
137
|
+
// Find the rank value for this count
|
|
138
|
+
const ranks = rankCounts.keys();
|
|
139
|
+
for (let j = 0; j < ranks.length; j++) {
|
|
140
|
+
if (rankCounts.get(ranks[j]) === count) {
|
|
141
|
+
const rankVal = Rank.getValue(ranks[j]);
|
|
142
|
+
if (!countValues.has(count)) {
|
|
143
|
+
countValues.set(count, rankVal);
|
|
144
|
+
} else {
|
|
145
|
+
// Multiple of same count (e.g., two pair)
|
|
146
|
+
const existing = countValues.get(count);
|
|
147
|
+
if (rankVal > existing) {
|
|
148
|
+
countValues.set(count, rankVal);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Determine hand type
|
|
157
|
+
let hasFour = false;
|
|
158
|
+
let hasThree = false;
|
|
159
|
+
let pairCount = 0;
|
|
160
|
+
let pairValue = 0;
|
|
161
|
+
let secondPairValue = 0;
|
|
162
|
+
let tripsValue = 0;
|
|
163
|
+
let quadsValue = 0;
|
|
164
|
+
|
|
165
|
+
for (let i = 0; i < counts.length; i++) {
|
|
166
|
+
if (counts[i] === 4) {
|
|
167
|
+
hasFour = true;
|
|
168
|
+
quadsValue = countValues.has(4) ? countValues.get(4) : 0;
|
|
169
|
+
} else if (counts[i] === 3) {
|
|
170
|
+
hasThree = true;
|
|
171
|
+
tripsValue = countValues.has(3) ? countValues.get(3) : 0;
|
|
172
|
+
} else if (counts[i] === 2) {
|
|
173
|
+
pairCount++;
|
|
174
|
+
if (pairValue === 0) {
|
|
175
|
+
pairValue = countValues.has(2) ? countValues.get(2) : 0;
|
|
176
|
+
} else {
|
|
177
|
+
secondPairValue = countValues.has(2) ? countValues.get(2) : 0;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Royal flush
|
|
183
|
+
if (isFlush && isStraight && straightHigh === 14) {
|
|
184
|
+
return new HandRank(HandType.ROYAL_FLUSH, []);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Straight flush
|
|
188
|
+
if (isFlush && isStraight) {
|
|
189
|
+
return new HandRank(HandType.STRAIGHT_FLUSH, [straightHigh]);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Four of a kind
|
|
193
|
+
if (hasFour) {
|
|
194
|
+
const kicker = rankValues[0] === quadsValue ? rankValues[4] : rankValues[0];
|
|
195
|
+
return new HandRank(HandType.FOUR_OF_A_KIND, [quadsValue, kicker]);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Full house
|
|
199
|
+
if (hasThree && pairCount > 0) {
|
|
200
|
+
return new HandRank(HandType.FULL_HOUSE, [tripsValue, pairValue]);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Flush
|
|
204
|
+
if (isFlush) {
|
|
205
|
+
return new HandRank(HandType.FLUSH, rankValues);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Straight
|
|
209
|
+
if (isStraight) {
|
|
210
|
+
return new HandRank(HandType.STRAIGHT, [straightHigh]);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Three of a kind
|
|
214
|
+
if (hasThree) {
|
|
215
|
+
const kickers = new Array<i32>(0);
|
|
216
|
+
for (let i = 0; i < 5; i++) {
|
|
217
|
+
if (rankValues[i] !== tripsValue) {
|
|
218
|
+
kickers.push(rankValues[i]);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
kickers.sort((a, b) => b - a);
|
|
222
|
+
return new HandRank(HandType.THREE_OF_A_KIND, [tripsValue, kickers[0], kickers[1]]);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Two pair
|
|
226
|
+
if (pairCount >= 2) {
|
|
227
|
+
const highPair = pairValue > secondPairValue ? pairValue : secondPairValue;
|
|
228
|
+
const lowPair = pairValue > secondPairValue ? secondPairValue : pairValue;
|
|
229
|
+
const kicker = rankValues[0] === highPair || rankValues[0] === lowPair
|
|
230
|
+
? (rankValues[1] === highPair || rankValues[1] === lowPair
|
|
231
|
+
? rankValues[2]
|
|
232
|
+
: rankValues[1])
|
|
233
|
+
: rankValues[0];
|
|
234
|
+
return new HandRank(HandType.TWO_PAIR, [highPair, lowPair, kicker]);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Pair
|
|
238
|
+
if (pairCount === 1) {
|
|
239
|
+
const kickers = new Array<i32>(0);
|
|
240
|
+
for (let i = 0; i < 5; i++) {
|
|
241
|
+
if (rankValues[i] !== pairValue) {
|
|
242
|
+
kickers.push(rankValues[i]);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
kickers.sort((a, b) => b - a);
|
|
246
|
+
return new HandRank(HandType.PAIR, [pairValue, kickers[0], kickers[1], kickers[2]]);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// High card
|
|
250
|
+
return new HandRank(HandType.HIGH_CARD, rankValues);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Compare two hands and determine winner
|
|
255
|
+
* Returns: -1 if hand1 < hand2, 0 if equal, 1 if hand1 > hand2
|
|
256
|
+
*/
|
|
257
|
+
export function compareHands(hand1: HandRank, hand2: HandRank): i32 {
|
|
258
|
+
return hand1.compare(hand2);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Get the best 5-card hand from hole cards and community cards
|
|
263
|
+
* Returns the 5 cards that form the best hand
|
|
264
|
+
*/
|
|
265
|
+
export function getBestFiveCards(holeCards: Card[], community: Card[]): Card[] {
|
|
266
|
+
const allCards = new Array<Card>(holeCards.length + community.length);
|
|
267
|
+
for (let i = 0; i < holeCards.length; i++) {
|
|
268
|
+
allCards[i] = holeCards[i];
|
|
269
|
+
}
|
|
270
|
+
for (let i = 0; i < community.length; i++) {
|
|
271
|
+
allCards[holeCards.length + i] = community[i];
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (allCards.length < 5) {
|
|
275
|
+
return allCards;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
let bestRank: HandRank | null = null;
|
|
279
|
+
let bestCards: Card[] | null = null;
|
|
280
|
+
|
|
281
|
+
const n = allCards.length;
|
|
282
|
+
for (let i = 0; i < n - 4; i++) {
|
|
283
|
+
for (let j = i + 1; j < n - 3; j++) {
|
|
284
|
+
for (let k = j + 1; k < n - 2; k++) {
|
|
285
|
+
for (let l = k + 1; l < n - 1; l++) {
|
|
286
|
+
for (let m = l + 1; m < n; m++) {
|
|
287
|
+
const fiveCards = new Array<Card>(5);
|
|
288
|
+
fiveCards[0] = allCards[i];
|
|
289
|
+
fiveCards[1] = allCards[j];
|
|
290
|
+
fiveCards[2] = allCards[k];
|
|
291
|
+
fiveCards[3] = allCards[l];
|
|
292
|
+
fiveCards[4] = allCards[m];
|
|
293
|
+
|
|
294
|
+
const rank = evaluateFiveCards(fiveCards);
|
|
295
|
+
if (bestRank === null || rank.compare(bestRank) > 0) {
|
|
296
|
+
bestRank = rank;
|
|
297
|
+
bestCards = fiveCards;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return bestCards !== null ? bestCards : allCards.slice(0, 5);
|
|
306
|
+
}
|
|
307
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@arcanahq/cardgames",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Utility library for card game logic, blackjack actions, and poker game utilities",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"main": "src/index.ts",
|
|
10
|
+
"types": "src/index.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": "./src/index.ts",
|
|
13
|
+
"./assembly/index": "./assembly/index.ts",
|
|
14
|
+
"./assembly/blackjack/actions": "./assembly/blackjack/actions.ts",
|
|
15
|
+
"./assembly/blackjack/rules": "./assembly/blackjack/rules.ts",
|
|
16
|
+
"./assembly/blackjack/blackjack": "./assembly/blackjack/blackjack.ts",
|
|
17
|
+
"./assembly/cardgames": "./assembly/cardgames.ts",
|
|
18
|
+
"./assembly/cards": "./assembly/cards.ts",
|
|
19
|
+
"./assembly/poker": "./assembly/poker.ts",
|
|
20
|
+
"./assembly/deck": "./assembly/deck/index.ts",
|
|
21
|
+
"./assembly/poker/game": "./assembly/poker/index.ts"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "asc assembly/index.ts --target release --exportRuntime --disableWarning AS235",
|
|
25
|
+
"test": "npx cross-env NODE_OPTIONS='--import tsx' npx as-test --config ./as-test.config.js --testFiles assembly/__tests__/test.ts"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"contract",
|
|
29
|
+
"engine",
|
|
30
|
+
"assemblyscript",
|
|
31
|
+
"wasm",
|
|
32
|
+
"cardgames",
|
|
33
|
+
"blackjack",
|
|
34
|
+
"poker",
|
|
35
|
+
"utility",
|
|
36
|
+
"library"
|
|
37
|
+
],
|
|
38
|
+
"author": "",
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@assemblyscript/loader": "^0.28.9",
|
|
42
|
+
"assemblyscript": "^0.27.0",
|
|
43
|
+
"assemblyscript-unittest-framework": "^2.1.0",
|
|
44
|
+
"cross-env": "^7.0.3",
|
|
45
|
+
"tsx": "^4.7.0",
|
|
46
|
+
"typescript": "^5.3.3"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@arcanahq/core": "file:../core"
|
|
50
|
+
}
|
|
51
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../core/tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"baseUrl": ".",
|
|
5
|
+
"paths": {
|
|
6
|
+
"@arcanahq/core/*": ["../core/assembly/*"],
|
|
7
|
+
"@arcanahq/cardgames/*": ["./assembly/*"]
|
|
8
|
+
}
|
|
9
|
+
},
|
|
10
|
+
"include": [
|
|
11
|
+
"assembly/**/*",
|
|
12
|
+
"src/**/*"
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
|