@block52/poker-vm-sdk 1.5.1 → 1.7.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/dist/index.js CHANGED
@@ -2847,6 +2847,7 @@ function createBaseMsgCreateGame() {
2847
2847
  gameVariant: "",
2848
2848
  entryFee: Long.UZERO,
2849
2849
  name: "",
2850
+ bettingLimit: "",
2850
2851
  };
2851
2852
  }
2852
2853
  const MsgCreateGame = {
@@ -2905,6 +2906,9 @@ const MsgCreateGame = {
2905
2906
  if (message.name !== "") {
2906
2907
  writer.uint32(154).string(message.name);
2907
2908
  }
2909
+ if (message.bettingLimit !== "") {
2910
+ writer.uint32(162).string(message.bettingLimit);
2911
+ }
2908
2912
  return writer;
2909
2913
  },
2910
2914
  decode(input, length) {
@@ -3040,6 +3044,13 @@ const MsgCreateGame = {
3040
3044
  message.name = reader.string();
3041
3045
  continue;
3042
3046
  }
3047
+ case 20: {
3048
+ if (tag !== 162) {
3049
+ break;
3050
+ }
3051
+ message.bettingLimit = reader.string();
3052
+ continue;
3053
+ }
3043
3054
  }
3044
3055
  if ((tag & 7) === 4 || tag === 0) {
3045
3056
  break;
@@ -3068,6 +3079,7 @@ const MsgCreateGame = {
3068
3079
  gameVariant: isSet$2b(object.gameVariant) ? globalThis.String(object.gameVariant) : "",
3069
3080
  entryFee: isSet$2b(object.entryFee) ? Long.fromValue(object.entryFee) : Long.UZERO,
3070
3081
  name: isSet$2b(object.name) ? globalThis.String(object.name) : "",
3082
+ bettingLimit: isSet$2b(object.bettingLimit) ? globalThis.String(object.bettingLimit) : "",
3071
3083
  };
3072
3084
  },
3073
3085
  toJSON(message) {
@@ -3126,6 +3138,9 @@ const MsgCreateGame = {
3126
3138
  if (message.name !== "") {
3127
3139
  obj.name = message.name;
3128
3140
  }
3141
+ if (message.bettingLimit !== "") {
3142
+ obj.bettingLimit = message.bettingLimit;
3143
+ }
3129
3144
  return obj;
3130
3145
  },
3131
3146
  create(base) {
@@ -3170,6 +3185,7 @@ const MsgCreateGame = {
3170
3185
  message.blindLevelDuration = object.blindLevelDuration ?? 0;
3171
3186
  message.gameVariant = object.gameVariant ?? "";
3172
3187
  message.name = object.name ?? "";
3188
+ message.bettingLimit = object.bettingLimit ?? "";
3173
3189
  message.entryFee = (object.entryFee !== undefined && object.entryFee !== null)
3174
3190
  ? Long.fromValue(object.entryFee)
3175
3191
  : Long.UZERO;
@@ -56097,6 +56113,17 @@ exports.GameVariant = void 0;
56097
56113
  GameVariant["TEXAS_HOLDEM"] = "texas-holdem";
56098
56114
  GameVariant["OMAHA"] = "omaha";
56099
56115
  })(exports.GameVariant || (exports.GameVariant = {}));
56116
+ /**
56117
+ * BettingLimit — the table's betting structure. Orthogonal to GameVariant: any
56118
+ * variant can be played at any limit (pot-limit Omaha, pot-limit Hold'em,
56119
+ * fixed-limit Hold'em, …). Absent on GameOptions ⇒ no-limit (current behavior).
56120
+ */
56121
+ exports.BettingLimit = void 0;
56122
+ (function (BettingLimit) {
56123
+ BettingLimit["NO_LIMIT"] = "no-limit";
56124
+ BettingLimit["POT_LIMIT"] = "pot-limit";
56125
+ BettingLimit["FIXED_LIMIT"] = "fixed-limit";
56126
+ })(exports.BettingLimit || (exports.BettingLimit = {}));
56100
56127
  exports.GameStatus = void 0;
56101
56128
  (function (GameStatus) {
56102
56129
  GameStatus["WAITING_FOR_PLAYERS"] = "waiting-for-players";
@@ -56468,6 +56495,30 @@ const parseGameVariant = (value) => {
56468
56495
  return "unknown";
56469
56496
  }
56470
56497
  };
56498
+ /**
56499
+ * The betting structure a table plays. The engine treats an absent
56500
+ * `gameOptions.bettingLimit` as no-limit (poker-vm#2652), so this mirrors that
56501
+ * rule rather than inventing a default (block52/pokerchain#407).
56502
+ *
56503
+ * @param options - the table's gameOptions (or anything carrying bettingLimit)
56504
+ * @returns the table's BettingLimit
56505
+ */
56506
+ const getBettingLimit = (options) => {
56507
+ return options?.bettingLimit ?? exports.BettingLimit.NO_LIMIT;
56508
+ };
56509
+ /**
56510
+ * Check whether a table is pot-limit (its max bet or raise is the pot cap).
56511
+ */
56512
+ const isPotLimit = (options) => {
56513
+ return getBettingLimit(options) === exports.BettingLimit.POT_LIMIT;
56514
+ };
56515
+ /**
56516
+ * How many hole cards a variant deals each player: 4 for Omaha, 2 for Texas
56517
+ * Hold'em. Mirrors the engine's HoleCardsPerPlayer (poker-vm#2650).
56518
+ */
56519
+ const holeCardsPerPlayer = (variant) => {
56520
+ return variant === exports.GameVariant.OMAHA ? 4 : 2;
56521
+ };
56471
56522
 
56472
56523
  /**
56473
56524
  * Check if a specific action is available in the legal actions list.
@@ -56723,6 +56774,41 @@ class PokerSolver {
56723
56774
  }
56724
56775
  return bestHand;
56725
56776
  }
56777
+ /**
56778
+ * Find the best Omaha hand: EXACTLY 2 of the 4 hole cards and EXACTLY 3 of
56779
+ * the 5 board cards — C(4,2) x C(5,3) = 60 candidates, ranked like Hold'em.
56780
+ * Mirrors the engine's FindBestOmahaHand (poker-vm#2650).
56781
+ */
56782
+ static findBestOmahaHand(holeCards, communityCards) {
56783
+ if (holeCards.length !== 4) {
56784
+ throw new Error("Omaha requires exactly 4 hole cards");
56785
+ }
56786
+ if (communityCards.length !== 5) {
56787
+ throw new Error("Omaha requires exactly 5 community cards");
56788
+ }
56789
+ let bestHand = null;
56790
+ for (const pair of this.getCombinationsFlexible(holeCards, 2)) {
56791
+ for (const triple of this.getCombinationsFlexible(communityCards, 3)) {
56792
+ const evaluation = this.evaluateHand([...pair, ...triple]);
56793
+ if (!bestHand || this.compareHands(evaluation, bestHand) > 0) {
56794
+ bestHand = evaluation;
56795
+ }
56796
+ }
56797
+ }
56798
+ if (!bestHand) {
56799
+ throw new Error("Failed to evaluate hand");
56800
+ }
56801
+ return bestHand;
56802
+ }
56803
+ /**
56804
+ * Best hand for a variant from hole cards and a full 5-card board.
56805
+ */
56806
+ static bestHandForVariant(variant, holeCards, communityCards) {
56807
+ if (variant === exports.GameVariant.OMAHA) {
56808
+ return this.findBestOmahaHand(holeCards, communityCards);
56809
+ }
56810
+ return this.findBestHand([...holeCards, ...communityCards]);
56811
+ }
56726
56812
  /**
56727
56813
  * Evaluate a hand with any number of cards (2-7)
56728
56814
  * This is useful for showing hand strength during all betting rounds
@@ -57117,12 +57203,14 @@ class PokerSolver {
57117
57203
  }
57118
57204
  /**
57119
57205
  * Calculate win percentages for multiple players (2+) using Monte Carlo simulation
57120
- * @param hands Array of hole cards for each player (each must have exactly 2 cards)
57206
+ * @param hands Array of hole cards for each player (2 each for Hold'em, 4 for Omaha)
57121
57207
  * @param communityCards Current community cards (0-5 cards)
57122
57208
  * @param simulations Number of simulations to run (default: 10000)
57209
+ * @param variant The table's variant; omitted = Texas Hold'em. Omaha hands are
57210
+ * scored with the two-plus-three rule (ui#769).
57123
57211
  * @returns Object containing win percentages for each player and overall tie percentage
57124
57212
  */
57125
- static calculateMultiPlayerEquity(hands, communityCards = [], simulations = 10000) {
57213
+ static calculateMultiPlayerEquity(hands, communityCards = [], simulations = 10000, variant) {
57126
57214
  // Validate inputs
57127
57215
  if (hands.length < 2) {
57128
57216
  throw new Error("Need at least 2 hands to calculate equity");
@@ -57130,9 +57218,10 @@ class PokerSolver {
57130
57218
  if (hands.length > 10) {
57131
57219
  throw new Error("Maximum 10 hands supported");
57132
57220
  }
57221
+ const holeCount = holeCardsPerPlayer(variant);
57133
57222
  for (let i = 0; i < hands.length; i++) {
57134
- if (hands[i].length !== 2) {
57135
- throw new Error(`Hand ${i} must have exactly 2 cards`);
57223
+ if (hands[i].length !== holeCount) {
57224
+ throw new Error(`Hand ${i} must have exactly ${holeCount} cards`);
57136
57225
  }
57137
57226
  }
57138
57227
  if (communityCards.length > 5) {
@@ -57159,10 +57248,7 @@ class PokerSolver {
57159
57248
  // Deal remaining community cards
57160
57249
  const fullCommunityCards = [...communityCards, ...shuffledDeck.slice(0, cardsNeeded)];
57161
57250
  // Evaluate all hands
57162
- const evaluations = hands.map(hand => {
57163
- const fullHand = [...hand, ...fullCommunityCards];
57164
- return this.findBestHand(fullHand);
57165
- });
57251
+ const evaluations = hands.map(hand => this.bestHandForVariant(variant, hand, fullCommunityCards));
57166
57252
  // Find winners using existing findWinners method
57167
57253
  const winnerIndices = this.findWinners(evaluations);
57168
57254
  // Update win counters
@@ -71025,9 +71111,14 @@ class SigningCosmosClient extends CosmosClient {
71025
71111
  return this.sendTokens(fromAddress, toAddress, amount, undefined, memo);
71026
71112
  }
71027
71113
  /**
71028
- * Create a new poker game
71114
+ * Create a new poker game.
71115
+ *
71116
+ * `bettingLimit` is the table's betting structure (block52/pokerchain#407).
71117
+ * Omit it for the variant's default: pot-limit for omaha (PLO), no-limit for
71118
+ * texas-holdem. Omitted, the field is not encoded at all, so a chain without
71119
+ * it accepts the tx unchanged. Fixed-limit is not playable yet (poker-vm#2654).
71029
71120
  */
71030
- async createGame(gameFormat, gameVariant, minPlayers, maxPlayers, minBuyInB52USDC, maxBuyInB52USDC, smallBlindB52USDC, bigBlindB52USDC, timeout, rakeConfig, sngConfig, name) {
71121
+ async createGame(gameFormat, gameVariant, minPlayers, maxPlayers, minBuyInB52USDC, maxBuyInB52USDC, smallBlindB52USDC, bigBlindB52USDC, timeout, rakeConfig, sngConfig, name, bettingLimit) {
71031
71122
  // Validate SNG/Tournament config BEFORE any network work — fail fast.
71032
71123
  // SNG/Tournament games are DEFINED by startingStack + blindLevelDuration.
71033
71124
  // Refuse to create one with missing/zero values rather than silently
@@ -71047,6 +71138,7 @@ class SigningCosmosClient extends CosmosClient {
71047
71138
  throw new Error(`${gameFormat} games require a positive blindLevelDuration; got ${sngConfig.blindLevelDuration}.`);
71048
71139
  }
71049
71140
  }
71141
+ assertCreatableBettingLimit(bettingLimit);
71050
71142
  await this.initializeSigningClient();
71051
71143
  if (!this.signingClient || !this.wallet) {
71052
71144
  throw new Error("Signing client not initialized");
@@ -71079,6 +71171,9 @@ class SigningCosmosClient extends CosmosClient {
71079
71171
  // Optional ENS-style table name (block52/pokerchain#337). Empty = unnamed
71080
71172
  // (no fee); a non-empty name costs $0.10/char and must be globally unique.
71081
71173
  name: name ?? "",
71174
+ // Betting structure (block52/pokerchain#407). "" = the variant's default
71175
+ // on chain, and an empty proto3 string is not encoded at all.
71176
+ bettingLimit: bettingLimit ?? "",
71082
71177
  });
71083
71178
  // Create the transaction message
71084
71179
  const msg = {
@@ -71098,6 +71193,7 @@ class SigningCosmosClient extends CosmosClient {
71098
71193
  smallBlind: smallBlindB52USDC.toString(),
71099
71194
  bigBlind: bigBlindB52USDC.toString(),
71100
71195
  timeout,
71196
+ ...(bettingLimit && { bettingLimit }),
71101
71197
  ...(rakeConfig && {
71102
71198
  rakeFreeThreshold: rakeConfig.rakeFreeThreshold.toString(),
71103
71199
  rakePercentage: rakeConfig.rakePercentage,
@@ -71974,6 +72070,21 @@ async function createSigningClientFromMnemonic(config, mnemonic) {
71974
72070
  });
71975
72071
  return createSigningCosmosClient(config, wallet);
71976
72072
  }
72073
+ /**
72074
+ * Throws for a betting limit a table cannot be created with (block52/pokerchain#407).
72075
+ * Undefined is allowed: the chain applies the variant's default.
72076
+ */
72077
+ function assertCreatableBettingLimit(bettingLimit) {
72078
+ if (bettingLimit === undefined) {
72079
+ return;
72080
+ }
72081
+ if (bettingLimit === exports.BettingLimit.FIXED_LIMIT) {
72082
+ throw new Error("Fixed-limit tables are not available yet (poker-vm#2654).");
72083
+ }
72084
+ if (bettingLimit !== exports.BettingLimit.NO_LIMIT && bettingLimit !== exports.BettingLimit.POT_LIMIT) {
72085
+ throw new Error(`Invalid betting limit: ${bettingLimit}.`);
72086
+ }
72087
+ }
71977
72088
 
71978
72089
  // Generated by Ignite ignite.com/cli
71979
72090
  const Client = IgniteClient.plugin([
@@ -72036,6 +72147,7 @@ exports.SigningCosmosClient = SigningCosmosClient;
72036
72147
  exports.TxExecutionError = TxExecutionError;
72037
72148
  exports.ValidatorBond = ValidatorBond;
72038
72149
  exports.WithdrawalRequest = WithdrawalRequest;
72150
+ exports.assertCreatableBettingLimit = assertCreatableBettingLimit;
72039
72151
  exports.assertExecuted = assertExecuted;
72040
72152
  exports.buildMsgCreateValidator = buildMsgCreateValidator;
72041
72153
  exports.computeGameNameFee = computeGameNameFee;
@@ -72047,14 +72159,17 @@ exports.formatGameVariantDisplay = formatGameVariantDisplay;
72047
72159
  exports.generateWallet = generateWallet;
72048
72160
  exports.getActionByType = getActionByType;
72049
72161
  exports.getAddressFromMnemonic = getAddressFromMnemonic;
72162
+ exports.getBettingLimit = getBettingLimit;
72050
72163
  exports.getDefaultCosmosConfig = getDefaultCosmosConfig;
72051
72164
  exports.getGameFormat = getGameFormat;
72052
72165
  exports.getGameFormatForCosmos = getGameFormatForCosmos;
72053
72166
  exports.getGameFormatFromObject = getGameFormatFromObject;
72054
72167
  exports.getGameVariant = getGameVariant;
72055
72168
  exports.hasAction = hasAction;
72169
+ exports.holeCardsPerPlayer = holeCardsPerPlayer;
72056
72170
  exports.isCashFormat = isCashFormat;
72057
72171
  exports.isEmptyAddress = isEmptyAddress;
72172
+ exports.isPotLimit = isPotLimit;
72058
72173
  exports.isSitAndGoFormat = isSitAndGoFormat;
72059
72174
  exports.isTournamentFormat = isTournamentFormat;
72060
72175
  exports.isValidMnemonic = isValidMnemonic;