shufflecom-calculations 4.1.2 → 4.1.4-1
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/lib/games/floor-is-lava-rules.d.ts +19 -5
- package/lib/games/floor-is-lava-rules.js +18 -5
- package/lib/games/floor-is-lava-rules.js.map +1 -1
- package/lib/games/floor-is-lava.d.ts +8 -7
- package/lib/games/floor-is-lava.js +25 -19
- package/lib/games/floor-is-lava.js.map +1 -1
- package/lib/lottery/lottery.d.ts +1 -1
- package/lib/lottery/lottery.js +1 -1
- package/lib/sports/sports.types.d.ts +5 -7
- package/lib/sports/sports.types.js +4 -5
- package/lib/sports/sports.types.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/utils/chain.d.ts +2 -1
- package/lib/utils/chain.js +1 -0
- package/lib/utils/chain.js.map +1 -1
- package/lib/utils/getExternalTxLinkOfChain.js +4 -0
- package/lib/utils/getExternalTxLinkOfChain.js.map +1 -1
- package/package.json +2 -2
- package/src/games/floor-is-lava-rules.spec.ts +30 -4
- package/src/games/floor-is-lava-rules.ts +35 -9
- package/src/games/floor-is-lava.spec.ts +73 -40
- package/src/games/floor-is-lava.ts +34 -19
- package/src/lottery/lottery.ts +1 -1
- package/src/sports/sports.types.ts +6 -9
- package/src/utils/chain.ts +1 -0
- package/src/utils/getExternalTxLinkOfChain.ts +4 -0
package/lib/utils/chain.d.ts
CHANGED
package/lib/utils/chain.js
CHANGED
package/lib/utils/chain.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chain.js","sourceRoot":"","sources":["../../src/utils/chain.ts"],"names":[],"mappings":";;;AAAA,IAAY,
|
|
1
|
+
{"version":3,"file":"chain.js","sourceRoot":"","sources":["../../src/utils/chain.ts"],"names":[],"mappings":";;;AAAA,IAAY,KAeX;AAfD,WAAY,KAAK;IACf,4BAAmB,CAAA;IACnB,8BAAqB,CAAA;IACrB,0BAAiB,CAAA;IACjB,8BAAqB,CAAA;IACrB,sBAAa,CAAA;IACb,wCAA+B,CAAA;IAC/B,oBAAW,CAAA;IACX,sBAAa,CAAA;IACb,oDAA2C,CAAA;IAC3C,oBAAW,CAAA;IACX,wBAAe,CAAA;IACf,sBAAa,CAAA;IACb,8BAAqB,CAAA;IACrB,oBAAW,CAAA;AACb,CAAC,EAfW,KAAK,qBAAL,KAAK,QAehB"}
|
|
@@ -16,6 +16,7 @@ exports.TX_LINK_PREFIXES_TESTNET = {
|
|
|
16
16
|
[chain_1.Chain.AVAXC]: 'https://testnet.snowtrace.io/tx/{txId}',
|
|
17
17
|
[chain_1.Chain.ARBITRUM]: 'https://sepolia.arbiscan.io/tx/{txId}',
|
|
18
18
|
[chain_1.Chain.BASE]: 'https://sepolia.basescan.org/tx/{txId}',
|
|
19
|
+
[chain_1.Chain.RHC]: 'https://explorer.testnet.chain.robinhood.com/tx/{txId}',
|
|
19
20
|
};
|
|
20
21
|
exports.TX_LINK_PREFIXES_MAINNET = {
|
|
21
22
|
[chain_1.Chain.ETHEREUM]: 'https://etherscan.io/tx/{txId}',
|
|
@@ -31,6 +32,7 @@ exports.TX_LINK_PREFIXES_MAINNET = {
|
|
|
31
32
|
[chain_1.Chain.AVAXC]: 'https://snowtrace.io/tx/{txId}',
|
|
32
33
|
[chain_1.Chain.ARBITRUM]: 'https://arbiscan.io/tx/{txId}',
|
|
33
34
|
[chain_1.Chain.BASE]: 'https://basescan.org/tx/{txId}',
|
|
35
|
+
[chain_1.Chain.RHC]: 'https://robinhoodchain.blockscout.com/{txId}',
|
|
34
36
|
};
|
|
35
37
|
exports.ADDRESS_LINK_PREFIXES_TESTNET = {
|
|
36
38
|
[chain_1.Chain.ETHEREUM]: 'https://sepolia.etherscan.io/address/{address}',
|
|
@@ -46,6 +48,7 @@ exports.ADDRESS_LINK_PREFIXES_TESTNET = {
|
|
|
46
48
|
[chain_1.Chain.AVAXC]: 'https://testnet.snowtrace.io/address/{address}',
|
|
47
49
|
[chain_1.Chain.ARBITRUM]: 'https://sepolia.arbiscan.io/address/{address}',
|
|
48
50
|
[chain_1.Chain.BASE]: 'https://sepolia.basescan.org/address/{address}',
|
|
51
|
+
[chain_1.Chain.RHC]: 'https://explorer.testnet.chain.robinhood.com/address/{address}',
|
|
49
52
|
};
|
|
50
53
|
exports.ADDRESS_LINK_PREFIXES_MAINNET = {
|
|
51
54
|
[chain_1.Chain.ETHEREUM]: 'https://etherscan.io/address/{address}',
|
|
@@ -61,6 +64,7 @@ exports.ADDRESS_LINK_PREFIXES_MAINNET = {
|
|
|
61
64
|
[chain_1.Chain.AVAXC]: 'https://snowtrace.io/address/{address}',
|
|
62
65
|
[chain_1.Chain.ARBITRUM]: 'https://arbiscan.io/address/{address}',
|
|
63
66
|
[chain_1.Chain.BASE]: 'https://basescan.org/address/{address}',
|
|
67
|
+
[chain_1.Chain.RHC]: 'https://robinhoodchain.blockscout.com/address/{address}',
|
|
64
68
|
};
|
|
65
69
|
const getExternalTxLinkOfChain = (txId, chain, isTest = false) => {
|
|
66
70
|
const TX_LINK_PREFIXES = isTest ? exports.TX_LINK_PREFIXES_TESTNET : exports.TX_LINK_PREFIXES_MAINNET;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getExternalTxLinkOfChain.js","sourceRoot":"","sources":["../../src/utils/getExternalTxLinkOfChain.ts"],"names":[],"mappings":";;;AAAA,mCAAgC;AAEnB,QAAA,wBAAwB,GAA0B;IAC7D,CAAC,aAAK,CAAC,QAAQ,CAAC,EAAE,wCAAwC;IAC1D,CAAC,aAAK,CAAC,OAAO,CAAC,EAAE,kDAAkD;IACnE,CAAC,aAAK,CAAC,QAAQ,CAAC,EAAE,sDAAsD;IACxE,CAAC,aAAK,CAAC,IAAI,CAAC,EAAE,kDAAkD;IAChE,CAAC,aAAK,CAAC,aAAa,CAAC,EAAE,wCAAwC;IAC/D,CAAC,aAAK,CAAC,GAAG,CAAC,EAAE,8CAA8C;IAC3D,CAAC,aAAK,CAAC,MAAM,CAAC,EAAE,6CAA6C;IAC7D,CAAC,aAAK,CAAC,IAAI,CAAC,EAAE,sDAAsD;IACpE,CAAC,aAAK,CAAC,mBAAmB,CAAC,EAAE,uCAAuC;IACpE,CAAC,aAAK,CAAC,GAAG,CAAC,EAAE,kDAAkD;IAC/D,CAAC,aAAK,CAAC,KAAK,CAAC,EAAE,wCAAwC;IACvD,CAAC,aAAK,CAAC,QAAQ,CAAC,EAAE,uCAAuC;IACzD,CAAC,aAAK,CAAC,IAAI,CAAC,EAAE,wCAAwC;
|
|
1
|
+
{"version":3,"file":"getExternalTxLinkOfChain.js","sourceRoot":"","sources":["../../src/utils/getExternalTxLinkOfChain.ts"],"names":[],"mappings":";;;AAAA,mCAAgC;AAEnB,QAAA,wBAAwB,GAA0B;IAC7D,CAAC,aAAK,CAAC,QAAQ,CAAC,EAAE,wCAAwC;IAC1D,CAAC,aAAK,CAAC,OAAO,CAAC,EAAE,kDAAkD;IACnE,CAAC,aAAK,CAAC,QAAQ,CAAC,EAAE,sDAAsD;IACxE,CAAC,aAAK,CAAC,IAAI,CAAC,EAAE,kDAAkD;IAChE,CAAC,aAAK,CAAC,aAAa,CAAC,EAAE,wCAAwC;IAC/D,CAAC,aAAK,CAAC,GAAG,CAAC,EAAE,8CAA8C;IAC3D,CAAC,aAAK,CAAC,MAAM,CAAC,EAAE,6CAA6C;IAC7D,CAAC,aAAK,CAAC,IAAI,CAAC,EAAE,sDAAsD;IACpE,CAAC,aAAK,CAAC,mBAAmB,CAAC,EAAE,uCAAuC;IACpE,CAAC,aAAK,CAAC,GAAG,CAAC,EAAE,kDAAkD;IAC/D,CAAC,aAAK,CAAC,KAAK,CAAC,EAAE,wCAAwC;IACvD,CAAC,aAAK,CAAC,QAAQ,CAAC,EAAE,uCAAuC;IACzD,CAAC,aAAK,CAAC,IAAI,CAAC,EAAE,wCAAwC;IACtD,CAAC,aAAK,CAAC,GAAG,CAAC,EAAE,wDAAwD;CACtE,CAAC;AAEW,QAAA,wBAAwB,GAA0B;IAC7D,CAAC,aAAK,CAAC,QAAQ,CAAC,EAAE,gCAAgC;IAClD,CAAC,aAAK,CAAC,OAAO,CAAC,EAAE,0CAA0C;IAC3D,CAAC,aAAK,CAAC,QAAQ,CAAC,EAAE,oDAAoD;IACtE,CAAC,aAAK,CAAC,IAAI,CAAC,EAAE,2CAA2C;IACzD,CAAC,aAAK,CAAC,aAAa,CAAC,EAAE,mCAAmC;IAC1D,CAAC,aAAK,CAAC,GAAG,CAAC,EAAE,+BAA+B;IAC5C,CAAC,aAAK,CAAC,MAAM,CAAC,EAAE,8BAA8B;IAC9C,CAAC,aAAK,CAAC,IAAI,CAAC,EAAE,sDAAsD;IACpE,CAAC,aAAK,CAAC,mBAAmB,CAAC,EAAE,+BAA+B;IAC5D,CAAC,aAAK,CAAC,GAAG,CAAC,EAAE,sCAAsC;IACnD,CAAC,aAAK,CAAC,KAAK,CAAC,EAAE,gCAAgC;IAC/C,CAAC,aAAK,CAAC,QAAQ,CAAC,EAAE,+BAA+B;IACjD,CAAC,aAAK,CAAC,IAAI,CAAC,EAAE,gCAAgC;IAC9C,CAAC,aAAK,CAAC,GAAG,CAAC,EAAE,8CAA8C;CAC5D,CAAC;AAEW,QAAA,6BAA6B,GAA0B;IAClE,CAAC,aAAK,CAAC,QAAQ,CAAC,EAAE,gDAAgD;IAClE,CAAC,aAAK,CAAC,OAAO,CAAC,EAAE,iEAAiE;IAClF,CAAC,aAAK,CAAC,QAAQ,CAAC,EAAE,8DAA8D;IAChF,CAAC,aAAK,CAAC,IAAI,CAAC,EAAE,iDAAiD;IAC/D,CAAC,aAAK,CAAC,aAAa,CAAC,EAAE,kDAAkD;IACzE,CAAC,aAAK,CAAC,GAAG,CAAC,EAAE,6CAA6C;IAC1D,CAAC,aAAK,CAAC,MAAM,CAAC,EAAE,qDAAqD;IACrE,CAAC,aAAK,CAAC,IAAI,CAAC,EAAE,8DAA8D;IAC5E,CAAC,aAAK,CAAC,mBAAmB,CAAC,EAAE,+CAA+C;IAC5E,CAAC,aAAK,CAAC,GAAG,CAAC,EAAE,yCAAyC;IACtD,CAAC,aAAK,CAAC,KAAK,CAAC,EAAE,gDAAgD;IAC/D,CAAC,aAAK,CAAC,QAAQ,CAAC,EAAE,+CAA+C;IACjE,CAAC,aAAK,CAAC,IAAI,CAAC,EAAE,gDAAgD;IAC9D,CAAC,aAAK,CAAC,GAAG,CAAC,EAAE,gEAAgE;CAC9E,CAAC;AAEW,QAAA,6BAA6B,GAA0B;IAClE,CAAC,aAAK,CAAC,QAAQ,CAAC,EAAE,wCAAwC;IAC1D,CAAC,aAAK,CAAC,OAAO,CAAC,EAAE,iEAAiE;IAClF,CAAC,aAAK,CAAC,QAAQ,CAAC,EAAE,mDAAmD;IACrE,CAAC,aAAK,CAAC,IAAI,CAAC,EAAE,0CAA0C;IACxD,CAAC,aAAK,CAAC,aAAa,CAAC,EAAE,2CAA2C;IAClE,CAAC,aAAK,CAAC,GAAG,CAAC,EAAE,uCAAuC;IACpD,CAAC,aAAK,CAAC,MAAM,CAAC,EAAE,sCAAsC;IACtD,CAAC,aAAK,CAAC,IAAI,CAAC,EAAE,8DAA8D;IAC5E,CAAC,aAAK,CAAC,mBAAmB,CAAC,EAAE,uCAAuC;IACpE,CAAC,aAAK,CAAC,GAAG,CAAC,EAAE,8CAA8C;IAC3D,CAAC,aAAK,CAAC,KAAK,CAAC,EAAE,wCAAwC;IACvD,CAAC,aAAK,CAAC,QAAQ,CAAC,EAAE,uCAAuC;IACzD,CAAC,aAAK,CAAC,IAAI,CAAC,EAAE,wCAAwC;IACtD,CAAC,aAAK,CAAC,GAAG,CAAC,EAAE,yDAAyD;CACvE,CAAC;AAEK,MAAM,wBAAwB,GAAG,CAAC,IAAY,EAAE,KAAY,EAAE,SAAkB,KAAK,EAAE,EAAE;IAC9F,MAAM,gBAAgB,GAAG,MAAM,CAAC,CAAC,CAAC,gCAAwB,CAAC,CAAC,CAAC,gCAAwB,CAAC;IAEtF,MAAM,GAAG,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;IAEpC,OAAO,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;AACrC,CAAC,CAAC;AANW,QAAA,wBAAwB,4BAMnC;AAEK,MAAM,sBAAsB,GAAG,CAAC,OAAe,EAAE,KAAY,EAAE,SAAkB,KAAK,EAAE,EAAE;IAC/F,MAAM,qBAAqB,GAAG,MAAM,CAAC,CAAC,CAAC,qCAA6B,CAAC,CAAC,CAAC,qCAA6B,CAAC;IAErG,MAAM,GAAG,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC;IAEzC,OAAO,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;AAC3C,CAAC,CAAC;AANW,QAAA,sBAAsB,0BAMjC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shufflecom-calculations",
|
|
3
|
-
"version": "4.1.
|
|
3
|
+
"version": "4.1.4-1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"types": "lib/index.d.ts",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -14,5 +14,5 @@
|
|
|
14
14
|
},
|
|
15
15
|
"author": "",
|
|
16
16
|
"license": "ISC",
|
|
17
|
-
"gitHead": "
|
|
17
|
+
"gitHead": "76dd602d130a03ab473e295046dd90957101b9eb"
|
|
18
18
|
}
|
|
@@ -4,17 +4,23 @@ import { FixedSequenceRng } from './fixed-rng';
|
|
|
4
4
|
import {
|
|
5
5
|
BOARD_SIZE,
|
|
6
6
|
DIFFICULTY_DROP_MAP,
|
|
7
|
+
FloorIsLavaActionPhase,
|
|
7
8
|
FloorIsLavaDifficulty,
|
|
8
|
-
FloorIsLavaPickAction,
|
|
9
9
|
FloorIsLavaRules,
|
|
10
10
|
FloorIsLavaStartAction,
|
|
11
|
+
FloorIsLavaSuccessfulPickAction,
|
|
11
12
|
} from './floor-is-lava-rules';
|
|
12
13
|
|
|
13
14
|
const EDGE_BPS = 200;
|
|
14
15
|
|
|
15
|
-
const startAction = (difficulty: FloorIsLavaDifficulty): FloorIsLavaStartAction => ({ phase:
|
|
16
|
+
const startAction = (difficulty: FloorIsLavaDifficulty): FloorIsLavaStartAction => ({ phase: FloorIsLavaActionPhase.START, difficulty });
|
|
16
17
|
|
|
17
|
-
const pickAction = ():
|
|
18
|
+
const pickAction = (): FloorIsLavaSuccessfulPickAction => ({
|
|
19
|
+
phase: FloorIsLavaActionPhase.SUCCESSFUL_PICK,
|
|
20
|
+
selectedTile: 0,
|
|
21
|
+
droppedTiles: [],
|
|
22
|
+
multiplier: new BigNumber(0),
|
|
23
|
+
});
|
|
18
24
|
|
|
19
25
|
describe('FloorIsLavaRules.maxRounds', () => {
|
|
20
26
|
it.each([
|
|
@@ -67,6 +73,14 @@ describe('FloorIsLavaRules.getRoundResults', () => {
|
|
|
67
73
|
});
|
|
68
74
|
});
|
|
69
75
|
|
|
76
|
+
describe('FloorIsLavaRules.lastSurvivingTile', () => {
|
|
77
|
+
it('returns the final tile of the full-board drop order — the last to fall', () => {
|
|
78
|
+
const champion = 7;
|
|
79
|
+
const dropOrder = [...Array.from({ length: BOARD_SIZE }, (_, i) => i).filter(tile => tile !== champion), champion];
|
|
80
|
+
expect(FloorIsLavaRules.lastSurvivingTile(new FixedSequenceRng(dropOrder))).toBe(champion);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
70
84
|
describe('FloorIsLavaRules.reconstructRounds', () => {
|
|
71
85
|
it('counts the PICK actions following START as survived rounds', () => {
|
|
72
86
|
const actions = [startAction(FloorIsLavaDifficulty.MEDIUM), pickAction(), pickAction()];
|
|
@@ -88,10 +102,22 @@ describe('FloorIsLavaRules.reconstructRounds', () => {
|
|
|
88
102
|
|
|
89
103
|
it('throws when a CASHOUT action appears in the history', () => {
|
|
90
104
|
expect(() =>
|
|
91
|
-
FloorIsLavaRules.reconstructRounds([
|
|
105
|
+
FloorIsLavaRules.reconstructRounds([
|
|
106
|
+
startAction(FloorIsLavaDifficulty.MEDIUM),
|
|
107
|
+
{ phase: FloorIsLavaActionPhase.CASHOUT, multiplier: new BigNumber(1), lastSurvivingTile: 0 },
|
|
108
|
+
]),
|
|
92
109
|
).toThrow('An active game cannot have a CASHOUT action');
|
|
93
110
|
});
|
|
94
111
|
|
|
112
|
+
it('throws when a BUST_PICK action appears in the history', () => {
|
|
113
|
+
expect(() =>
|
|
114
|
+
FloorIsLavaRules.reconstructRounds([
|
|
115
|
+
startAction(FloorIsLavaDifficulty.MEDIUM),
|
|
116
|
+
{ phase: FloorIsLavaActionPhase.BUST_PICK, selectedTile: 0, droppedTiles: [], lastSurvivingTile: 1 },
|
|
117
|
+
]),
|
|
118
|
+
).toThrow('An active game cannot have a BUST_PICK action');
|
|
119
|
+
});
|
|
120
|
+
|
|
95
121
|
it('throws when survived rounds exceed the difficulty max', () => {
|
|
96
122
|
expect(() => FloorIsLavaRules.reconstructRounds([startAction(FloorIsLavaDifficulty.TOXIC), pickAction(), pickAction()])).toThrow(
|
|
97
123
|
'Reconstructed rounds (2) exceeds max rounds (1)',
|
|
@@ -21,24 +21,43 @@ export const DIFFICULTY_DROP_MAP: Readonly<Record<FloorIsLavaDifficulty, number>
|
|
|
21
21
|
|
|
22
22
|
export const MAX_ROUNDS = DROPPABLE_TILES / Math.min(...Object.values(DIFFICULTY_DROP_MAP));
|
|
23
23
|
|
|
24
|
+
export enum FloorIsLavaActionPhase {
|
|
25
|
+
START = 'START',
|
|
26
|
+
SUCCESSFUL_PICK = 'SUCCESSFUL_PICK',
|
|
27
|
+
BUST_PICK = 'BUST_PICK',
|
|
28
|
+
CASHOUT = 'CASHOUT',
|
|
29
|
+
}
|
|
30
|
+
|
|
24
31
|
export interface FloorIsLavaStartAction {
|
|
25
|
-
phase:
|
|
32
|
+
phase: FloorIsLavaActionPhase.START;
|
|
26
33
|
difficulty: FloorIsLavaDifficulty;
|
|
27
34
|
}
|
|
28
35
|
|
|
29
|
-
export interface
|
|
30
|
-
phase:
|
|
36
|
+
export interface FloorIsLavaSuccessfulPickAction {
|
|
37
|
+
phase: FloorIsLavaActionPhase.SUCCESSFUL_PICK;
|
|
31
38
|
selectedTile: number;
|
|
32
39
|
droppedTiles: number[];
|
|
33
40
|
multiplier: BigNumber;
|
|
34
41
|
}
|
|
35
42
|
|
|
43
|
+
export interface FloorIsLavaBustPickAction {
|
|
44
|
+
phase: FloorIsLavaActionPhase.BUST_PICK;
|
|
45
|
+
selectedTile: number;
|
|
46
|
+
droppedTiles: number[];
|
|
47
|
+
lastSurvivingTile: number;
|
|
48
|
+
}
|
|
49
|
+
|
|
36
50
|
export interface FloorIsLavaCashoutAction {
|
|
37
|
-
phase:
|
|
51
|
+
phase: FloorIsLavaActionPhase.CASHOUT;
|
|
38
52
|
multiplier: BigNumber;
|
|
53
|
+
lastSurvivingTile: number;
|
|
39
54
|
}
|
|
40
55
|
|
|
41
|
-
export type FloorIsLavaGameAction =
|
|
56
|
+
export type FloorIsLavaGameAction =
|
|
57
|
+
| FloorIsLavaStartAction
|
|
58
|
+
| FloorIsLavaSuccessfulPickAction
|
|
59
|
+
| FloorIsLavaBustPickAction
|
|
60
|
+
| FloorIsLavaCashoutAction;
|
|
42
61
|
|
|
43
62
|
export class FloorIsLavaRules {
|
|
44
63
|
public static maxRounds(difficulty: FloorIsLavaDifficulty): number {
|
|
@@ -58,6 +77,11 @@ export class FloorIsLavaRules {
|
|
|
58
77
|
return calculateEdgeMultiplier(edge).multipliedBy(BOARD_SIZE).dividedBy(FloorIsLavaRules.tilesRemaining(roundsSurvived, dropsPerRound));
|
|
59
78
|
}
|
|
60
79
|
|
|
80
|
+
public static lastSurvivingTile(generator: RandomNumberGenerator): number {
|
|
81
|
+
const dropOrder = generator.getRandomIntsWithoutReplacement({ min: 0, max: BOARD_SIZE - 1, count: BOARD_SIZE });
|
|
82
|
+
return dropOrder[dropOrder.length - 1];
|
|
83
|
+
}
|
|
84
|
+
|
|
61
85
|
public static getRoundResults(generator: RandomNumberGenerator, dropsPerRound: number, numRounds: number): number[][] {
|
|
62
86
|
const dropSequence = generator.getRandomIntsWithoutReplacement({ min: 0, max: BOARD_SIZE - 1, count: numRounds * dropsPerRound });
|
|
63
87
|
|
|
@@ -70,7 +94,7 @@ export class FloorIsLavaRules {
|
|
|
70
94
|
|
|
71
95
|
public static reconstructRounds(gameActions: FloorIsLavaGameAction[]): { roundsSurvived: number; dropsPerRound: number } {
|
|
72
96
|
const first = gameActions[0];
|
|
73
|
-
if (!first || first.phase !==
|
|
97
|
+
if (!first || first.phase !== FloorIsLavaActionPhase.START) {
|
|
74
98
|
throw new Error('First action must be START');
|
|
75
99
|
}
|
|
76
100
|
|
|
@@ -81,15 +105,17 @@ export class FloorIsLavaRules {
|
|
|
81
105
|
for (let i = 1; i < gameActions.length; i++) {
|
|
82
106
|
const action = gameActions[i];
|
|
83
107
|
switch (action.phase) {
|
|
84
|
-
case
|
|
108
|
+
case FloorIsLavaActionPhase.START:
|
|
85
109
|
throw new Error('START action can only appear as the first action');
|
|
86
|
-
case
|
|
110
|
+
case FloorIsLavaActionPhase.SUCCESSFUL_PICK:
|
|
87
111
|
roundsSurvived++;
|
|
88
112
|
if (roundsSurvived > maxRounds) {
|
|
89
113
|
throw new Error(`Reconstructed rounds (${roundsSurvived}) exceeds max rounds (${maxRounds})`);
|
|
90
114
|
}
|
|
91
115
|
break;
|
|
92
|
-
case
|
|
116
|
+
case FloorIsLavaActionPhase.BUST_PICK:
|
|
117
|
+
throw new Error('An active game cannot have a BUST_PICK action');
|
|
118
|
+
case FloorIsLavaActionPhase.CASHOUT:
|
|
93
119
|
throw new Error('An active game cannot have a CASHOUT action');
|
|
94
120
|
}
|
|
95
121
|
}
|
|
@@ -6,11 +6,12 @@ import {
|
|
|
6
6
|
DIFFICULTY_DROP_MAP,
|
|
7
7
|
DROPPABLE_TILES,
|
|
8
8
|
FloorIsLava,
|
|
9
|
+
FloorIsLavaActionPhase,
|
|
10
|
+
FloorIsLavaAutoBetRoundOutcome,
|
|
9
11
|
FloorIsLavaDifficulty,
|
|
10
|
-
FloorIsLavaPickAction,
|
|
11
|
-
FloorIsLavaRoundOutcome,
|
|
12
12
|
FloorIsLavaRules,
|
|
13
13
|
FloorIsLavaStartAction,
|
|
14
|
+
FloorIsLavaSuccessfulPickAction,
|
|
14
15
|
} from './floor-is-lava';
|
|
15
16
|
import { TrueRandomRng } from './true-random-rng';
|
|
16
17
|
|
|
@@ -18,9 +19,22 @@ const ALL_DIFFICULTIES = [FloorIsLavaDifficulty.EASY, FloorIsLavaDifficulty.MEDI
|
|
|
18
19
|
|
|
19
20
|
const EDGE_BPS = 200;
|
|
20
21
|
|
|
21
|
-
const startAction = (difficulty: FloorIsLavaDifficulty): FloorIsLavaStartAction => ({ phase:
|
|
22
|
+
const startAction = (difficulty: FloorIsLavaDifficulty): FloorIsLavaStartAction => ({ phase: FloorIsLavaActionPhase.START, difficulty });
|
|
22
23
|
|
|
23
|
-
const pickAction = ():
|
|
24
|
+
const pickAction = (): FloorIsLavaSuccessfulPickAction => ({
|
|
25
|
+
phase: FloorIsLavaActionPhase.SUCCESSFUL_PICK,
|
|
26
|
+
selectedTile: 0,
|
|
27
|
+
droppedTiles: [],
|
|
28
|
+
multiplier: new BigNumber(0),
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// A full 49-tile drop order whose leading entries are `prefix` and whose final (last-surviving) tile is `champion`.
|
|
32
|
+
const fullDraw = (prefix: number[], champion: number): number[] => {
|
|
33
|
+
const middle = Array.from({ length: BOARD_SIZE }, (_, i) => i).filter(tile => tile !== champion && !prefix.includes(tile));
|
|
34
|
+
return [...prefix, ...middle, champion];
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const fullBoardRng = () => new FixedSequenceRng(Array.from({ length: BOARD_SIZE }, (_, i) => i));
|
|
24
38
|
|
|
25
39
|
const gameWithRounds = (difficulty: FloorIsLavaDifficulty, roundsSurvived: number) =>
|
|
26
40
|
FloorIsLava.fromActions([startAction(difficulty), ...Array.from({ length: roundsSurvived }, () => pickAction())]);
|
|
@@ -75,7 +89,11 @@ describe('FloorIsLava.fromActions', () => {
|
|
|
75
89
|
|
|
76
90
|
it('throws when the history contains a CASHOUT action', () => {
|
|
77
91
|
expect(() =>
|
|
78
|
-
FloorIsLava.fromActions([
|
|
92
|
+
FloorIsLava.fromActions([
|
|
93
|
+
startAction(FloorIsLavaDifficulty.MEDIUM),
|
|
94
|
+
pickAction(),
|
|
95
|
+
{ phase: FloorIsLavaActionPhase.CASHOUT, multiplier: new BigNumber(1), lastSurvivingTile: 0 },
|
|
96
|
+
]),
|
|
79
97
|
).toThrow('An active game cannot have a CASHOUT action');
|
|
80
98
|
});
|
|
81
99
|
|
|
@@ -101,20 +119,20 @@ describe('FloorIsLava.next', () => {
|
|
|
101
119
|
expect(result).toMatchObject({
|
|
102
120
|
isLoss: false,
|
|
103
121
|
isLastTile: false,
|
|
104
|
-
gameAction: { phase: '
|
|
122
|
+
gameAction: { phase: 'SUCCESSFUL_PICK', selectedTile: 5, droppedTiles: [10, 11, 12, 13, 14, 15] },
|
|
105
123
|
});
|
|
106
124
|
expect(result.multiplier.isEqualTo(expectedMultiplier(1, 6))).toBe(true);
|
|
107
125
|
});
|
|
108
126
|
|
|
109
|
-
it('a pick that lands on a dropped tile zeroes the multiplier and
|
|
110
|
-
const rng = new FixedSequenceRng([10, 11, 12, 13, 14, 15]);
|
|
127
|
+
it('a pick that lands on a dropped tile zeroes the multiplier and reveals the surviving tile', () => {
|
|
128
|
+
const rng = new FixedSequenceRng(fullDraw([10, 11, 12, 13, 14, 15], 32));
|
|
111
129
|
const result = expectOk(gameWithRounds(FloorIsLavaDifficulty.MEDIUM, 0).next(12, rng, EDGE_BPS));
|
|
112
130
|
|
|
113
131
|
expect(result).toMatchObject({
|
|
114
132
|
isLoss: true,
|
|
115
133
|
isLastTile: false,
|
|
116
134
|
multiplier: new BigNumber(0),
|
|
117
|
-
gameAction: { phase: '
|
|
135
|
+
gameAction: { phase: 'BUST_PICK', selectedTile: 12, droppedTiles: [10, 11, 12, 13, 14, 15], lastSurvivingTile: 32 },
|
|
118
136
|
});
|
|
119
137
|
});
|
|
120
138
|
|
|
@@ -133,10 +151,15 @@ describe('FloorIsLava.next', () => {
|
|
|
133
151
|
});
|
|
134
152
|
|
|
135
153
|
it('a loss deep into a streak still pays zero', () => {
|
|
136
|
-
const rng = new FixedSequenceRng([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 20, 21, 22, 23, 24, 25]);
|
|
154
|
+
const rng = new FixedSequenceRng(fullDraw([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 20, 21, 22, 23, 24, 25], 32));
|
|
137
155
|
const result = expectOk(gameWithRounds(FloorIsLavaDifficulty.MEDIUM, 2).next(22, rng, EDGE_BPS));
|
|
138
156
|
|
|
139
|
-
expect(result).toMatchObject({
|
|
157
|
+
expect(result).toMatchObject({
|
|
158
|
+
isLoss: true,
|
|
159
|
+
isLastTile: false,
|
|
160
|
+
multiplier: new BigNumber(0),
|
|
161
|
+
gameAction: { phase: 'BUST_PICK', lastSurvivingTile: 32 },
|
|
162
|
+
});
|
|
140
163
|
});
|
|
141
164
|
|
|
142
165
|
it('a full clear completes the game at the top multiplier', () => {
|
|
@@ -178,7 +201,7 @@ describe('FloorIsLava.next', () => {
|
|
|
178
201
|
|
|
179
202
|
describe('FloorIsLava — chained real sessions (fromActions replay of actual next() output)', () => {
|
|
180
203
|
it('threads real next() output through fromActions across multiple rounds', () => {
|
|
181
|
-
const rng = new FixedSequenceRng([10, 11, 12, 13, 14, 15, 20, 21, 22, 23, 24, 25]);
|
|
204
|
+
const rng = new FixedSequenceRng(fullDraw([10, 11, 12, 13, 14, 15, 20, 21, 22, 23, 24, 25], 32));
|
|
182
205
|
|
|
183
206
|
const game0 = FloorIsLava.fromActions([startAction(FloorIsLavaDifficulty.MEDIUM)]);
|
|
184
207
|
const round1 = expectOk(game0.next(30, rng, EDGE_BPS));
|
|
@@ -192,12 +215,13 @@ describe('FloorIsLava — chained real sessions (fromActions replay of actual ne
|
|
|
192
215
|
const game2 = FloorIsLava.fromActions([startAction(FloorIsLavaDifficulty.MEDIUM), round1.gameAction, round2.gameAction]);
|
|
193
216
|
expect(game2.canCashout()).toBe(true);
|
|
194
217
|
|
|
195
|
-
const cashoutResult = expectOk(game2.cashout(EDGE_BPS));
|
|
218
|
+
const cashoutResult = expectOk(game2.cashout(rng, EDGE_BPS));
|
|
196
219
|
expect(cashoutResult.multiplier).toEqual(round2.multiplier);
|
|
220
|
+
expect(cashoutResult.gameAction.lastSurvivingTile).toBe(32);
|
|
197
221
|
});
|
|
198
222
|
|
|
199
223
|
it('threads a real survived pick into a second round that busts', () => {
|
|
200
|
-
const rng = new FixedSequenceRng([10, 11, 12, 13, 14, 15, 20, 21, 22, 23, 24, 25]);
|
|
224
|
+
const rng = new FixedSequenceRng(fullDraw([10, 11, 12, 13, 14, 15, 20, 21, 22, 23, 24, 25], 32));
|
|
201
225
|
|
|
202
226
|
const game0 = FloorIsLava.fromActions([startAction(FloorIsLavaDifficulty.MEDIUM)]);
|
|
203
227
|
const round1 = expectOk(game0.next(30, rng, EDGE_BPS));
|
|
@@ -205,7 +229,12 @@ describe('FloorIsLava — chained real sessions (fromActions replay of actual ne
|
|
|
205
229
|
const game1 = FloorIsLava.fromActions([startAction(FloorIsLavaDifficulty.MEDIUM), round1.gameAction]);
|
|
206
230
|
const round2 = expectOk(game1.next(20, rng, EDGE_BPS));
|
|
207
231
|
|
|
208
|
-
expect(round2).toMatchObject({
|
|
232
|
+
expect(round2).toMatchObject({
|
|
233
|
+
isLoss: true,
|
|
234
|
+
isLastTile: false,
|
|
235
|
+
multiplier: new BigNumber(0),
|
|
236
|
+
gameAction: { phase: 'BUST_PICK', lastSurvivingTile: 32 },
|
|
237
|
+
});
|
|
209
238
|
});
|
|
210
239
|
});
|
|
211
240
|
|
|
@@ -216,7 +245,7 @@ describe('FloorIsLava.cashout', () => {
|
|
|
216
245
|
[FloorIsLavaDifficulty.HARD, 2],
|
|
217
246
|
])('cashing out %s after %i rounds pays the telescoped multiplier', (difficulty, rounds) => {
|
|
218
247
|
const dropsPerRound = difficulty === FloorIsLavaDifficulty.HARD ? 8 : 6;
|
|
219
|
-
const result = expectOk(gameWithRounds(difficulty, rounds).cashout(EDGE_BPS));
|
|
248
|
+
const result = expectOk(gameWithRounds(difficulty, rounds).cashout(fullBoardRng(), EDGE_BPS));
|
|
220
249
|
|
|
221
250
|
expect(result.multiplier.isEqualTo(expectedMultiplier(rounds, dropsPerRound))).toBe(true);
|
|
222
251
|
expect(result.gameAction.phase).toBe('CASHOUT');
|
|
@@ -224,83 +253,87 @@ describe('FloorIsLava.cashout', () => {
|
|
|
224
253
|
});
|
|
225
254
|
|
|
226
255
|
it('returns no-tiles-selected with zero rounds survived', () => {
|
|
227
|
-
expect(gameWithRounds(FloorIsLavaDifficulty.MEDIUM, 0).cashout(EDGE_BPS)).toEqual({ ok: false, error: 'no-tiles-selected' });
|
|
256
|
+
expect(gameWithRounds(FloorIsLavaDifficulty.MEDIUM, 0).cashout(fullBoardRng(), EDGE_BPS)).toEqual({ ok: false, error: 'no-tiles-selected' });
|
|
228
257
|
});
|
|
229
258
|
});
|
|
230
259
|
|
|
231
260
|
describe('FloorIsLava.autobet', () => {
|
|
232
261
|
it('surviving every selected round pays the N-round multiplier', () => {
|
|
233
|
-
const rng = new FixedSequenceRng([10, 11, 12, 13, 14, 15, 20, 21, 22, 23, 24, 25, 40, 41, 42, 43, 44, 45]);
|
|
262
|
+
const rng = new FixedSequenceRng(fullDraw([10, 11, 12, 13, 14, 15, 20, 21, 22, 23, 24, 25, 40, 41, 42, 43, 44, 45], 0));
|
|
234
263
|
const result = expectOk(FloorIsLava.autobet(FloorIsLavaDifficulty.MEDIUM, [30, 31, 32], rng, EDGE_BPS));
|
|
235
264
|
|
|
236
265
|
expect(result).toMatchObject({
|
|
237
266
|
survivedRounds: 3,
|
|
267
|
+
lastSurvivingTile: 0,
|
|
238
268
|
results: [
|
|
239
|
-
{ droppedTiles: [10, 11, 12, 13, 14, 15], outcome:
|
|
240
|
-
{ droppedTiles: [20, 21, 22, 23, 24, 25], outcome:
|
|
241
|
-
{ droppedTiles: [40, 41, 42, 43, 44, 45], outcome:
|
|
269
|
+
{ droppedTiles: [10, 11, 12, 13, 14, 15], outcome: FloorIsLavaAutoBetRoundOutcome.SURVIVED },
|
|
270
|
+
{ droppedTiles: [20, 21, 22, 23, 24, 25], outcome: FloorIsLavaAutoBetRoundOutcome.SURVIVED },
|
|
271
|
+
{ droppedTiles: [40, 41, 42, 43, 44, 45], outcome: FloorIsLavaAutoBetRoundOutcome.SURVIVED },
|
|
242
272
|
],
|
|
243
273
|
});
|
|
244
274
|
expect(result.multiplier.isEqualTo(expectedMultiplier(3, 6))).toBe(true);
|
|
245
275
|
});
|
|
246
276
|
|
|
247
277
|
it('busts and pays zero when a selected tile drops in its own round', () => {
|
|
248
|
-
const rng = new FixedSequenceRng([10, 11, 12, 13, 14, 15, 20, 21, 22, 23, 24, 25]);
|
|
278
|
+
const rng = new FixedSequenceRng(fullDraw([10, 11, 12, 13, 14, 15, 20, 21, 22, 23, 24, 25], 0));
|
|
249
279
|
const result = expectOk(FloorIsLava.autobet(FloorIsLavaDifficulty.MEDIUM, [30, 21], rng, EDGE_BPS));
|
|
250
280
|
|
|
251
281
|
expect(result).toMatchObject({
|
|
252
282
|
multiplier: new BigNumber(0),
|
|
253
283
|
survivedRounds: 1,
|
|
284
|
+
lastSurvivingTile: 0,
|
|
254
285
|
results: [
|
|
255
|
-
{ selectedTile: 30, outcome:
|
|
256
|
-
{ selectedTile: 21, droppedTiles: [20, 21, 22, 23, 24, 25], outcome:
|
|
286
|
+
{ selectedTile: 30, outcome: FloorIsLavaAutoBetRoundOutcome.SURVIVED },
|
|
287
|
+
{ selectedTile: 21, droppedTiles: [20, 21, 22, 23, 24, 25], outcome: FloorIsLavaAutoBetRoundOutcome.LOST },
|
|
257
288
|
],
|
|
258
289
|
});
|
|
259
290
|
});
|
|
260
291
|
|
|
261
292
|
it('forces a cashout (not a bust) when a selected tile had already dropped in an earlier round', () => {
|
|
262
|
-
const rng = new FixedSequenceRng([10, 11, 12, 13, 14, 15, 20, 21, 22, 23, 24, 25, 40, 41, 42, 43, 44, 45]);
|
|
293
|
+
const rng = new FixedSequenceRng(fullDraw([10, 11, 12, 13, 14, 15, 20, 21, 22, 23, 24, 25, 40, 41, 42, 43, 44, 45], 0));
|
|
263
294
|
// round 3 blindly stands on tile 10, which was already lava since round 1 - the player never actually risked this round's own draw, so they're paid out at what they'd already survived
|
|
264
295
|
const result = expectOk(FloorIsLava.autobet(FloorIsLavaDifficulty.MEDIUM, [30, 31, 10], rng, EDGE_BPS));
|
|
265
296
|
|
|
266
297
|
expect(result).toMatchObject({
|
|
267
298
|
survivedRounds: 2,
|
|
299
|
+
lastSurvivingTile: 0,
|
|
268
300
|
results: [
|
|
269
|
-
{ selectedTile: 30, outcome:
|
|
270
|
-
{ selectedTile: 31, outcome:
|
|
271
|
-
{ selectedTile: 10, droppedTiles: [40, 41, 42, 43, 44, 45], outcome:
|
|
301
|
+
{ selectedTile: 30, outcome: FloorIsLavaAutoBetRoundOutcome.SURVIVED },
|
|
302
|
+
{ selectedTile: 31, outcome: FloorIsLavaAutoBetRoundOutcome.SURVIVED },
|
|
303
|
+
{ selectedTile: 10, droppedTiles: [40, 41, 42, 43, 44, 45], outcome: FloorIsLavaAutoBetRoundOutcome.CASHED_OUT },
|
|
272
304
|
],
|
|
273
305
|
});
|
|
274
306
|
expect(result.multiplier.isEqualTo(expectedMultiplier(2, 6))).toBe(true);
|
|
275
307
|
});
|
|
276
308
|
|
|
277
309
|
it('forces a cashout on the very next round after the pick was already dead', () => {
|
|
278
|
-
const rng = new FixedSequenceRng([10, 11, 12, 13, 14, 15, 20, 21, 22, 23, 24, 25]);
|
|
310
|
+
const rng = new FixedSequenceRng(fullDraw([10, 11, 12, 13, 14, 15, 20, 21, 22, 23, 24, 25], 0));
|
|
279
311
|
const result = expectOk(FloorIsLava.autobet(FloorIsLavaDifficulty.MEDIUM, [30, 10], rng, EDGE_BPS));
|
|
280
312
|
|
|
281
313
|
expect(result).toMatchObject({
|
|
282
314
|
survivedRounds: 1,
|
|
315
|
+
lastSurvivingTile: 0,
|
|
283
316
|
results: [
|
|
284
|
-
{ selectedTile: 30, outcome:
|
|
285
|
-
{ selectedTile: 10, droppedTiles: [20, 21, 22, 23, 24, 25], outcome:
|
|
317
|
+
{ selectedTile: 30, outcome: FloorIsLavaAutoBetRoundOutcome.SURVIVED },
|
|
318
|
+
{ selectedTile: 10, droppedTiles: [20, 21, 22, 23, 24, 25], outcome: FloorIsLavaAutoBetRoundOutcome.CASHED_OUT },
|
|
286
319
|
],
|
|
287
320
|
});
|
|
288
321
|
expect(result.multiplier.isEqualTo(expectedMultiplier(1, 6))).toBe(true);
|
|
289
322
|
});
|
|
290
323
|
|
|
291
324
|
it('allows revisiting a still-standing tile across rounds', () => {
|
|
292
|
-
const rng = new FixedSequenceRng([10, 11, 12, 13, 14, 15, 20, 21, 22, 23, 24, 25]);
|
|
325
|
+
const rng = new FixedSequenceRng(fullDraw([10, 11, 12, 13, 14, 15, 20, 21, 22, 23, 24, 25], 0));
|
|
293
326
|
const result = expectOk(FloorIsLava.autobet(FloorIsLavaDifficulty.MEDIUM, [5, 5], rng, EDGE_BPS));
|
|
294
327
|
|
|
295
|
-
expect(result).toMatchObject({ survivedRounds: 2 });
|
|
328
|
+
expect(result).toMatchObject({ survivedRounds: 2, lastSurvivingTile: 0 });
|
|
296
329
|
expect(result.multiplier.isEqualTo(expectedMultiplier(2, 6))).toBe(true);
|
|
297
330
|
});
|
|
298
331
|
|
|
299
332
|
it('a full-length sequence full-clears at the top multiplier', () => {
|
|
300
|
-
const rng = new FixedSequenceRng(Array.from({ length:
|
|
333
|
+
const rng = new FixedSequenceRng(Array.from({ length: BOARD_SIZE }, (_, i) => i));
|
|
301
334
|
const result = expectOk(FloorIsLava.autobet(FloorIsLavaDifficulty.TOXIC, [DROPPABLE_TILES], rng, EDGE_BPS));
|
|
302
335
|
|
|
303
|
-
expect(result).toMatchObject({ survivedRounds: 1, multiplier: new BigNumber('48.02') });
|
|
336
|
+
expect(result).toMatchObject({ survivedRounds: 1, multiplier: new BigNumber('48.02'), lastSurvivingTile: DROPPABLE_TILES });
|
|
304
337
|
});
|
|
305
338
|
|
|
306
339
|
it('returns invalid-selected-tiles-count when the sequence exceeds the difficulty max rounds', () => {
|
|
@@ -327,7 +360,7 @@ describe('FloorIsLava.autobet', () => {
|
|
|
327
360
|
|
|
328
361
|
describe('FloorIsLava — edge parameter', () => {
|
|
329
362
|
it.each([100, 500, 1000])('cashout applies the given edge (%i bps), not a hardcoded value', edgeBps => {
|
|
330
|
-
const result = expectOk(gameWithRounds(FloorIsLavaDifficulty.MEDIUM, 3).cashout(edgeBps));
|
|
363
|
+
const result = expectOk(gameWithRounds(FloorIsLavaDifficulty.MEDIUM, 3).cashout(fullBoardRng(), edgeBps));
|
|
331
364
|
expect(result.multiplier.isEqualTo(expectedMultiplier(3, 6, edgeBps))).toBe(true);
|
|
332
365
|
});
|
|
333
366
|
|
|
@@ -338,13 +371,13 @@ describe('FloorIsLava — edge parameter', () => {
|
|
|
338
371
|
});
|
|
339
372
|
|
|
340
373
|
it.each([100, 500, 1000])('autobet applies the given edge (%i bps) on a full survive', edgeBps => {
|
|
341
|
-
const rng = new FixedSequenceRng([10, 11, 12, 13, 14, 15, 20, 21, 22, 23, 24, 25]);
|
|
374
|
+
const rng = new FixedSequenceRng(fullDraw([10, 11, 12, 13, 14, 15, 20, 21, 22, 23, 24, 25], 0));
|
|
342
375
|
const result = expectOk(FloorIsLava.autobet(FloorIsLavaDifficulty.MEDIUM, [30, 31], rng, edgeBps));
|
|
343
376
|
expect(result.multiplier.isEqualTo(expectedMultiplier(2, 6, edgeBps))).toBe(true);
|
|
344
377
|
});
|
|
345
378
|
|
|
346
379
|
it('rejects an edge below the platform minimum (100 bps) on cashout', () => {
|
|
347
|
-
expect(() => gameWithRounds(FloorIsLavaDifficulty.MEDIUM, 3).cashout(99)).toThrow('Exceeded Minimum edge allowed on platform');
|
|
380
|
+
expect(() => gameWithRounds(FloorIsLavaDifficulty.MEDIUM, 3).cashout(fullBoardRng(), 99)).toThrow('Exceeded Minimum edge allowed on platform');
|
|
348
381
|
});
|
|
349
382
|
|
|
350
383
|
it('rejects an edge below the platform minimum (100 bps) on a surviving next()', () => {
|
|
@@ -353,7 +386,7 @@ describe('FloorIsLava — edge parameter', () => {
|
|
|
353
386
|
});
|
|
354
387
|
|
|
355
388
|
it('rejects an edge below the platform minimum (100 bps) on a surviving autobet', () => {
|
|
356
|
-
const rng = new FixedSequenceRng([10, 11, 12, 13, 14, 15, 20, 21, 22, 23, 24, 25]);
|
|
389
|
+
const rng = new FixedSequenceRng(fullDraw([10, 11, 12, 13, 14, 15, 20, 21, 22, 23, 24, 25], 0));
|
|
357
390
|
expect(() => FloorIsLava.autobet(FloorIsLavaDifficulty.MEDIUM, [30, 31], rng, 99)).toThrow('Exceeded Minimum edge allowed on platform');
|
|
358
391
|
});
|
|
359
392
|
});
|
|
@@ -429,7 +462,7 @@ describe('FloorIsLava — exhaustive multiplier & RTP across every endpoint', ()
|
|
|
429
462
|
expect(multiplier.toNumber()).toBeCloseTo(houseEdge.dividedBy(chance).toNumber(), 9);
|
|
430
463
|
|
|
431
464
|
// the game surface (fromActions + cashout) returns the same endpoint multiplier
|
|
432
|
-
expect(expectOk(gameWithRounds(difficulty, k).cashout(EDGE_BPS)).multiplier.isEqualTo(multiplier)).toBe(true);
|
|
465
|
+
expect(expectOk(gameWithRounds(difficulty, k).cashout(fullBoardRng(), EDGE_BPS)).multiplier.isEqualTo(multiplier)).toBe(true);
|
|
433
466
|
|
|
434
467
|
// RTP == winChance * payout is the house edge at EVERY endpoint, not just on average
|
|
435
468
|
expect(chance.multipliedBy(multiplier).toNumber()).toBeCloseTo(houseEdge.toNumber(), 9);
|