labag 1.2.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +3 -6
- package/dist/index.js +190 -155
- package/dist/labag.d.ts +78 -0
- package/dist/labag.js +222 -0
- package/dist/mode.d.ts +30 -0
- package/dist/mode.js +51 -0
- package/dist/pattern.d.ts +22 -0
- package/dist/pattern.js +32 -0
- package/dist/test.d.ts +1 -0
- package/dist/test.js +35 -0
- package/dist/types/index.d.ts +18 -0
- package/dist/types/index.js +2 -0
- package/dist/utils/RandInt.d.ts +7 -1
- package/dist/utils/RandInt.js +9 -4
- package/package.json +2 -2
- package/src/index.ts +191 -169
- package/src/labag.ts +239 -0
- package/src/mode.ts +61 -0
- package/src/pattern.ts +31 -0
- package/src/test.ts +37 -0
- package/src/types/index.ts +28 -0
- package/src/utils/randInt.ts +8 -0
- package/tsconfig.json +13 -13
- package/src/types/BaseLaBaG.ts +0 -193
- package/src/types/JsonLaBaG.ts +0 -70
- package/src/types/LaBaG.ts +0 -37
- package/src/types/Mode.ts +0 -13
- package/src/types/P.ts +0 -40
- package/src/types/PlayLaBaG.ts +0 -80
- package/src/utils/RandInt.ts +0 -3
- package/src/utils/data.ts +0 -12
package/src/labag.ts
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { Mode } from "./mode";
|
|
2
|
+
import { patterns } from "./pattern";
|
|
3
|
+
import { Pattern, LaBaGEvent, PatternName } from "./types";
|
|
4
|
+
import { randInt } from "./utils/randInt";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 拉霸遊戲的主要類別。
|
|
8
|
+
*/
|
|
9
|
+
export class LaBaG {
|
|
10
|
+
/** 總遊玩次數限制 */
|
|
11
|
+
times: number;
|
|
12
|
+
/** 已遊玩次數 */
|
|
13
|
+
played: number;
|
|
14
|
+
/** 當前進行的輪次 */
|
|
15
|
+
rounds: number;
|
|
16
|
+
/** 當前分數 */
|
|
17
|
+
score: number;
|
|
18
|
+
/** 邊際分數 */
|
|
19
|
+
marginScore: number;
|
|
20
|
+
/** 當前轉出的圖案組合 */
|
|
21
|
+
patterns: [Pattern | null, Pattern | null, Pattern | null];
|
|
22
|
+
/** 遊戲模式列表 */
|
|
23
|
+
modes: Mode[];
|
|
24
|
+
/** 事件監聽器列表 */
|
|
25
|
+
eventListeners: Record<LaBaGEvent, ((game: LaBaG) => void)[]>;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 初始化拉霸遊戲。
|
|
29
|
+
* @param times - 總遊玩次數,預設為 30。
|
|
30
|
+
*/
|
|
31
|
+
constructor(times: number = 30) {
|
|
32
|
+
this.times = times;
|
|
33
|
+
this.played = 0;
|
|
34
|
+
this.rounds = 0;
|
|
35
|
+
this.score = 0;
|
|
36
|
+
this.marginScore = 0;
|
|
37
|
+
this.patterns = [null, null, null];
|
|
38
|
+
this.modes = [];
|
|
39
|
+
this.eventListeners = {
|
|
40
|
+
gameOver: [],
|
|
41
|
+
gameStart: [],
|
|
42
|
+
roundStart: [],
|
|
43
|
+
roundEnd: [],
|
|
44
|
+
rollSlots: [],
|
|
45
|
+
calculateScore: [],
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 觸發指定事件。
|
|
51
|
+
* @param event - 要觸發的事件名稱。
|
|
52
|
+
*/
|
|
53
|
+
private emit(event: LaBaGEvent) {
|
|
54
|
+
this.eventListeners[event].forEach((fn) => fn(this));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* 新增事件監聽器。
|
|
59
|
+
* @param event - 事件名稱。
|
|
60
|
+
* @param listener - 監聽器函式。
|
|
61
|
+
*/
|
|
62
|
+
addEventListener(event: LaBaGEvent, callbackFn: (game: LaBaG) => void) {
|
|
63
|
+
this.eventListeners[event].push(callbackFn);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* 新增遊戲模式。
|
|
68
|
+
* @param mode - 要新增的模式。
|
|
69
|
+
*/
|
|
70
|
+
addMode(mode: Mode) {
|
|
71
|
+
this.modes.push(mode);
|
|
72
|
+
// 註冊特定模式的監聽器
|
|
73
|
+
Object.entries(mode.eventListener).forEach(([event, listener]) => {
|
|
74
|
+
if (listener) {
|
|
75
|
+
this.addEventListener(event as LaBaGEvent, (game) =>
|
|
76
|
+
listener(game, mode)
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* 檢查遊戲是否正在進行中(未達次數上限)。
|
|
84
|
+
* @returns 如果遊戲仍在進行中則返回 true,否則返回 false。
|
|
85
|
+
*/
|
|
86
|
+
isRunning() {
|
|
87
|
+
return this.played < this.times;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* 取得目前遊戲的相關設定
|
|
92
|
+
*/
|
|
93
|
+
getCurrentConfig() {
|
|
94
|
+
const activeModes = this.modes.filter((m) => m.active);
|
|
95
|
+
if (activeModes.length === 0) {
|
|
96
|
+
console.warn("目前沒有啟用中的模式,無法轉動拉霸機。");
|
|
97
|
+
return { modes: activeModes, ranges: [] };
|
|
98
|
+
}
|
|
99
|
+
// 合併所有啟用中模式的機率設定
|
|
100
|
+
const combinedRates: Record<PatternName, number> = {
|
|
101
|
+
gss: 0,
|
|
102
|
+
hhh: 0,
|
|
103
|
+
hentai: 0,
|
|
104
|
+
handson: 0,
|
|
105
|
+
kachu: 0,
|
|
106
|
+
rrr: 0,
|
|
107
|
+
};
|
|
108
|
+
activeModes.forEach((mode) => {
|
|
109
|
+
Object.entries(mode.rates).forEach(([patternName, rate]) => {
|
|
110
|
+
combinedRates[patternName as PatternName] += rate;
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// 預先計算合併後的區間
|
|
115
|
+
const ranges: { threshold: number; pattern: Pattern }[] = [];
|
|
116
|
+
let acc = 0;
|
|
117
|
+
for (const pattern of patterns) {
|
|
118
|
+
const rate = combinedRates[pattern.name];
|
|
119
|
+
if (rate !== undefined) {
|
|
120
|
+
acc += rate;
|
|
121
|
+
ranges.push({ threshold: acc, pattern });
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return { modes: activeModes, ranges };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* 初始化遊戲狀態。
|
|
130
|
+
*/
|
|
131
|
+
init() {
|
|
132
|
+
this.played = 0;
|
|
133
|
+
this.score = 0;
|
|
134
|
+
this.marginScore = 0;
|
|
135
|
+
this.patterns = [null, null, null];
|
|
136
|
+
this.rounds = 0;
|
|
137
|
+
this.emit("gameStart");
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* 新的一小輪開始。
|
|
142
|
+
*/
|
|
143
|
+
private roundStart() {
|
|
144
|
+
this.played += 1;
|
|
145
|
+
this.rounds += 1;
|
|
146
|
+
this.marginScore = 0;
|
|
147
|
+
this.emit("roundStart");
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* 轉動拉霸機,產生隨機圖案。
|
|
152
|
+
*/
|
|
153
|
+
private rollSlots() {
|
|
154
|
+
const { ranges } = this.getCurrentConfig();
|
|
155
|
+
const rangesAcc =
|
|
156
|
+
ranges.length > 0 ? ranges[ranges.length - 1].threshold : 0;
|
|
157
|
+
// 產生 3 個隨機數字
|
|
158
|
+
const randomNums = [
|
|
159
|
+
randInt(1, rangesAcc),
|
|
160
|
+
randInt(1, rangesAcc),
|
|
161
|
+
randInt(1, rangesAcc),
|
|
162
|
+
];
|
|
163
|
+
|
|
164
|
+
randomNums.forEach((num, index) => {
|
|
165
|
+
// 根據預先計算的區間找到對應的圖案
|
|
166
|
+
const match = ranges.find((r) => num <= r.threshold);
|
|
167
|
+
if (match) {
|
|
168
|
+
this.patterns[index] = match.pattern;
|
|
169
|
+
} else {
|
|
170
|
+
this.patterns[index] = null;
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
this.emit("rollSlots");
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* 計算分數。
|
|
179
|
+
*/
|
|
180
|
+
private calculateScore() {
|
|
181
|
+
const [p1, p2, p3] = this.patterns;
|
|
182
|
+
if (!p1 || !p2 || !p3) {
|
|
183
|
+
console.warn("圖案未正確生成,無法計算分數。");
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
if (p1.name === p2.name && p2.name === p3.name) {
|
|
187
|
+
// 三個圖案相同
|
|
188
|
+
this.marginScore += p1.scores[0];
|
|
189
|
+
} else if (
|
|
190
|
+
p1.name === p2.name ||
|
|
191
|
+
p2.name === p3.name ||
|
|
192
|
+
p1.name === p3.name
|
|
193
|
+
) {
|
|
194
|
+
// 兩個圖案相同
|
|
195
|
+
if (p1.name === p2.name) {
|
|
196
|
+
this.marginScore += p1.scores[1];
|
|
197
|
+
this.marginScore += p3.scores[2];
|
|
198
|
+
} else if (p2.name === p3.name) {
|
|
199
|
+
this.marginScore += p2.scores[1];
|
|
200
|
+
this.marginScore += p1.scores[2];
|
|
201
|
+
} else {
|
|
202
|
+
this.marginScore += p1.scores[1];
|
|
203
|
+
this.marginScore += p2.scores[2];
|
|
204
|
+
}
|
|
205
|
+
this.marginScore = Math.round(this.marginScore / 1.4);
|
|
206
|
+
} else {
|
|
207
|
+
// 三個圖案皆不同
|
|
208
|
+
this.marginScore += p1.scores[2];
|
|
209
|
+
this.marginScore += p2.scores[2];
|
|
210
|
+
this.marginScore += p3.scores[2];
|
|
211
|
+
this.marginScore = Math.round(this.marginScore / 3);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
this.emit("calculateScore");
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
private roundEnd() {
|
|
218
|
+
this.score += this.marginScore;
|
|
219
|
+
this.emit("roundEnd");
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
private gameOver() {
|
|
223
|
+
this.emit("gameOver");
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
play() {
|
|
227
|
+
if (!this.isRunning()) {
|
|
228
|
+
console.warn("遊戲次數已達上限,無法繼續遊玩。");
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
this.roundStart();
|
|
232
|
+
this.rollSlots();
|
|
233
|
+
this.calculateScore();
|
|
234
|
+
this.roundEnd();
|
|
235
|
+
if (!this.isRunning()) {
|
|
236
|
+
this.gameOver();
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
package/src/mode.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { LaBaG } from "./labag";
|
|
2
|
+
import { patterns } from "./pattern";
|
|
3
|
+
import { LaBaGEvent, Pattern, PatternName } from "./types";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 代表遊戲的一種模式,包含機率設定和事件監聽器。
|
|
7
|
+
*/
|
|
8
|
+
export class Mode {
|
|
9
|
+
/** 模式是否啟用 */
|
|
10
|
+
active: boolean;
|
|
11
|
+
/** 模式名稱 */
|
|
12
|
+
name: string;
|
|
13
|
+
/** 各圖案出現的機率 */
|
|
14
|
+
rates: Record<PatternName, number>;
|
|
15
|
+
// 預先計算的區間,用於高效查找
|
|
16
|
+
ranges: { threshold: number; pattern: Pattern }[];
|
|
17
|
+
/** 事件監聽器 */
|
|
18
|
+
eventListener: Partial<Record<LaBaGEvent, (game: LaBaG, mode: Mode) => void>>;
|
|
19
|
+
|
|
20
|
+
/** 模式專屬的變數儲存空間 */
|
|
21
|
+
variable: Record<string, any>;
|
|
22
|
+
/** 機率總和 */
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 建立一個新的模式。
|
|
26
|
+
* @param active - 是否啟用此模式。
|
|
27
|
+
* @param name - 模式名稱。
|
|
28
|
+
* @param rates - 各圖案的機率設定。
|
|
29
|
+
* @param eventListener - 事件監聽器。
|
|
30
|
+
*/
|
|
31
|
+
constructor(
|
|
32
|
+
active: boolean,
|
|
33
|
+
name: string,
|
|
34
|
+
rates: Record<PatternName, number>,
|
|
35
|
+
eventListener?: Partial<
|
|
36
|
+
Record<LaBaGEvent, (game: LaBaG, mode: Mode) => void>
|
|
37
|
+
>,
|
|
38
|
+
variable?: Record<string, any>
|
|
39
|
+
) {
|
|
40
|
+
this.active = active;
|
|
41
|
+
this.name = name;
|
|
42
|
+
this.rates = rates;
|
|
43
|
+
this.eventListener = eventListener ?? {};
|
|
44
|
+
this.variable = variable ?? {};
|
|
45
|
+
|
|
46
|
+
// 預先計算機率區間
|
|
47
|
+
this.ranges = [];
|
|
48
|
+
let acc = 0;
|
|
49
|
+
// 遍歷定義的圖案以確保順序一致
|
|
50
|
+
for (const pattern of patterns) {
|
|
51
|
+
const rate = rates[pattern.name];
|
|
52
|
+
if (rate !== undefined) {
|
|
53
|
+
acc += rate;
|
|
54
|
+
this.ranges.push({ threshold: acc, pattern });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (acc > 100) {
|
|
58
|
+
console.warn(`模式 "${name}" 的機率總和超過 100%,請檢查設定。`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
package/src/pattern.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Pattern } from "./types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 定義遊戲中可用的圖案及其分數。
|
|
5
|
+
*/
|
|
6
|
+
export const patterns = [
|
|
7
|
+
{
|
|
8
|
+
name: "gss",
|
|
9
|
+
scores: [800, 400, 180],
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
name: "hhh",
|
|
13
|
+
scores: [1500, 800, 300],
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
name: "hentai",
|
|
17
|
+
scores: [2500, 1200, 500],
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: "handson",
|
|
21
|
+
scores: [2900, 1450, 690],
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: "kachu",
|
|
25
|
+
scores: [12000, 8000, 1250],
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: "rrr",
|
|
29
|
+
scores: [20000, 12000, 2500],
|
|
30
|
+
},
|
|
31
|
+
] as const satisfies readonly Pattern[];
|
package/src/test.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import labag from "./index";
|
|
2
|
+
import { Pattern } from "./types";
|
|
3
|
+
labag.addEventListener("gameStart", (game) => {
|
|
4
|
+
console.log("Game Started!");
|
|
5
|
+
console.log(`Total Rounds: ${game.times}\n`);
|
|
6
|
+
});
|
|
7
|
+
labag.addEventListener("roundStart", (game) => {
|
|
8
|
+
console.log(`--- Round ${game.rounds} Start ---`);
|
|
9
|
+
});
|
|
10
|
+
labag.addEventListener("rollSlots", (game) => {
|
|
11
|
+
const { modes, ranges } = game.getCurrentConfig();
|
|
12
|
+
console.log(`Active Modes: ${modes.map((m) => m.name).join(", ")}`);
|
|
13
|
+
console.log(
|
|
14
|
+
`Probability Ranges: ${ranges
|
|
15
|
+
.map((r) => `${r.pattern.name}<=${r.threshold}`)
|
|
16
|
+
.join(", ")}`
|
|
17
|
+
);
|
|
18
|
+
});
|
|
19
|
+
labag.addEventListener("roundEnd", (game) => {
|
|
20
|
+
console.log(game.patterns.map((p) => (p ? p.name : "null")).join(" | "));
|
|
21
|
+
console.log(`Margin Score: ${game.marginScore}`);
|
|
22
|
+
console.log(`Score: ${game.score}\n`);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
labag.init();
|
|
26
|
+
while (labag.isRunning()) {
|
|
27
|
+
labag.play();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
console.log("Game Over");
|
|
31
|
+
console.log(`Final Score: ${labag.score}`);
|
|
32
|
+
console.log(
|
|
33
|
+
`Active Modes at end: ${labag
|
|
34
|
+
.getCurrentConfig()
|
|
35
|
+
.modes.map((m) => m.name)
|
|
36
|
+
.join(", ")}`
|
|
37
|
+
);
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { patterns } from "src/pattern";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 拉霸遊戲的事件類型。
|
|
5
|
+
*/
|
|
6
|
+
export type LaBaGEvent =
|
|
7
|
+
| "gameOver"
|
|
8
|
+
| "gameStart"
|
|
9
|
+
| "roundStart"
|
|
10
|
+
| "roundEnd"
|
|
11
|
+
| "rollSlots"
|
|
12
|
+
| "calculateScore";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 代表一個圖案及其對應的分數。
|
|
16
|
+
*/
|
|
17
|
+
export type Pattern = {
|
|
18
|
+
/** 圖案名稱 */
|
|
19
|
+
name: string;
|
|
20
|
+
/** 對應的分數陣列 */
|
|
21
|
+
scores: number[];
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 圖案名稱的型別。
|
|
26
|
+
*/
|
|
27
|
+
export type PatternName = (typeof patterns)[number]["name"];
|
|
28
|
+
|
package/tsconfig.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
|
12
12
|
|
|
13
13
|
/* Language and Environment */
|
|
14
|
-
"target": "ES2024"
|
|
14
|
+
"target": "ES2024" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
|
|
15
15
|
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
|
16
16
|
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
|
17
17
|
// "libReplacement": true, /* Enable lib replacement. */
|
|
@@ -26,11 +26,11 @@
|
|
|
26
26
|
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
|
27
27
|
|
|
28
28
|
/* Modules */
|
|
29
|
-
"module": "
|
|
29
|
+
"module": "node16" /* Specify what module code is generated. */,
|
|
30
30
|
// "rootDir": "./", /* Specify the root folder within your source files. */
|
|
31
|
-
"moduleResolution": "
|
|
32
|
-
"baseUrl": "./"
|
|
33
|
-
// "paths": {},
|
|
31
|
+
"moduleResolution": "node16" /* Specify how TypeScript looks up a file from a given module specifier. */,
|
|
32
|
+
"baseUrl": "./" /* Specify the base directory to resolve non-relative module names. */,
|
|
33
|
+
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
|
34
34
|
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
|
35
35
|
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
|
|
36
36
|
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
|
@@ -42,24 +42,24 @@
|
|
|
42
42
|
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
|
|
43
43
|
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
|
|
44
44
|
// "noUncheckedSideEffectImports": true, /* Check side effect imports. */
|
|
45
|
-
"resolveJsonModule": true
|
|
45
|
+
"resolveJsonModule": true /* Enable importing .json files. */,
|
|
46
46
|
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
|
|
47
47
|
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
|
48
48
|
|
|
49
49
|
/* JavaScript Support */
|
|
50
|
-
"allowJs": true
|
|
50
|
+
"allowJs": true /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */,
|
|
51
51
|
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
|
52
52
|
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
|
|
53
53
|
|
|
54
54
|
/* Emit */
|
|
55
|
-
"declaration": true
|
|
55
|
+
"declaration": true /* Generate .d.ts files from TypeScript and JavaScript files in your project. */,
|
|
56
56
|
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
|
57
57
|
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
|
58
58
|
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
|
59
59
|
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
|
60
60
|
// "noEmit": true, /* Disable emitting files from a compilation. */
|
|
61
61
|
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
|
62
|
-
"outDir": "./dist"
|
|
62
|
+
"outDir": "./dist" /* Specify an output folder for all emitted files. */,
|
|
63
63
|
// "removeComments": true, /* Disable emitting comments. */
|
|
64
64
|
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
|
65
65
|
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
|
@@ -80,12 +80,12 @@
|
|
|
80
80
|
// "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */
|
|
81
81
|
// "erasableSyntaxOnly": true, /* Do not allow runtime constructs that are not part of ECMAScript. */
|
|
82
82
|
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
|
83
|
-
"esModuleInterop": true
|
|
83
|
+
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
|
|
84
84
|
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
|
85
|
-
"forceConsistentCasingInFileNames": true
|
|
85
|
+
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
|
|
86
86
|
|
|
87
87
|
/* Type Checking */
|
|
88
|
-
"strict": true
|
|
88
|
+
"strict": true /* Enable all strict type-checking options. */,
|
|
89
89
|
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
|
90
90
|
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
|
|
91
91
|
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
|
@@ -108,6 +108,6 @@
|
|
|
108
108
|
|
|
109
109
|
/* Completeness */
|
|
110
110
|
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
|
111
|
-
"skipLibCheck": true
|
|
111
|
+
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
|
112
112
|
}
|
|
113
113
|
}
|
package/src/types/BaseLaBaG.ts
DELETED
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
import { Modes } from "..";
|
|
2
|
-
import { AllDataType, LaBaG, OneDataType } from "./LaBaG";
|
|
3
|
-
import { Mode, ModeNames } from "./Mode";
|
|
4
|
-
import { P } from "./P";
|
|
5
|
-
import { RandInt } from "../utils/RandInt";
|
|
6
|
-
|
|
7
|
-
export class BaseLaBaG implements LaBaG {
|
|
8
|
-
AllData: AllDataType = {};
|
|
9
|
-
OneData: OneDataType = {};
|
|
10
|
-
DataIndex: number = 0;
|
|
11
|
-
|
|
12
|
-
Times: number = 30;
|
|
13
|
-
Played: number = 0;
|
|
14
|
-
|
|
15
|
-
Score: number = 0;
|
|
16
|
-
MarginScore: number = 0;
|
|
17
|
-
|
|
18
|
-
Ps: [P | null, P | null, P | null] = [null, null, null];
|
|
19
|
-
|
|
20
|
-
RateRanges: Record<ModeNames, number[]> = [
|
|
21
|
-
"Normal",
|
|
22
|
-
"SuperHHH",
|
|
23
|
-
"GreenWei",
|
|
24
|
-
"PiKaChu",
|
|
25
|
-
].reduce((Ranges: Record<ModeNames, number[]>, mode: string) => {
|
|
26
|
-
const res: number[] = [];
|
|
27
|
-
let accRate: number = 0;
|
|
28
|
-
for (const p of P.Map.values()) {
|
|
29
|
-
accRate += p.rates[mode as ModeNames];
|
|
30
|
-
res.push(accRate);
|
|
31
|
-
}
|
|
32
|
-
Ranges[mode as ModeNames] = res;
|
|
33
|
-
return Ranges;
|
|
34
|
-
}, {} as Record<ModeNames, number[]>);
|
|
35
|
-
|
|
36
|
-
ScoreTimes: Record<ModeNames, number> = {
|
|
37
|
-
Normal: 1,
|
|
38
|
-
SuperHHH: 1,
|
|
39
|
-
GreenWei: 3,
|
|
40
|
-
PiKaChu: 1,
|
|
41
|
-
};
|
|
42
|
-
ScoreTime: number = 1;
|
|
43
|
-
|
|
44
|
-
ModeToScreen: boolean = false;
|
|
45
|
-
|
|
46
|
-
constructor() {}
|
|
47
|
-
|
|
48
|
-
GameRunning(): boolean {
|
|
49
|
-
//遊戲進行
|
|
50
|
-
return this.Times > this.Played;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
NowMode(): ModeNames {
|
|
54
|
-
// 查找當前模式
|
|
55
|
-
const mode = Object.entries(Modes).find(
|
|
56
|
-
([_, mode]: [string, Mode]) => mode.InMode ?? false
|
|
57
|
-
);
|
|
58
|
-
return mode ? (mode[0] as ModeNames) : "Normal";
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
Reset() {
|
|
62
|
-
// 重置
|
|
63
|
-
this.AllData = {};
|
|
64
|
-
this.DataIndex = 0;
|
|
65
|
-
|
|
66
|
-
this.Played = 0;
|
|
67
|
-
this.Score = 0;
|
|
68
|
-
this.MarginScore = 0;
|
|
69
|
-
this.ScoreTime = 1;
|
|
70
|
-
|
|
71
|
-
this.Ps = [null, null, null];
|
|
72
|
-
|
|
73
|
-
Object.values(Modes).forEach((mode: Mode) => {
|
|
74
|
-
if (mode.InMode !== undefined) mode.InMode = false;
|
|
75
|
-
if (mode.Score !== undefined) mode.Score = 0;
|
|
76
|
-
if (mode.RandNum !== undefined) mode.RandNum = 0;
|
|
77
|
-
if (mode.Times !== undefined) mode.Times = 0;
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
Random(): void {
|
|
82
|
-
const RandNums: [number, number, number] = Array.from({ length: 3 }, () =>
|
|
83
|
-
RandInt(1, 100)
|
|
84
|
-
) as [number, number, number];
|
|
85
|
-
|
|
86
|
-
RandNums.forEach((RandNum: number, index: number) => {
|
|
87
|
-
this.OneData[`RandNums[${index}]` as `RandNums[${0 | 1 | 2}]`] = RandNum;
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
Object.values(Modes).forEach((mode: Mode) => {
|
|
91
|
-
mode.Random?.();
|
|
92
|
-
});
|
|
93
|
-
this.OneData["SuperHHH"] = Modes.SuperHHH.RandNum as number;
|
|
94
|
-
this.OneData["GreenWei"] = Modes.GreenWei.RandNum as number;
|
|
95
|
-
|
|
96
|
-
const RateRange: number[] = this.RateRanges[this.NowMode()];
|
|
97
|
-
const PCodes = Array.from(P.Map.keys());
|
|
98
|
-
RandNums.forEach((RandNum: number, i: number) => {
|
|
99
|
-
const code = PCodes.find((_, j: number) => RandNum <= RateRange[j]);
|
|
100
|
-
if (code) {
|
|
101
|
-
this.Ps[i] = P.Map.get(code) ?? null;
|
|
102
|
-
}
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
// 累積咖波累積數
|
|
106
|
-
this.Ps.forEach((p) => {
|
|
107
|
-
if (Modes.GreenWei.Score !== undefined) {
|
|
108
|
-
if (p?.code === "A" && Modes.GreenWei.Score < 20) {
|
|
109
|
-
Modes.GreenWei.Score += 1;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
CalculateScore(): void {
|
|
115
|
-
//計算分數
|
|
116
|
-
const UniqueCount: number = new Set(this.Ps.map((p) => p?.code)).size;
|
|
117
|
-
switch (UniqueCount) {
|
|
118
|
-
case 1: // 三個一樣
|
|
119
|
-
this.MarginScore += this.Ps[0]?.scores?.[0] as number;
|
|
120
|
-
break;
|
|
121
|
-
case 2: // 兩個一樣
|
|
122
|
-
if (this.Ps[0]?.code === this.Ps[1]?.code) {
|
|
123
|
-
this.MarginScore += this.Ps[0]?.scores?.[1] as number;
|
|
124
|
-
this.MarginScore += this.Ps[2]?.scores?.[2] as number;
|
|
125
|
-
this.MarginScore = Math.round(this.MarginScore / 1.4);
|
|
126
|
-
} else if (this.Ps[1]?.code === this.Ps[2]?.code) {
|
|
127
|
-
this.MarginScore += this.Ps[1]?.scores?.[1] as number;
|
|
128
|
-
this.MarginScore += this.Ps[0]?.scores?.[2] as number;
|
|
129
|
-
this.MarginScore = Math.round(this.MarginScore / 1.4);
|
|
130
|
-
} else if (this.Ps[2]?.code === this.Ps[0]?.code) {
|
|
131
|
-
this.MarginScore += this.Ps[2]?.scores?.[1] as number;
|
|
132
|
-
this.MarginScore += this.Ps[1]?.scores?.[2] as number;
|
|
133
|
-
this.MarginScore = Math.round(this.MarginScore / 1.4);
|
|
134
|
-
}
|
|
135
|
-
break;
|
|
136
|
-
case 3: // 三個不一樣
|
|
137
|
-
this.MarginScore += this.Ps[0]?.scores?.[2] as number;
|
|
138
|
-
this.MarginScore += this.Ps[1]?.scores?.[2] as number;
|
|
139
|
-
this.MarginScore += this.Ps[2]?.scores?.[2] as number;
|
|
140
|
-
this.MarginScore = Math.round(this.MarginScore / 3);
|
|
141
|
-
break;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// 根據當前模式更新加分倍數
|
|
145
|
-
this.ScoreTime = this.ScoreTimes[this.NowMode()];
|
|
146
|
-
this.MarginScore *= this.ScoreTime;
|
|
147
|
-
}
|
|
148
|
-
Result(): void {
|
|
149
|
-
// 結果
|
|
150
|
-
this.Played += 1;
|
|
151
|
-
this.DataIndex += 1;
|
|
152
|
-
this.Score += this.MarginScore;
|
|
153
|
-
this.AllData[`${this.DataIndex}`] = this.OneData;
|
|
154
|
-
}
|
|
155
|
-
JudgeMode(): void {
|
|
156
|
-
if (!this.GameRunning()) {
|
|
157
|
-
Modes.PiKaChu.Judge?.(this);
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
const mode: ModeNames = this.NowMode();
|
|
162
|
-
switch (mode) {
|
|
163
|
-
case "Normal":
|
|
164
|
-
case "PiKaChu":
|
|
165
|
-
Modes.SuperHHH.Judge?.(this);
|
|
166
|
-
if (!Modes.SuperHHH.InMode) {
|
|
167
|
-
Modes.GreenWei.Judge?.(this);
|
|
168
|
-
}
|
|
169
|
-
break;
|
|
170
|
-
case "SuperHHH":
|
|
171
|
-
Modes.SuperHHH.Judge?.(this);
|
|
172
|
-
break;
|
|
173
|
-
case "GreenWei":
|
|
174
|
-
Modes.GreenWei.Judge?.(this);
|
|
175
|
-
break;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
Logic(): void {
|
|
179
|
-
// 邏輯流程
|
|
180
|
-
this.Reset();
|
|
181
|
-
while (this.GameRunning()) {
|
|
182
|
-
this.ModeToScreen = false;
|
|
183
|
-
this.OneData = {};
|
|
184
|
-
|
|
185
|
-
this.MarginScore = 0;
|
|
186
|
-
|
|
187
|
-
this.Random();
|
|
188
|
-
this.CalculateScore();
|
|
189
|
-
this.Result();
|
|
190
|
-
this.JudgeMode();
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
}
|