@betorigami/game-engine 0.0.0-sha-d6d1f10-20260907014734
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.cjs +1 -0
- package/dist/index.d.ts +267 -0
- package/dist/index.mjs +1 -0
- package/dist/testing/index.cjs +1 -0
- package/dist/testing/index.d.ts +75 -0
- package/dist/testing/index.mjs +1 -0
- package/package.json +47 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var v=Object.create;var{getPrototypeOf:L,defineProperty:a,getOwnPropertyNames:I,getOwnPropertyDescriptor:k}=Object,f=Object.prototype.hasOwnProperty;var m=(e,t,r)=>{r=e!=null?v(L(e)):{};let i=t||!e||!e.__esModule?a(r,"default",{value:e,enumerable:!0}):r;for(let u of I(e))if(!f.call(i,u))a(i,u,{get:()=>e[u],enumerable:!0});return i},T=new WeakMap,P=(e)=>{var t=T.get(e),r;if(t)return t;if(t=a({},"__esModule",{value:!0}),e&&typeof e==="object"||typeof e==="function")I(e).map((i)=>!f.call(t,i)&&a(t,i,{get:()=>e[i],enumerable:!(r=k(e,i))||r.enumerable}));return T.set(e,t),t};var K=(e,t)=>{for(var r in t)a(e,r,{get:t[r],enumerable:!0,configurable:!0,set:(i)=>t[r]=()=>i})};var z={};K(z,{sumPayout:()=>X,rtpOf:()=>J,probabilityOf:()=>W,nCr:()=>j,maxMultiplierOf:()=>Q,bigMin:()=>q,bigMax:()=>Z,SingleActionGame:()=>B,RandomValuePool:()=>x,OrigamiGame:()=>M,MultiplierContainer:()=>G,MultiplierCalculator:()=>N,MultiActionGame:()=>h,MIN_CARD_VALUE:()=>$,MAX_CARD_VALUE:()=>w,GameErrorCode:()=>O,GameError:()=>o,FACE_CARD_RANKS:()=>F,Card:()=>S,CARD_VALUES:()=>b,CARD_SUIT_COLORS:()=>H,CARD_SUITS:()=>R,CARD_RANKS:()=>C});module.exports=P(z);var d=require("@betorigami/guard");var $=0,w=51,b=Array.from({length:52},(e,t)=>t),R=["Spades","Hearts","Diamonds","Clubs"],C=["Ace","Two","Three","Four","Five","Six","Seven","Eight","Nine","Ten","Jack","Queen","King"],F=["Jack","Queen","King"],H={Hearts:"red",Diamonds:"red",Spades:"black",Clubs:"black"};class S{value;constructor(e){let t=b[e];d.Guard.againstNullOrUndefined(t,`Invalid card value: ${e}`),this.value=t}get suit(){let e=Math.floor(this.value/13),t=R[e];return d.Guard.againstNullOrUndefined(t,`Invalid suit index: ${e}`),t}get rank(){let e=this.value%13,t=C[e];return d.Guard.againstNullOrUndefined(t,`Invalid rank index: ${e}`),t}toDto(){return{rank:this.rank,suit:this.suit,cardValue:this.value}}}var O;((r)=>{r.UNSUPPORTED_EDGE="UNSUPPORTED_EDGE";r.INPUT_VALIDATION_ERROR="INPUT_VALIDATION_ERROR"})(O||={});class o extends Error{static createUnsupportedEdgeError(e){return new o("UNSUPPORTED_EDGE",e)}static createValidationError(e){return new o("INPUT_VALIDATION_ERROR",e)}static isGameError(e){return e instanceof o}errorCode;constructor(e,t){super(t);this.errorCode=e}}var E=require("@betorigami/guard"),y=m(require("fast-deep-equal"));class h{gameId;edge;constructor(e,t){this.gameId=e;this.edge=t;this.validateEdge(t)}play(e,t,r=[]){let i=this.createGameStore(),u=this.generateRandomValues(e),p=u.toMultipleRandomValues();for(let U of r)i.send({...U.playerArgument,randomValuePool:u});let D=r[r.length-1];if(D)E.Guard.against(!y.default(D.state,this.toExternalState(i.getSnapshot().context.state)),"The final state of the game does not match the expected state based on the previous actions");i.send({...t,randomValuePool:u});let l=i.getSnapshot().context;return{playerArgument:l.playerArgument,state:this.toExternalState(l.state),config:l.config,additionalBetAmount:l.additionalBetAmount,proof:{proofHash:p.proofHash,proofString:p.proofString}}}validateEdge(e){let t=this.supportedEdgeConfigurations.includes(e);E.Guard.against(!t,()=>o.createUnsupportedEdgeError(`${this.gameId} does not support ${e}% edge. Supported edge configurations include: ${this.supportedEdgeConfigurations}`))}}class G{multipliers;constructor(e){this.multipliers=e}getMultipliers(e){let t=this.multipliers.get(e);if(t===void 0)throw new Error(`Multipliers for ${e} not found`);return t}getAllMultipliers(){return Array.from(this.multipliers.entries()).map(([e,t])=>({edge:e,multipliers:t}))}}var M;((n)=>{n.DICE="DICE";n.MINES="MINES";n.KENO="KENO";n.LIMBO="LIMBO";n.ADVANCED_DICE="ADVANCED_DICE";n.BACCARAT="BACCARAT";n.DIAMONDS="DIAMONDS";n.PLINKO="PLINKO";n.ROULETTE="ROULETTE";n.WHEEL="WHEEL";n.BLACKJACK="BLACKJACK";n.BLITZ="BLITZ";n.DARTS="DARTS"})(M||={});var A=m(require("big.js")),c=require("@betorigami/guard"),J=({outcomes:e,totalWeight:t})=>{return c.Guard.against(t<=0,"An outcome distribution must have a positive total weight"),e.reduce((r,{multiplier:i,weight:u})=>r.plus(i.times(u)),A.default(0)).div(t)},Q=({outcomes:e})=>{return c.Guard.againstEmptyArray(e,"An outcome distribution must have at least one outcome"),e.reduce((t,{multiplier:r})=>r.gt(t)?r:t,e[0].multiplier)},W=({outcomes:e,totalWeight:t},r)=>{c.Guard.against(t<=0,"An outcome distribution must have a positive total weight");let i=e.filter((u)=>u.slot===r).reduce((u,p)=>u+p.weight,0);return A.default(i).div(t)};var V=m(require("big.js")),X=(e)=>e.legs.reduce((t,r)=>t.add(r.amount),new V.default("0"));var s=require("@betorigami/guard");class x{source;index=0;constructor(e){this.source=e}static from(e,t){return s.Guard.against(t<=0,`Count must be positive, got ${t}`),s.Guard.against(e.values.length!==t,`Expected ${t} values, got ${e.values.length}`),new x(e)}get consumedValues(){return this.source.values.slice(0,this.index)}toMultipleRandomValues(){return this.source}takeOne(){let e=this.source.values[this.index];return s.Guard.againstUndefined(e,"RandomValuePool exhausted"),this.index+=1,e}take(e){s.Guard.against(this.index+e>this.source.values.length,`RandomValuePool exhausted: requested ${e} but only ${this.source.values.length-this.index} remain`);let t=this.source.values.slice(this.index,this.index+e);return this.index+=e,t}}var _=require("@betorigami/guard");class B{gameId;constructor(e){this.gameId=e}getGameResult(e){let t=this.supportedEdgeConfigurations.includes(e.edge);return _.Guard.against(!t,()=>o.createUnsupportedEdgeError(`${this.gameId} does not support ${e.edge}% edge. Supported edge configurations include: ${this.supportedEdgeConfigurations}`)),this.determineGameResult(e)}}var Z=(e,t)=>{return e.gt(t)?e:t};var q=(e,t)=>{return e.lt(t)?e:t};var g=m(require("big.js"));class N{static withMultiplier(e,t){return new g.default(e).times(new g.default(100-t).div(100))}static withWinChance(e,t){return g.default(100-e).div(t)}}var j=(e,t)=>{if(t<0||e<0||t>e)throw new Error("Invalid input: ensure 0 ≤ r ≤ n and n ≥ 0.");if(t>e-t)t=e-t;let r=1;for(let i=1;i<=t;i++)r=r*(e-i+1)/i;return r};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
// Generated by dts-bundle-generator v9.5.1
|
|
2
|
+
|
|
3
|
+
import Big$1 from 'big.js';
|
|
4
|
+
|
|
5
|
+
export declare const MIN_CARD_VALUE = 0;
|
|
6
|
+
export declare const MAX_CARD_VALUE = 51;
|
|
7
|
+
export declare const CARD_VALUES: number[];
|
|
8
|
+
export declare const CARD_SUITS: readonly [
|
|
9
|
+
"Spades",
|
|
10
|
+
"Hearts",
|
|
11
|
+
"Diamonds",
|
|
12
|
+
"Clubs"
|
|
13
|
+
];
|
|
14
|
+
export type CardSuit = (typeof CARD_SUITS)[number];
|
|
15
|
+
export declare const CARD_RANKS: readonly [
|
|
16
|
+
"Ace",
|
|
17
|
+
"Two",
|
|
18
|
+
"Three",
|
|
19
|
+
"Four",
|
|
20
|
+
"Five",
|
|
21
|
+
"Six",
|
|
22
|
+
"Seven",
|
|
23
|
+
"Eight",
|
|
24
|
+
"Nine",
|
|
25
|
+
"Ten",
|
|
26
|
+
"Jack",
|
|
27
|
+
"Queen",
|
|
28
|
+
"King"
|
|
29
|
+
];
|
|
30
|
+
export type CardRank = (typeof CARD_RANKS)[number];
|
|
31
|
+
export declare const FACE_CARD_RANKS: readonly CardRank[];
|
|
32
|
+
export type CardValue = (typeof CARD_VALUES)[number];
|
|
33
|
+
export type CardDto = {
|
|
34
|
+
rank: CardRank;
|
|
35
|
+
suit: CardSuit;
|
|
36
|
+
cardValue: CardValue;
|
|
37
|
+
};
|
|
38
|
+
export declare const CARD_SUIT_COLORS: Record<CardSuit, "red" | "black">;
|
|
39
|
+
export declare class Card {
|
|
40
|
+
readonly value: CardValue;
|
|
41
|
+
constructor(rawValue: number);
|
|
42
|
+
get suit(): CardSuit;
|
|
43
|
+
get rank(): CardRank;
|
|
44
|
+
toDto(): CardDto;
|
|
45
|
+
}
|
|
46
|
+
export declare enum GameErrorCode {
|
|
47
|
+
UNSUPPORTED_EDGE = "UNSUPPORTED_EDGE",
|
|
48
|
+
INPUT_VALIDATION_ERROR = "INPUT_VALIDATION_ERROR"
|
|
49
|
+
}
|
|
50
|
+
export declare class GameError extends Error {
|
|
51
|
+
static createUnsupportedEdgeError(message: string): GameError;
|
|
52
|
+
static createValidationError(message: string): GameError;
|
|
53
|
+
static isGameError(error: unknown): error is GameError;
|
|
54
|
+
errorCode: GameErrorCode;
|
|
55
|
+
constructor(errorCode: GameErrorCode, message?: string);
|
|
56
|
+
}
|
|
57
|
+
export declare enum OrigamiGame {
|
|
58
|
+
DICE = "DICE",
|
|
59
|
+
MINES = "MINES",
|
|
60
|
+
KENO = "KENO",
|
|
61
|
+
LIMBO = "LIMBO",
|
|
62
|
+
ADVANCED_DICE = "ADVANCED_DICE",
|
|
63
|
+
BACCARAT = "BACCARAT",
|
|
64
|
+
DIAMONDS = "DIAMONDS",
|
|
65
|
+
PLINKO = "PLINKO",
|
|
66
|
+
ROULETTE = "ROULETTE",
|
|
67
|
+
WHEEL = "WHEEL",
|
|
68
|
+
BLACKJACK = "BLACKJACK",
|
|
69
|
+
BLITZ = "BLITZ",
|
|
70
|
+
DARTS = "DARTS"
|
|
71
|
+
}
|
|
72
|
+
export interface Game {
|
|
73
|
+
gameId: OrigamiGame;
|
|
74
|
+
supportedEdgeConfigurations: [
|
|
75
|
+
number,
|
|
76
|
+
...number[]
|
|
77
|
+
];
|
|
78
|
+
}
|
|
79
|
+
export type Proof = {
|
|
80
|
+
proofHash: string;
|
|
81
|
+
proofString: string;
|
|
82
|
+
};
|
|
83
|
+
export type SingleRandomValue = {
|
|
84
|
+
value: number;
|
|
85
|
+
values: number[];
|
|
86
|
+
count: 1;
|
|
87
|
+
min: number;
|
|
88
|
+
max: number;
|
|
89
|
+
} & Proof;
|
|
90
|
+
export type MultipleRandomValues = {
|
|
91
|
+
values: number[];
|
|
92
|
+
count: number;
|
|
93
|
+
min: number;
|
|
94
|
+
max: number;
|
|
95
|
+
} & Proof;
|
|
96
|
+
export interface RandomNumberGenerator {
|
|
97
|
+
getRandomInt({ min, max }: {
|
|
98
|
+
min: number;
|
|
99
|
+
max: number;
|
|
100
|
+
}): SingleRandomValue;
|
|
101
|
+
getUniqueRandomInts({ min, max, count }: {
|
|
102
|
+
min: number;
|
|
103
|
+
max: number;
|
|
104
|
+
count: number;
|
|
105
|
+
}): MultipleRandomValues;
|
|
106
|
+
getRandomIntsWithReplacement({ min, max, count }: {
|
|
107
|
+
min: number;
|
|
108
|
+
max: number;
|
|
109
|
+
count: number;
|
|
110
|
+
}): MultipleRandomValues;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* A single exact payout amount from a game round.
|
|
114
|
+
*
|
|
115
|
+
* A round returns zero or more legs. Neither their order nor their count is
|
|
116
|
+
* significant: a leg is not tied to a particular bet, an amount may be zero,
|
|
117
|
+
* and the number of legs need not match the number of bets placed. The only
|
|
118
|
+
* supported operation is summing them.
|
|
119
|
+
*
|
|
120
|
+
* Amounts are gross — they include returned stake — and unrounded. Rounding to
|
|
121
|
+
* a currency's precision is the consuming service's decision, not this
|
|
122
|
+
* package's.
|
|
123
|
+
*/
|
|
124
|
+
export type PayoutLeg = Readonly<{
|
|
125
|
+
amount: Big$1;
|
|
126
|
+
}>;
|
|
127
|
+
/**
|
|
128
|
+
* A round's payout, expressed as its constituent legs.
|
|
129
|
+
*/
|
|
130
|
+
export type Payout = Readonly<{
|
|
131
|
+
legs: readonly PayoutLeg[];
|
|
132
|
+
}>;
|
|
133
|
+
/**
|
|
134
|
+
* Totals a round's payout.
|
|
135
|
+
*
|
|
136
|
+
* Summing the legs is lossless; reconstructing the total from a rounded
|
|
137
|
+
* payout multiplier is not, so this is the only supported way to arrive at
|
|
138
|
+
* what a round pays.
|
|
139
|
+
*/
|
|
140
|
+
export declare const sumPayout: (payout: Payout) => Big$1;
|
|
141
|
+
export type EmptyGameOutput = {};
|
|
142
|
+
export interface GetGameResultRequest<TGameInputs> {
|
|
143
|
+
generator: RandomNumberGenerator;
|
|
144
|
+
edge: number;
|
|
145
|
+
gameInputs: TGameInputs;
|
|
146
|
+
}
|
|
147
|
+
export interface GameResult<TGameOutputs = EmptyGameOutput> {
|
|
148
|
+
isFinished: boolean;
|
|
149
|
+
payout: Payout;
|
|
150
|
+
payoutMultiplier: Big$1;
|
|
151
|
+
outputs: TGameOutputs & {
|
|
152
|
+
result: number[];
|
|
153
|
+
};
|
|
154
|
+
randomValues: SingleRandomValue | MultipleRandomValues;
|
|
155
|
+
}
|
|
156
|
+
export declare abstract class SingleActionGame<TGameInputs, TGameOutputs = EmptyGameOutput> implements Game {
|
|
157
|
+
readonly gameId: OrigamiGame;
|
|
158
|
+
protected constructor(gameId: OrigamiGame);
|
|
159
|
+
abstract get supportedEdgeConfigurations(): [
|
|
160
|
+
number,
|
|
161
|
+
...number[]
|
|
162
|
+
];
|
|
163
|
+
abstract getBetAmount(gameInputs: TGameInputs): Big$1;
|
|
164
|
+
getGameResult(request: GetGameResultRequest<TGameInputs>): GameResult<TGameOutputs>;
|
|
165
|
+
protected abstract determineGameResult(request: GetGameResultRequest<TGameInputs>): GameResult<TGameOutputs>;
|
|
166
|
+
}
|
|
167
|
+
export interface GameDescriptor<TInputs, TOutputs = EmptyGameOutput> {
|
|
168
|
+
readonly id: OrigamiGame;
|
|
169
|
+
readonly create: () => SingleActionGame<TInputs, TOutputs>;
|
|
170
|
+
readonly suppliesOwnBetAmount: boolean;
|
|
171
|
+
}
|
|
172
|
+
export declare class RandomValuePool {
|
|
173
|
+
private readonly source;
|
|
174
|
+
private index;
|
|
175
|
+
private constructor();
|
|
176
|
+
static from(result: MultipleRandomValues, count: number): RandomValuePool;
|
|
177
|
+
get consumedValues(): number[];
|
|
178
|
+
toMultipleRandomValues(): MultipleRandomValues;
|
|
179
|
+
takeOne(): number;
|
|
180
|
+
take(count: number): ReadonlyArray<number>;
|
|
181
|
+
}
|
|
182
|
+
export type BaseInProgressState = Readonly<{
|
|
183
|
+
isFinished: false;
|
|
184
|
+
multiplier: Big$1;
|
|
185
|
+
}>;
|
|
186
|
+
export type BaseFinishedState = Readonly<{
|
|
187
|
+
isFinished: true;
|
|
188
|
+
payout: Payout;
|
|
189
|
+
multiplier: Big$1;
|
|
190
|
+
randomValues: number[];
|
|
191
|
+
}>;
|
|
192
|
+
export type BaseGameState = BaseInProgressState | BaseFinishedState;
|
|
193
|
+
export type GameContext<TAction, TState extends BaseGameState, TConfig> = {
|
|
194
|
+
readonly playerArgument: TAction;
|
|
195
|
+
readonly state: TState;
|
|
196
|
+
readonly config: TConfig;
|
|
197
|
+
readonly additionalBetAmount?: Big$1;
|
|
198
|
+
};
|
|
199
|
+
export type RandomValuePoolEvent = Readonly<{
|
|
200
|
+
randomValuePool: RandomValuePool;
|
|
201
|
+
}>;
|
|
202
|
+
export type UntypedGameContext = GameContext<{
|
|
203
|
+
readonly type: string;
|
|
204
|
+
}, BaseGameState, unknown>;
|
|
205
|
+
export type PlayResult = UntypedGameContext & {
|
|
206
|
+
readonly proof: Proof;
|
|
207
|
+
};
|
|
208
|
+
export interface GameStore<TInternalState extends BaseGameState, TAction, TConfig> {
|
|
209
|
+
send(event: {
|
|
210
|
+
readonly type: string;
|
|
211
|
+
} & RandomValuePoolEvent & Record<string, unknown>): void;
|
|
212
|
+
getSnapshot(): {
|
|
213
|
+
readonly context: GameContext<TAction, TInternalState, TConfig>;
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
export declare abstract class MultiActionGame<TInternalState extends BaseGameState, TExternalState extends BaseGameState, TAction extends {
|
|
217
|
+
readonly type: string;
|
|
218
|
+
}, TConfig> implements Game {
|
|
219
|
+
readonly gameId: OrigamiGame;
|
|
220
|
+
readonly edge: number;
|
|
221
|
+
protected constructor(gameId: OrigamiGame, edge: number);
|
|
222
|
+
abstract get supportedEdgeConfigurations(): [
|
|
223
|
+
number,
|
|
224
|
+
...number[]
|
|
225
|
+
];
|
|
226
|
+
abstract getValidNextActionTypes(state: TExternalState): ReadonlyArray<TAction["type"]>;
|
|
227
|
+
protected abstract createGameStore(): GameStore<TInternalState, TAction, TConfig>;
|
|
228
|
+
protected abstract generateRandomValues(generator: RandomNumberGenerator): RandomValuePool;
|
|
229
|
+
protected abstract toExternalState(internal: TInternalState): TExternalState;
|
|
230
|
+
play(generator: RandomNumberGenerator, action: {
|
|
231
|
+
readonly type: string;
|
|
232
|
+
} & Record<string, unknown>, previousActions?: readonly UntypedGameContext[]): PlayResult;
|
|
233
|
+
private validateEdge;
|
|
234
|
+
}
|
|
235
|
+
export declare class MultiplierContainer<TMultipliers> {
|
|
236
|
+
readonly multipliers: Map<number, TMultipliers>;
|
|
237
|
+
constructor(multipliers: Map<number, TMultipliers>);
|
|
238
|
+
getMultipliers(edge: number): TMultipliers;
|
|
239
|
+
getAllMultipliers(): {
|
|
240
|
+
edge: number;
|
|
241
|
+
multipliers: TMultipliers;
|
|
242
|
+
}[];
|
|
243
|
+
}
|
|
244
|
+
export type Outcome<TSlot extends string = string> = Readonly<{
|
|
245
|
+
slot: TSlot;
|
|
246
|
+
multiplier: Big$1;
|
|
247
|
+
weight: number;
|
|
248
|
+
}>;
|
|
249
|
+
export type OutcomeDistribution<TSlot extends string = string> = Readonly<{
|
|
250
|
+
outcomes: readonly Outcome<TSlot>[];
|
|
251
|
+
totalWeight: number;
|
|
252
|
+
}>;
|
|
253
|
+
export interface DescribesOutcomes<TSelection, TSlot extends string = string> {
|
|
254
|
+
describeOutcomes(selection: TSelection, edge: number): OutcomeDistribution<TSlot>;
|
|
255
|
+
}
|
|
256
|
+
export declare const rtpOf: ({ outcomes, totalWeight }: OutcomeDistribution) => Big$1;
|
|
257
|
+
export declare const maxMultiplierOf: ({ outcomes }: OutcomeDistribution) => Big$1;
|
|
258
|
+
export declare const probabilityOf: ({ outcomes, totalWeight }: OutcomeDistribution, slot: string) => Big$1;
|
|
259
|
+
export declare const bigMax: (a: Big$1, b: Big$1) => Big$1;
|
|
260
|
+
export declare const bigMin: (a: Big$1, b: Big$1) => Big$1;
|
|
261
|
+
export declare abstract class MultiplierCalculator {
|
|
262
|
+
static withMultiplier(multiplier: Big$1, edge: number): Big$1;
|
|
263
|
+
static withWinChance(edge: number, winChance: Big$1): Big$1;
|
|
264
|
+
}
|
|
265
|
+
export declare const nCr: (n: number, r: number) => number;
|
|
266
|
+
|
|
267
|
+
export {};
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{Guard as l}from"@betorigami/guard";var V=0,_=51,g=Array.from({length:52},(e,t)=>t),b=["Spades","Hearts","Diamonds","Clubs"],R=["Ace","Two","Three","Four","Five","Six","Seven","Eight","Nine","Ten","Jack","Queen","King"],B=["Jack","Queen","King"],N={Hearts:"red",Diamonds:"red",Spades:"black",Clubs:"black"};class D{value;constructor(e){let t=g[e];l.againstNullOrUndefined(t,`Invalid card value: ${e}`),this.value=t}get suit(){let e=Math.floor(this.value/13),t=b[e];return l.againstNullOrUndefined(t,`Invalid suit index: ${e}`),t}get rank(){let e=this.value%13,t=R[e];return l.againstNullOrUndefined(t,`Invalid rank index: ${e}`),t}toDto(){return{rank:this.rank,suit:this.suit,cardValue:this.value}}}var T;((r)=>{r.UNSUPPORTED_EDGE="UNSUPPORTED_EDGE";r.INPUT_VALIDATION_ERROR="INPUT_VALIDATION_ERROR"})(T||={});class o extends Error{static createUnsupportedEdgeError(e){return new o("UNSUPPORTED_EDGE",e)}static createValidationError(e){return new o("INPUT_VALIDATION_ERROR",e)}static isGameError(e){return e instanceof o}errorCode;constructor(e,t){super(t);this.errorCode=e}}import{Guard as C}from"@betorigami/guard";import I from"fast-deep-equal";class f{gameId;edge;constructor(e,t){this.gameId=e;this.edge=t;this.validateEdge(t)}play(e,t,r=[]){let i=this.createGameStore(),n=this.generateRandomValues(e),a=n.toMultipleRandomValues();for(let x of r)i.send({...x.playerArgument,randomValuePool:n});let c=r[r.length-1];if(c)C.against(!I(c.state,this.toExternalState(i.getSnapshot().context.state)),"The final state of the game does not match the expected state based on the previous actions");i.send({...t,randomValuePool:n});let s=i.getSnapshot().context;return{playerArgument:s.playerArgument,state:this.toExternalState(s.state),config:s.config,additionalBetAmount:s.additionalBetAmount,proof:{proofHash:a.proofHash,proofString:a.proofString}}}validateEdge(e){let t=this.supportedEdgeConfigurations.includes(e);C.against(!t,()=>o.createUnsupportedEdgeError(`${this.gameId} does not support ${e}% edge. Supported edge configurations include: ${this.supportedEdgeConfigurations}`))}}class S{multipliers;constructor(e){this.multipliers=e}getMultipliers(e){let t=this.multipliers.get(e);if(t===void 0)throw new Error(`Multipliers for ${e} not found`);return t}getAllMultipliers(){return Array.from(this.multipliers.entries()).map(([e,t])=>({edge:e,multipliers:t}))}}var O;((u)=>{u.DICE="DICE";u.MINES="MINES";u.KENO="KENO";u.LIMBO="LIMBO";u.ADVANCED_DICE="ADVANCED_DICE";u.BACCARAT="BACCARAT";u.DIAMONDS="DIAMONDS";u.PLINKO="PLINKO";u.ROULETTE="ROULETTE";u.WHEEL="WHEEL";u.BLACKJACK="BLACKJACK";u.BLITZ="BLITZ";u.DARTS="DARTS"})(O||={});import E from"big.js";import{Guard as m}from"@betorigami/guard";var q=({outcomes:e,totalWeight:t})=>{return m.against(t<=0,"An outcome distribution must have a positive total weight"),e.reduce((r,{multiplier:i,weight:n})=>r.plus(i.times(n)),E(0)).div(t)},j=({outcomes:e})=>{return m.againstEmptyArray(e,"An outcome distribution must have at least one outcome"),e.reduce((t,{multiplier:r})=>r.gt(t)?r:t,e[0].multiplier)},z=({outcomes:e,totalWeight:t},r)=>{m.against(t<=0,"An outcome distribution must have a positive total weight");let i=e.filter((n)=>n.slot===r).reduce((n,a)=>n+a.weight,0);return E(i).div(t)};import y from"big.js";var te=(e)=>e.legs.reduce((t,r)=>t.add(r.amount),new y("0"));import{Guard as p}from"@betorigami/guard";class A{source;index=0;constructor(e){this.source=e}static from(e,t){return p.against(t<=0,`Count must be positive, got ${t}`),p.against(e.values.length!==t,`Expected ${t} values, got ${e.values.length}`),new A(e)}get consumedValues(){return this.source.values.slice(0,this.index)}toMultipleRandomValues(){return this.source}takeOne(){let e=this.source.values[this.index];return p.againstUndefined(e,"RandomValuePool exhausted"),this.index+=1,e}take(e){p.against(this.index+e>this.source.values.length,`RandomValuePool exhausted: requested ${e} but only ${this.source.values.length-this.index} remain`);let t=this.source.values.slice(this.index,this.index+e);return this.index+=e,t}}import{Guard as h}from"@betorigami/guard";class G{gameId;constructor(e){this.gameId=e}getGameResult(e){let t=this.supportedEdgeConfigurations.includes(e.edge);return h.against(!t,()=>o.createUnsupportedEdgeError(`${this.gameId} does not support ${e.edge}% edge. Supported edge configurations include: ${this.supportedEdgeConfigurations}`)),this.determineGameResult(e)}}var se=(e,t)=>{return e.gt(t)?e:t};var le=(e,t)=>{return e.lt(t)?e:t};import d from"big.js";class M{static withMultiplier(e,t){return new d(e).times(new d(100-t).div(100))}static withWinChance(e,t){return d(100-e).div(t)}}var ge=(e,t)=>{if(t<0||e<0||t>e)throw new Error("Invalid input: ensure 0 ≤ r ≤ n and n ≥ 0.");if(t>e-t)t=e-t;let r=1;for(let i=1;i<=t;i++)r=r*(e-i+1)/i;return r};export{te as sumPayout,q as rtpOf,z as probabilityOf,ge as nCr,j as maxMultiplierOf,le as bigMin,se as bigMax,G as SingleActionGame,A as RandomValuePool,O as OrigamiGame,S as MultiplierContainer,M as MultiplierCalculator,f as MultiActionGame,V as MIN_CARD_VALUE,_ as MAX_CARD_VALUE,T as GameErrorCode,o as GameError,B as FACE_CARD_RANKS,D as Card,g as CARD_VALUES,N as CARD_SUIT_COLORS,b as CARD_SUITS,R as CARD_RANKS};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var b=Object.create;var{getPrototypeOf:d,defineProperty:u,getOwnPropertyNames:l,getOwnPropertyDescriptor:c}=Object,s=Object.prototype.hasOwnProperty;var v=(e,n,r)=>{r=e!=null?b(d(e)):{};let t=n||!e||!e.__esModule?u(r,"default",{value:e,enumerable:!0}):r;for(let m of l(e))if(!s.call(t,m))u(t,m,{get:()=>e[m],enumerable:!0});return t},a=new WeakMap,R=(e)=>{var n=a.get(e),r;if(n)return n;if(n=u({},"__esModule",{value:!0}),e&&typeof e==="object"||typeof e==="function")l(e).map((t)=>!s.call(n,t)&&u(n,t,{get:()=>e[t],enumerable:!(r=c(e,t))||r.enumerable}));return a.set(e,n),n};var h=(e,n)=>{for(var r in n)u(e,r,{get:n[r],enumerable:!0,configurable:!0,set:(t)=>n[r]=()=>t})};var g={};h(g,{SingleRandomNumberGeneratorStub:()=>i,RandomNumberGeneratorStub:()=>o,MultipleRandomNumberGeneratorStub:()=>p});module.exports=R(g);class o{getRandomInt({}){throw new Error("Not implemented")}getRandomIntsWithReplacement({}){throw new Error("Not implemented")}getUniqueRandomInts({}){throw new Error("Not implemented")}}class i extends o{value;constructor(e){super();this.value=e}getRandomInt(e){return{...e,value:this.value,values:[this.value],count:1,proofHash:"stub-proof-hash",proofString:"stub-proof-string"}}}class p extends o{values;constructor(e){super();this.values=e}getRandomIntsWithReplacement(e){return{...e,values:this.values,proofHash:"stub-proof-hash",proofString:"stub-proof-string"}}getUniqueRandomInts(e){return{...e,values:this.values,proofHash:"stub-proof-hash",proofString:"stub-proof-string"}}}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// Generated by dts-bundle-generator v9.5.1
|
|
2
|
+
|
|
3
|
+
export type Proof = {
|
|
4
|
+
proofHash: string;
|
|
5
|
+
proofString: string;
|
|
6
|
+
};
|
|
7
|
+
export type SingleRandomValue = {
|
|
8
|
+
value: number;
|
|
9
|
+
values: number[];
|
|
10
|
+
count: 1;
|
|
11
|
+
min: number;
|
|
12
|
+
max: number;
|
|
13
|
+
} & Proof;
|
|
14
|
+
export type MultipleRandomValues = {
|
|
15
|
+
values: number[];
|
|
16
|
+
count: number;
|
|
17
|
+
min: number;
|
|
18
|
+
max: number;
|
|
19
|
+
} & Proof;
|
|
20
|
+
export interface RandomNumberGenerator {
|
|
21
|
+
getRandomInt({ min, max }: {
|
|
22
|
+
min: number;
|
|
23
|
+
max: number;
|
|
24
|
+
}): SingleRandomValue;
|
|
25
|
+
getUniqueRandomInts({ min, max, count }: {
|
|
26
|
+
min: number;
|
|
27
|
+
max: number;
|
|
28
|
+
count: number;
|
|
29
|
+
}): MultipleRandomValues;
|
|
30
|
+
getRandomIntsWithReplacement({ min, max, count }: {
|
|
31
|
+
min: number;
|
|
32
|
+
max: number;
|
|
33
|
+
count: number;
|
|
34
|
+
}): MultipleRandomValues;
|
|
35
|
+
}
|
|
36
|
+
export declare abstract class RandomNumberGeneratorStub implements RandomNumberGenerator {
|
|
37
|
+
getRandomInt({}: {
|
|
38
|
+
min: number;
|
|
39
|
+
max: number;
|
|
40
|
+
}): SingleRandomValue;
|
|
41
|
+
getRandomIntsWithReplacement({}: {
|
|
42
|
+
min: number;
|
|
43
|
+
max: number;
|
|
44
|
+
count: number;
|
|
45
|
+
}): MultipleRandomValues;
|
|
46
|
+
getUniqueRandomInts({}: {
|
|
47
|
+
min: number;
|
|
48
|
+
max: number;
|
|
49
|
+
count: number;
|
|
50
|
+
}): MultipleRandomValues;
|
|
51
|
+
}
|
|
52
|
+
export declare class SingleRandomNumberGeneratorStub extends RandomNumberGeneratorStub {
|
|
53
|
+
private readonly value;
|
|
54
|
+
constructor(value: number);
|
|
55
|
+
getRandomInt(args: {
|
|
56
|
+
min: number;
|
|
57
|
+
max: number;
|
|
58
|
+
}): SingleRandomValue;
|
|
59
|
+
}
|
|
60
|
+
export declare class MultipleRandomNumberGeneratorStub extends RandomNumberGeneratorStub {
|
|
61
|
+
private readonly values;
|
|
62
|
+
constructor(values: number[]);
|
|
63
|
+
getRandomIntsWithReplacement(args: {
|
|
64
|
+
min: number;
|
|
65
|
+
max: number;
|
|
66
|
+
count: number;
|
|
67
|
+
}): MultipleRandomValues;
|
|
68
|
+
getUniqueRandomInts(args: {
|
|
69
|
+
min: number;
|
|
70
|
+
max: number;
|
|
71
|
+
count: number;
|
|
72
|
+
}): MultipleRandomValues;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
class n{getRandomInt({}){throw new Error("Not implemented")}getRandomIntsWithReplacement({}){throw new Error("Not implemented")}getUniqueRandomInts({}){throw new Error("Not implemented")}}class r extends n{value;constructor(e){super();this.value=e}getRandomInt(e){return{...e,value:this.value,values:[this.value],count:1,proofHash:"stub-proof-hash",proofString:"stub-proof-string"}}}class t extends n{values;constructor(e){super();this.values=e}getRandomIntsWithReplacement(e){return{...e,values:this.values,proofHash:"stub-proof-hash",proofString:"stub-proof-string"}}getUniqueRandomInts(e){return{...e,values:this.values,proofHash:"stub-proof-hash",proofString:"stub-proof-string"}}}export{r as SingleRandomNumberGeneratorStub,n as RandomNumberGeneratorStub,t as MultipleRandomNumberGeneratorStub};
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@betorigami/game-engine",
|
|
3
|
+
"version": "0.0.0-sha-d6d1f10-20260907014734",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "dist/index.cjs",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"require": "./dist/index.cjs",
|
|
12
|
+
"import": "./dist/index.mjs"
|
|
13
|
+
},
|
|
14
|
+
"./testing": {
|
|
15
|
+
"types": "./dist/testing/index.d.ts",
|
|
16
|
+
"require": "./dist/testing/index.cjs",
|
|
17
|
+
"import": "./dist/testing/index.mjs"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"tsc": "tsc",
|
|
25
|
+
"build": "bun run build.ts",
|
|
26
|
+
"watch": "bun run --watch src/index.ts",
|
|
27
|
+
"test": "bun test",
|
|
28
|
+
"test:watch": "bun test --watch",
|
|
29
|
+
"bundlemon": "bundlemon --subProject game-engine",
|
|
30
|
+
"format": "biome format --write"
|
|
31
|
+
},
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@tsconfig/bun": "^1.0.8",
|
|
37
|
+
"@types/big.js": "^6.2.2",
|
|
38
|
+
"@types/bun": "^1.2.17",
|
|
39
|
+
"bun-plugin-dts": "^0.3.0",
|
|
40
|
+
"typescript": "^5.8.3"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@betorigami/guard": "^2.0.7",
|
|
44
|
+
"big.js": "^7.0.1",
|
|
45
|
+
"fast-deep-equal": "^3.1.3"
|
|
46
|
+
}
|
|
47
|
+
}
|