labag 3.1.0 → 3.1.2

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/labag.js CHANGED
@@ -18,7 +18,7 @@ class LaBaG {
18
18
  this.randomPattern(),
19
19
  ];
20
20
  const multiplier = this.calculateMultiplier(this.reels);
21
- const reward = bet * multiplier;
21
+ const reward = Math.floor(bet * multiplier);
22
22
  return {
23
23
  reels: this.reels,
24
24
  reward,
package/dist/test.d.ts CHANGED
@@ -1 +1,3 @@
1
- export {};
1
+ import { Pattern, Payout } from "./types";
2
+ export declare const patterns: Pattern[];
3
+ export declare const payouts: Payout[];
package/dist/test.js CHANGED
@@ -1,55 +1,56 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.payouts = exports.patterns = void 0;
3
4
  const labag_1 = require("./labag");
4
5
  // --- 設定 (Configuration) ---
5
- const BET_AMOUNT = 200;
6
- const SIMULATION_COUNT = 1_000_000;
6
+ const BET_AMOUNT = 100;
7
+ const SIMULATION_COUNT = 1000000;
7
8
  // --- 資料定義 (Data Definitions) ---
8
- const patterns = [
9
+ exports.patterns = [
9
10
  {
10
- id: 1,
11
+ id: "1",
11
12
  weight: 36,
12
13
  image: "https://fanyu.vercel.app/api/album/item/1mDK1ewfLiV3fAB1HbjvwdSaDTdJdBGG3",
13
14
  },
14
15
  {
15
- id: 2,
16
+ id: "2",
16
17
  weight: 24,
17
18
  image: "https://fanyu.vercel.app/api/album/item/1oB-uZhPPfjfTtG4CITnb3_E-Ops9JTA0",
18
19
  },
19
20
  {
20
- id: 3,
21
+ id: "3",
21
22
  weight: 17,
22
23
  image: "https://fanyu.vercel.app/api/album/item/1bMJdRB8uerQZfGYINzBI9Vaw32bZljl2",
23
24
  },
24
25
  {
25
- id: 4,
26
+ id: "4",
26
27
  weight: 12,
27
28
  image: "https://fanyu.vercel.app/api/album/item/1In8LF1wVfLXpPkp57a20zX84QgsAeLQx",
28
29
  },
29
30
  {
30
- id: 5,
31
+ id: "5",
31
32
  weight: 8,
32
33
  image: "https://fanyu.vercel.app/api/album/item/1Zo_PjrXm-4TBrL2cLAeFkEl1el9kTR56",
33
34
  },
34
35
  {
35
- id: 6,
36
+ id: "6",
36
37
  weight: 3,
37
38
  image: "https://fanyu.vercel.app/api/album/item/19NMnVgcb-9IsknNcfe9TpCyPBIcGnhQU",
38
39
  },
39
40
  ];
40
- const payouts = [
41
- { id: 1, match_count: 2, pattern_id: 1, reward: 56 },
42
- { id: 2, match_count: 3, pattern_id: 1, reward: 242 },
43
- { id: 3, match_count: 2, pattern_id: 2, reward: 119 },
44
- { id: 4, match_count: 3, pattern_id: 2, reward: 578 },
45
- { id: 5, match_count: 2, pattern_id: 3, reward: 266 },
46
- { id: 6, match_count: 3, pattern_id: 3, reward: 1345 },
47
- { id: 7, match_count: 2, pattern_id: 4, reward: 571 },
48
- { id: 8, match_count: 3, pattern_id: 4, reward: 3503 },
49
- { id: 9, match_count: 2, pattern_id: 5, reward: 2136 },
50
- { id: 10, match_count: 3, pattern_id: 5, reward: 11727 },
51
- { id: 11, match_count: 2, pattern_id: 6, reward: 18708 },
52
- { id: 12, match_count: 3, pattern_id: 6, reward: 182200 },
41
+ exports.payouts = [
42
+ { id: "1", match_count: 2, pattern_id: "1", multiplier: 0.56 },
43
+ { id: "2", match_count: 3, pattern_id: "1", multiplier: 2.5 },
44
+ { id: "3", match_count: 2, pattern_id: "2", multiplier: 1.05 },
45
+ { id: "4", match_count: 3, pattern_id: "2", multiplier: 10.0 },
46
+ { id: "5", match_count: 2, pattern_id: "3", multiplier: 1.3 },
47
+ { id: "6", match_count: 3, pattern_id: "3", multiplier: 20.0 },
48
+ { id: "7", match_count: 2, pattern_id: "4", multiplier: 2.1 },
49
+ { id: "8", match_count: 3, pattern_id: "4", multiplier: 45.0 },
50
+ { id: "9", match_count: 2, pattern_id: "5", multiplier: 2.6 },
51
+ { id: "10", match_count: 3, pattern_id: "5", multiplier: 75.0 },
52
+ { id: "11", match_count: 2, pattern_id: "6", multiplier: 7.0 },
53
+ { id: "12", match_count: 3, pattern_id: "6", multiplier: 600.0 },
53
54
  ];
54
55
  // --- 輔助函式 (Helpers) ---
55
56
  /**
@@ -72,6 +73,7 @@ function calculateTheoreticalStats(patterns, payouts, bet) {
72
73
  let ev = 0;
73
74
  let varianceSum = 0; // E[X^2]
74
75
  const hitProbabilities = {};
76
+ const payoutStats = [];
75
77
  for (const payout of payouts) {
76
78
  const pattern = patterns.find((p) => p.id === payout.pattern_id);
77
79
  if (!pattern)
@@ -82,17 +84,26 @@ function calculateTheoreticalStats(patterns, payouts, bet) {
82
84
  // k=3: p^3
83
85
  // k=2: C(3,2) * p^2 * (1-p) = 3 * p^2 * (1-p)
84
86
  const prob = payout.match_count === 3 ? Math.pow(p, 3) : 3 * Math.pow(p, 2) * (1 - p);
85
- ev += prob * payout.reward;
87
+ const reward = Math.floor(payout.multiplier * bet);
88
+ const rtpContribution = ((prob * reward) / bet) * 100; // RTP 貢獻 = (概率 * 獎金 / 投注) * 100
89
+ ev += prob * reward;
86
90
  // 假設賠率互斥: E[X^2] = sum(prob * reward^2)
87
91
  // Assuming disjoint payouts: E[X^2] = sum(prob * reward^2)
88
- varianceSum += prob * Math.pow(payout.reward, 2);
89
- hitProbabilities[payout.reward] =
90
- (hitProbabilities[payout.reward] || 0) + prob;
92
+ varianceSum += prob * Math.pow(reward, 2);
93
+ hitProbabilities[reward] = (hitProbabilities[reward] || 0) + prob;
94
+ payoutStats.push({
95
+ id: payout.id,
96
+ patternId: payout.pattern_id,
97
+ matchCount: payout.match_count,
98
+ prob,
99
+ reward,
100
+ rtpContribution,
101
+ });
91
102
  }
92
103
  const variance = varianceSum - Math.pow(ev, 2);
93
104
  const stdDev = Math.sqrt(variance);
94
105
  const rtp = (ev / bet) * 100;
95
- return { ev, rtp, stdDev, hitProbabilities };
106
+ return { ev, rtp, stdDev, hitProbabilities, payoutStats };
96
107
  }
97
108
  // --- 模擬邏輯 (Simulation Logic) ---
98
109
  function runSimulation(game, count) {
@@ -101,7 +112,7 @@ function runSimulation(game, count) {
101
112
  let maxWin = 0;
102
113
  const hitFrequency = {};
103
114
  for (let i = 0; i < count; i++) {
104
- const result = game.spin();
115
+ const result = game.spin(BET_AMOUNT);
105
116
  totalReward += result.reward;
106
117
  if (result.reward > 0) {
107
118
  winCount++;
@@ -120,7 +131,7 @@ function runSimulation(game, count) {
120
131
  // --- 主要執行區塊 (Main Execution) ---
121
132
  // 1. 理論分析 (Theoretical Analysis)
122
133
  console.log("正在計算理論數值...");
123
- const theo = calculateTheoreticalStats(patterns, payouts, BET_AMOUNT);
134
+ const theo = calculateTheoreticalStats(exports.patterns, exports.payouts, BET_AMOUNT);
124
135
  console.log("==========================================");
125
136
  console.log(" 理論分析 (Theoretical) ");
126
137
  console.log("==========================================");
@@ -128,9 +139,19 @@ console.log(`理論期望值 (EV) : ${theo.ev.toFixed(2)}`);
128
139
  console.log(`理論 RTP : ${theo.rtp.toFixed(2)}%`);
129
140
  console.log(`標準差 (SD) : ${theo.stdDev.toFixed(2)}`);
130
141
  console.log("------------------------------------------");
142
+ console.log("\n==========================================");
143
+ console.log(" RTP 貢獻分析 (RTP Contribution) ");
144
+ console.log("==========================================");
145
+ console.log(`| ID | Pattern | Count | Reward | Prob (%) | RTP Contrib (%) |`);
146
+ console.log(`|----|---------|-------|--------|----------|-----------------|`);
147
+ theo.payoutStats.forEach((stat) => {
148
+ console.log(`| ${stat.id.padEnd(2)} | ${stat.patternId.padEnd(7)} | ${stat.matchCount} | ${stat.reward.toString().padEnd(6)} | ${(stat.prob * 100).toFixed(4).padEnd(8)} | ${stat.rtpContribution.toFixed(4).padEnd(15)} |`);
149
+ });
150
+ console.log("------------------------------------------");
151
+ console.log(`總 RTP: ${theo.rtp.toFixed(2)}%`);
131
152
  // 2. 模擬 (Simulation)
132
153
  console.log(`\n正在執行模擬 (n=${formatCurrency(SIMULATION_COUNT)})...`);
133
- const game = new labag_1.LaBaG(patterns, payouts);
154
+ const game = new labag_1.LaBaG(exports.patterns, exports.payouts);
134
155
  const sim = runSimulation(game, SIMULATION_COUNT);
135
156
  const simEV = sim.totalReward / SIMULATION_COUNT;
136
157
  const simRTP = (simEV / BET_AMOUNT) * 100;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "labag",
3
- "version": "3.1.0",
3
+ "version": "3.1.2",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/labag.ts CHANGED
@@ -18,7 +18,7 @@ export class LaBaG {
18
18
  this.randomPattern(),
19
19
  ];
20
20
  const multiplier = this.calculateMultiplier(this.reels);
21
- const reward = bet * multiplier;
21
+ const reward = Math.floor(bet * multiplier);
22
22
  return {
23
23
  reels: this.reels,
24
24
  reward,
package/src/test.ts CHANGED
@@ -2,62 +2,65 @@ import { LaBaG } from "./labag";
2
2
  import { Pattern, Payout } from "./types";
3
3
 
4
4
  // --- 設定 (Configuration) ---
5
- const BET_AMOUNT = 200;
6
- const SIMULATION_COUNT = 1_000_000;
5
+ const BET_AMOUNT = 100;
6
+ const SIMULATION_COUNT = 1000000;
7
7
 
8
8
  // --- 資料定義 (Data Definitions) ---
9
- const patterns: Pattern[] = [
9
+
10
+ export const patterns: Pattern[] = [
10
11
  {
11
- id: 1,
12
+ id: "1",
12
13
  weight: 36,
13
14
  image:
14
15
  "https://fanyu.vercel.app/api/album/item/1mDK1ewfLiV3fAB1HbjvwdSaDTdJdBGG3",
15
16
  },
16
17
  {
17
- id: 2,
18
+ id: "2",
18
19
  weight: 24,
19
20
  image:
20
21
  "https://fanyu.vercel.app/api/album/item/1oB-uZhPPfjfTtG4CITnb3_E-Ops9JTA0",
21
22
  },
23
+
22
24
  {
23
- id: 3,
25
+ id: "3",
24
26
  weight: 17,
25
27
  image:
26
28
  "https://fanyu.vercel.app/api/album/item/1bMJdRB8uerQZfGYINzBI9Vaw32bZljl2",
27
29
  },
28
30
  {
29
- id: 4,
31
+ id: "4",
30
32
  weight: 12,
31
33
  image:
32
34
  "https://fanyu.vercel.app/api/album/item/1In8LF1wVfLXpPkp57a20zX84QgsAeLQx",
33
35
  },
36
+
34
37
  {
35
- id: 5,
38
+ id: "5",
36
39
  weight: 8,
37
40
  image:
38
41
  "https://fanyu.vercel.app/api/album/item/1Zo_PjrXm-4TBrL2cLAeFkEl1el9kTR56",
39
42
  },
40
43
  {
41
- id: 6,
44
+ id: "6",
42
45
  weight: 3,
43
46
  image:
44
47
  "https://fanyu.vercel.app/api/album/item/19NMnVgcb-9IsknNcfe9TpCyPBIcGnhQU",
45
48
  },
46
49
  ];
47
50
 
48
- const payouts: Payout[] = [
49
- { id: 1, match_count: 2, pattern_id: 1, reward: 56 },
50
- { id: 2, match_count: 3, pattern_id: 1, reward: 242 },
51
- { id: 3, match_count: 2, pattern_id: 2, reward: 119 },
52
- { id: 4, match_count: 3, pattern_id: 2, reward: 578 },
53
- { id: 5, match_count: 2, pattern_id: 3, reward: 266 },
54
- { id: 6, match_count: 3, pattern_id: 3, reward: 1345 },
55
- { id: 7, match_count: 2, pattern_id: 4, reward: 571 },
56
- { id: 8, match_count: 3, pattern_id: 4, reward: 3503 },
57
- { id: 9, match_count: 2, pattern_id: 5, reward: 2136 },
58
- { id: 10, match_count: 3, pattern_id: 5, reward: 11727 },
59
- { id: 11, match_count: 2, pattern_id: 6, reward: 18708 },
60
- { id: 12, match_count: 3, pattern_id: 6, reward: 182200 },
51
+ export const payouts: Payout[] = [
52
+ { id: "1", match_count: 2, pattern_id: "1", multiplier: 0.56 },
53
+ { id: "2", match_count: 3, pattern_id: "1", multiplier: 2.5 },
54
+ { id: "3", match_count: 2, pattern_id: "2", multiplier: 1.05 },
55
+ { id: "4", match_count: 3, pattern_id: "2", multiplier: 10.0 },
56
+ { id: "5", match_count: 2, pattern_id: "3", multiplier: 1.3 },
57
+ { id: "6", match_count: 3, pattern_id: "3", multiplier: 20.0 },
58
+ { id: "7", match_count: 2, pattern_id: "4", multiplier: 2.1 },
59
+ { id: "8", match_count: 3, pattern_id: "4", multiplier: 45.0 },
60
+ { id: "9", match_count: 2, pattern_id: "5", multiplier: 2.6 },
61
+ { id: "10", match_count: 3, pattern_id: "5", multiplier: 75.0 },
62
+ { id: "11", match_count: 2, pattern_id: "6", multiplier: 7.0 },
63
+ { id: "12", match_count: 3, pattern_id: "6", multiplier: 600.0 },
61
64
  ];
62
65
 
63
66
  // --- 輔助函式 (Helpers) ---
@@ -90,6 +93,15 @@ function calculateTheoreticalStats(
90
93
  let varianceSum = 0; // E[X^2]
91
94
  const hitProbabilities: Record<number, number> = {};
92
95
 
96
+ const payoutStats: {
97
+ id: string;
98
+ patternId: string;
99
+ matchCount: number;
100
+ prob: number;
101
+ reward: number;
102
+ rtpContribution: number;
103
+ }[] = [];
104
+
93
105
  for (const payout of payouts) {
94
106
  const pattern = patterns.find((p) => p.id === payout.pattern_id);
95
107
  if (!pattern) continue;
@@ -102,20 +114,31 @@ function calculateTheoreticalStats(
102
114
  const prob =
103
115
  payout.match_count === 3 ? Math.pow(p, 3) : 3 * Math.pow(p, 2) * (1 - p);
104
116
 
105
- ev += prob * payout.reward;
117
+ const reward = Math.floor(payout.multiplier * bet);
118
+ const rtpContribution = ((prob * reward) / bet) * 100; // RTP 貢獻 = (概率 * 獎金 / 投注) * 100
119
+
120
+ ev += prob * reward;
106
121
  // 假設賠率互斥: E[X^2] = sum(prob * reward^2)
107
122
  // Assuming disjoint payouts: E[X^2] = sum(prob * reward^2)
108
- varianceSum += prob * Math.pow(payout.reward, 2);
109
-
110
- hitProbabilities[payout.reward] =
111
- (hitProbabilities[payout.reward] || 0) + prob;
123
+ varianceSum += prob * Math.pow(reward, 2);
124
+
125
+ hitProbabilities[reward] = (hitProbabilities[reward] || 0) + prob;
126
+
127
+ payoutStats.push({
128
+ id: payout.id,
129
+ patternId: payout.pattern_id,
130
+ matchCount: payout.match_count,
131
+ prob,
132
+ reward,
133
+ rtpContribution,
134
+ });
112
135
  }
113
136
 
114
137
  const variance = varianceSum - Math.pow(ev, 2);
115
138
  const stdDev = Math.sqrt(variance);
116
139
  const rtp = (ev / bet) * 100;
117
140
 
118
- return { ev, rtp, stdDev, hitProbabilities };
141
+ return { ev, rtp, stdDev, hitProbabilities, payoutStats };
119
142
  }
120
143
 
121
144
  // --- 模擬邏輯 (Simulation Logic) ---
@@ -126,7 +149,7 @@ function runSimulation(game: LaBaG, count: number) {
126
149
  const hitFrequency: Record<number, number> = {};
127
150
 
128
151
  for (let i = 0; i < count; i++) {
129
- const result = game.spin();
152
+ const result = game.spin(BET_AMOUNT);
130
153
  totalReward += result.reward;
131
154
 
132
155
  if (result.reward > 0) {
@@ -158,6 +181,20 @@ console.log(`理論 RTP : ${theo.rtp.toFixed(2)}%`);
158
181
  console.log(`標準差 (SD) : ${theo.stdDev.toFixed(2)}`);
159
182
  console.log("------------------------------------------");
160
183
 
184
+ console.log("\n==========================================");
185
+ console.log(" RTP 貢獻分析 (RTP Contribution) ");
186
+ console.log("==========================================");
187
+ console.log(`| ID | Pattern | Count | Reward | Prob (%) | RTP Contrib (%) |`);
188
+ console.log(`|----|---------|-------|--------|----------|-----------------|`);
189
+
190
+ theo.payoutStats.forEach((stat) => {
191
+ console.log(
192
+ `| ${stat.id.padEnd(2)} | ${stat.patternId.padEnd(7)} | ${stat.matchCount} | ${stat.reward.toString().padEnd(6)} | ${(stat.prob * 100).toFixed(4).padEnd(8)} | ${stat.rtpContribution.toFixed(4).padEnd(15)} |`,
193
+ );
194
+ });
195
+ console.log("------------------------------------------");
196
+ console.log(`總 RTP: ${theo.rtp.toFixed(2)}%`);
197
+
161
198
  // 2. 模擬 (Simulation)
162
199
  console.log(`\n正在執行模擬 (n=${formatCurrency(SIMULATION_COUNT)})...`);
163
200
  const game = new LaBaG(patterns, payouts);