@slot-engine/core 0.1.4 → 0.1.5

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.d.mts CHANGED
@@ -698,6 +698,70 @@ interface GameModeOpts {
698
698
  isBonusBuy: boolean;
699
699
  }
700
700
 
701
+ declare class WinType {
702
+ protected payout: number;
703
+ protected winCombinations: WinCombination[];
704
+ protected ctx: GameContext;
705
+ protected readonly wildSymbol?: WildSymbol;
706
+ constructor(opts: WinTypeOpts);
707
+ /**
708
+ * Implementation of win evaluation logic. Sets `this.payout` and `this.winCombinations`.
709
+ */
710
+ evaluateWins(board: Reels): this;
711
+ /**
712
+ * Custom post-processing of wins, e.g. for handling multipliers.
713
+ */
714
+ postProcess(func: PostProcessFn<typeof this.winCombinations>): this;
715
+ /**
716
+ * Returns the total payout and detailed win combinations.
717
+ */
718
+ getWins(): {
719
+ payout: number;
720
+ winCombinations: WinCombination[];
721
+ };
722
+ protected isWild(symbol: GameSymbol): boolean;
723
+ protected getSymbolPayout(symbol: GameSymbol, count: number): number;
724
+ }
725
+ interface WinTypeOpts {
726
+ /**
727
+ * A reference to the game context.
728
+ */
729
+ ctx: GameContext<any, any, any>;
730
+ /**
731
+ * Configuration used to identify wild symbols on the board.\
732
+ * You can either provide a specific `GameSymbol` instance or a set of properties to match against symbols on the board.
733
+ *
734
+ * @example
735
+ * If you have different wild symbols, each with a property `isWild: true`, you can define:
736
+ * ```ts
737
+ * wildSymbol: { isWild: true }
738
+ * ```
739
+ *
740
+ * @example
741
+ * If you have a single wild symbol instance, you can define:
742
+ * ```ts
743
+ * wildSymbol: myWildSymbol
744
+ * ```
745
+ */
746
+ wildSymbol?: WildSymbol;
747
+ }
748
+ type WinCombination = {
749
+ payout: number;
750
+ kind: number;
751
+ baseSymbol: GameSymbol;
752
+ symbols: Array<{
753
+ symbol: GameSymbol;
754
+ isWild: boolean;
755
+ substitutedFor?: GameSymbol;
756
+ reelIndex: number;
757
+ posIndex: number;
758
+ }>;
759
+ };
760
+ type PostProcessFn<TWinCombs extends WinCombination[]> = (wins: TWinCombs, ctx: GameContext) => {
761
+ winCombinations: TWinCombs;
762
+ };
763
+ type WildSymbol = GameSymbol | Record<string, any>;
764
+
701
765
  declare class GameService<TGameModes extends AnyGameModes = AnyGameModes, TSymbols extends AnySymbols = AnySymbols, TUserState extends AnyUserData = AnyUserData> extends AbstractService {
702
766
  constructor(ctx: () => GameContext<TGameModes, TSymbols, TUserState>);
703
767
  /**
@@ -736,6 +800,18 @@ declare class GameService<TGameModes extends AnyGameModes = AnyGameModes, TSymbo
736
800
  * Also sets `state.triggeredFreespins` to true.
737
801
  */
738
802
  awardFreespins(amount: number): void;
803
+ /**
804
+ * Dedupes win symbols.
805
+ *
806
+ * Since it may be possible that multiple win combinations include the same symbol (e.g. Wilds),\
807
+ * this method ensures that each symbol is only listed once.
808
+ *
809
+ * If you want to tumble based on winning symbols, run them through this method first.
810
+ */
811
+ dedupeWinSymbols(winCombinations: WinCombination[]): {
812
+ reelIdx: number;
813
+ rowIdx: number;
814
+ }[];
739
815
  }
740
816
 
741
817
  declare class Wallet {
@@ -1180,70 +1256,6 @@ declare const defineUserState: <TUserState extends AnyUserData>(data: TUserState
1180
1256
  declare const defineSymbols: <TSymbols extends AnySymbols>(symbols: TSymbols) => TSymbols;
1181
1257
  declare const defineGameModes: <TGameModes extends AnyGameModes>(gameModes: TGameModes) => TGameModes;
1182
1258
 
1183
- declare class WinType {
1184
- protected payout: number;
1185
- protected winCombinations: WinCombination[];
1186
- protected ctx: GameContext;
1187
- protected readonly wildSymbol?: WildSymbol;
1188
- constructor(opts: WinTypeOpts);
1189
- /**
1190
- * Implementation of win evaluation logic. Sets `this.payout` and `this.winCombinations`.
1191
- */
1192
- evaluateWins(board: Reels): this;
1193
- /**
1194
- * Custom post-processing of wins, e.g. for handling multipliers.
1195
- */
1196
- postProcess(func: PostProcessFn<typeof this.winCombinations>): this;
1197
- /**
1198
- * Returns the total payout and detailed win combinations.
1199
- */
1200
- getWins(): {
1201
- payout: number;
1202
- winCombinations: WinCombination[];
1203
- };
1204
- protected isWild(symbol: GameSymbol): boolean;
1205
- protected getSymbolPayout(symbol: GameSymbol, count: number): number;
1206
- }
1207
- interface WinTypeOpts {
1208
- /**
1209
- * A reference to the game context.
1210
- */
1211
- ctx: GameContext<any, any, any>;
1212
- /**
1213
- * Configuration used to identify wild symbols on the board.\
1214
- * You can either provide a specific `GameSymbol` instance or a set of properties to match against symbols on the board.
1215
- *
1216
- * @example
1217
- * If you have different wild symbols, each with a property `isWild: true`, you can define:
1218
- * ```ts
1219
- * wildSymbol: { isWild: true }
1220
- * ```
1221
- *
1222
- * @example
1223
- * If you have a single wild symbol instance, you can define:
1224
- * ```ts
1225
- * wildSymbol: myWildSymbol
1226
- * ```
1227
- */
1228
- wildSymbol?: WildSymbol;
1229
- }
1230
- type WinCombination = {
1231
- payout: number;
1232
- kind: number;
1233
- baseSymbol: GameSymbol;
1234
- symbols: Array<{
1235
- symbol: GameSymbol;
1236
- isWild: boolean;
1237
- substitutedFor?: GameSymbol;
1238
- reelIndex: number;
1239
- posIndex: number;
1240
- }>;
1241
- };
1242
- type PostProcessFn<TWinCombs extends WinCombination[]> = (wins: TWinCombs, ctx: GameContext) => {
1243
- winCombinations: TWinCombs;
1244
- };
1245
- type WildSymbol = GameSymbol | Record<string, any>;
1246
-
1247
1259
  declare class LinesWinType extends WinType {
1248
1260
  protected lines: Record<number, number[]>;
1249
1261
  protected winCombinations: LineWinCombination[];
package/dist/index.d.ts CHANGED
@@ -698,6 +698,70 @@ interface GameModeOpts {
698
698
  isBonusBuy: boolean;
699
699
  }
700
700
 
701
+ declare class WinType {
702
+ protected payout: number;
703
+ protected winCombinations: WinCombination[];
704
+ protected ctx: GameContext;
705
+ protected readonly wildSymbol?: WildSymbol;
706
+ constructor(opts: WinTypeOpts);
707
+ /**
708
+ * Implementation of win evaluation logic. Sets `this.payout` and `this.winCombinations`.
709
+ */
710
+ evaluateWins(board: Reels): this;
711
+ /**
712
+ * Custom post-processing of wins, e.g. for handling multipliers.
713
+ */
714
+ postProcess(func: PostProcessFn<typeof this.winCombinations>): this;
715
+ /**
716
+ * Returns the total payout and detailed win combinations.
717
+ */
718
+ getWins(): {
719
+ payout: number;
720
+ winCombinations: WinCombination[];
721
+ };
722
+ protected isWild(symbol: GameSymbol): boolean;
723
+ protected getSymbolPayout(symbol: GameSymbol, count: number): number;
724
+ }
725
+ interface WinTypeOpts {
726
+ /**
727
+ * A reference to the game context.
728
+ */
729
+ ctx: GameContext<any, any, any>;
730
+ /**
731
+ * Configuration used to identify wild symbols on the board.\
732
+ * You can either provide a specific `GameSymbol` instance or a set of properties to match against symbols on the board.
733
+ *
734
+ * @example
735
+ * If you have different wild symbols, each with a property `isWild: true`, you can define:
736
+ * ```ts
737
+ * wildSymbol: { isWild: true }
738
+ * ```
739
+ *
740
+ * @example
741
+ * If you have a single wild symbol instance, you can define:
742
+ * ```ts
743
+ * wildSymbol: myWildSymbol
744
+ * ```
745
+ */
746
+ wildSymbol?: WildSymbol;
747
+ }
748
+ type WinCombination = {
749
+ payout: number;
750
+ kind: number;
751
+ baseSymbol: GameSymbol;
752
+ symbols: Array<{
753
+ symbol: GameSymbol;
754
+ isWild: boolean;
755
+ substitutedFor?: GameSymbol;
756
+ reelIndex: number;
757
+ posIndex: number;
758
+ }>;
759
+ };
760
+ type PostProcessFn<TWinCombs extends WinCombination[]> = (wins: TWinCombs, ctx: GameContext) => {
761
+ winCombinations: TWinCombs;
762
+ };
763
+ type WildSymbol = GameSymbol | Record<string, any>;
764
+
701
765
  declare class GameService<TGameModes extends AnyGameModes = AnyGameModes, TSymbols extends AnySymbols = AnySymbols, TUserState extends AnyUserData = AnyUserData> extends AbstractService {
702
766
  constructor(ctx: () => GameContext<TGameModes, TSymbols, TUserState>);
703
767
  /**
@@ -736,6 +800,18 @@ declare class GameService<TGameModes extends AnyGameModes = AnyGameModes, TSymbo
736
800
  * Also sets `state.triggeredFreespins` to true.
737
801
  */
738
802
  awardFreespins(amount: number): void;
803
+ /**
804
+ * Dedupes win symbols.
805
+ *
806
+ * Since it may be possible that multiple win combinations include the same symbol (e.g. Wilds),\
807
+ * this method ensures that each symbol is only listed once.
808
+ *
809
+ * If you want to tumble based on winning symbols, run them through this method first.
810
+ */
811
+ dedupeWinSymbols(winCombinations: WinCombination[]): {
812
+ reelIdx: number;
813
+ rowIdx: number;
814
+ }[];
739
815
  }
740
816
 
741
817
  declare class Wallet {
@@ -1180,70 +1256,6 @@ declare const defineUserState: <TUserState extends AnyUserData>(data: TUserState
1180
1256
  declare const defineSymbols: <TSymbols extends AnySymbols>(symbols: TSymbols) => TSymbols;
1181
1257
  declare const defineGameModes: <TGameModes extends AnyGameModes>(gameModes: TGameModes) => TGameModes;
1182
1258
 
1183
- declare class WinType {
1184
- protected payout: number;
1185
- protected winCombinations: WinCombination[];
1186
- protected ctx: GameContext;
1187
- protected readonly wildSymbol?: WildSymbol;
1188
- constructor(opts: WinTypeOpts);
1189
- /**
1190
- * Implementation of win evaluation logic. Sets `this.payout` and `this.winCombinations`.
1191
- */
1192
- evaluateWins(board: Reels): this;
1193
- /**
1194
- * Custom post-processing of wins, e.g. for handling multipliers.
1195
- */
1196
- postProcess(func: PostProcessFn<typeof this.winCombinations>): this;
1197
- /**
1198
- * Returns the total payout and detailed win combinations.
1199
- */
1200
- getWins(): {
1201
- payout: number;
1202
- winCombinations: WinCombination[];
1203
- };
1204
- protected isWild(symbol: GameSymbol): boolean;
1205
- protected getSymbolPayout(symbol: GameSymbol, count: number): number;
1206
- }
1207
- interface WinTypeOpts {
1208
- /**
1209
- * A reference to the game context.
1210
- */
1211
- ctx: GameContext<any, any, any>;
1212
- /**
1213
- * Configuration used to identify wild symbols on the board.\
1214
- * You can either provide a specific `GameSymbol` instance or a set of properties to match against symbols on the board.
1215
- *
1216
- * @example
1217
- * If you have different wild symbols, each with a property `isWild: true`, you can define:
1218
- * ```ts
1219
- * wildSymbol: { isWild: true }
1220
- * ```
1221
- *
1222
- * @example
1223
- * If you have a single wild symbol instance, you can define:
1224
- * ```ts
1225
- * wildSymbol: myWildSymbol
1226
- * ```
1227
- */
1228
- wildSymbol?: WildSymbol;
1229
- }
1230
- type WinCombination = {
1231
- payout: number;
1232
- kind: number;
1233
- baseSymbol: GameSymbol;
1234
- symbols: Array<{
1235
- symbol: GameSymbol;
1236
- isWild: boolean;
1237
- substitutedFor?: GameSymbol;
1238
- reelIndex: number;
1239
- posIndex: number;
1240
- }>;
1241
- };
1242
- type PostProcessFn<TWinCombs extends WinCombination[]> = (wins: TWinCombs, ctx: GameContext) => {
1243
- winCombinations: TWinCombs;
1244
- };
1245
- type WildSymbol = GameSymbol | Record<string, any>;
1246
-
1247
1259
  declare class LinesWinType extends WinType {
1248
1260
  protected lines: Record<number, number[]>;
1249
1261
  protected winCombinations: LineWinCombination[];
package/dist/index.js CHANGED
@@ -772,6 +772,9 @@ var Board = class {
772
772
  newPaddingTopSymbols[ridx].unshift(padSymbol);
773
773
  }
774
774
  }
775
+ this.lastDrawnReelStops = this.lastDrawnReelStops.map((stop, ridx) => {
776
+ return newFirstSymbolPositions[ridx] ?? stop;
777
+ });
775
778
  return {
776
779
  newBoardSymbols,
777
780
  newPaddingTopSymbols
@@ -1117,6 +1120,27 @@ var GameService = class extends AbstractService {
1117
1120
  this.ctx().state.totalFreespinAmount += amount;
1118
1121
  this.ctx().state.triggeredFreespins = true;
1119
1122
  }
1123
+ /**
1124
+ * Dedupes win symbols.
1125
+ *
1126
+ * Since it may be possible that multiple win combinations include the same symbol (e.g. Wilds),\
1127
+ * this method ensures that each symbol is only listed once.
1128
+ *
1129
+ * If you want to tumble based on winning symbols, run them through this method first.
1130
+ */
1131
+ dedupeWinSymbols(winCombinations) {
1132
+ const symbolsMap = /* @__PURE__ */ new Map();
1133
+ winCombinations.forEach((wc) => {
1134
+ wc.symbols.forEach((s) => {
1135
+ symbolsMap.set(`${s.reelIndex},${s.posIndex}`, {
1136
+ reelIdx: s.reelIndex,
1137
+ rowIdx: s.posIndex
1138
+ });
1139
+ });
1140
+ });
1141
+ const symbolsToRemove = Array.from(symbolsMap.values());
1142
+ return symbolsToRemove;
1143
+ }
1120
1144
  };
1121
1145
 
1122
1146
  // src/service/wallet.ts
@@ -1731,7 +1755,12 @@ Simulating game mode: ${mode}`);
1731
1755
  }
1732
1756
  });
1733
1757
  worker.on("error", (error) => {
1734
- console.error("Error:", error);
1758
+ process.stdout.write(`
1759
+ ${error.message}
1760
+ `);
1761
+ process.stdout.write(`
1762
+ ${error.stack}
1763
+ `);
1735
1764
  reject(error);
1736
1765
  });
1737
1766
  worker.on("exit", (code) => {