Skip to content

Commit dc29361

Browse files
committed
Fix gacha calculations
1 parent b30bf12 commit dc29361

1 file changed

Lines changed: 46 additions & 20 deletions

File tree

src/commands/misc/gachacalc.ts

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const gachas: Record<string, Banner> = {
1818
bannerName: "5* Banner character",
1919
banner: 0.5,
2020
guaranteed: 1,
21+
minConst: -1,
2122
maxConst: 6,
2223
constFormat: "C",
2324
constName: "Constellations",
@@ -28,6 +29,7 @@ const gachas: Record<string, Banner> = {
2829
bannerName: "Specific 4* banner character",
2930
banner: 0.5,
3031
guaranteed: 1/3,
32+
minConst: -1,
3133
maxConst: 6,
3234
constFormat: "C",
3335
constName: "Constellations",
@@ -39,6 +41,7 @@ const gachas: Record<string, Banner> = {
3941
banner: 0.75,
4042
guaranteed: 1/2,
4143
guaranteedPity: 3,
44+
minConst: 0,
4245
maxConst: 5,
4346
constFormat: "R",
4447
constName: "Refinements",
@@ -52,6 +55,7 @@ type Banner = {
5255
banner: number
5356
guaranteed: number
5457
guaranteedPity?: number
58+
minConst: number
5559
maxConst: number
5660
maxPity: number
5761
constFormat: string
@@ -90,7 +94,7 @@ Example with 70 pulls, 10 pity and guaranteed for 5 star weapon banner: \`${conf
9094
options: [{
9195
name: "pulls",
9296
description: "Amount of pulls to simulate",
93-
type: "NUMBER",
97+
type: "INTEGER",
9498
required: true
9599
}, {
96100
name: "gacha",
@@ -110,13 +114,23 @@ Example with 70 pulls, 10 pity and guaranteed for 5 star weapon banner: \`${conf
110114
}, {
111115
name: "pity",
112116
description: "Amount of pity to start at",
113-
type: "NUMBER",
117+
type: "INTEGER",
114118
required: false
115119
}, {
116120
name: "guaranteed",
117121
description: "Start with guaranteed rate up",
118122
type: "BOOLEAN",
119123
required: false
124+
}, {
125+
name: "current",
126+
description: "Current amount of copies to start with (default: none)",
127+
type: "INTEGER",
128+
required: false
129+
}, {
130+
name: "guaranteed_pity",
131+
description: "Amount of Epitomized Path to start with (for Weapon banner)",
132+
type: "INTEGER",
133+
required: false
120134
}]
121135
})
122136
}
@@ -125,11 +139,13 @@ Example with 70 pulls, 10 pity and guaranteed for 5 star weapon banner: \`${conf
125139
const { options } = source
126140

127141
const gacha = options.getString("gacha") ?? "char"
128-
const pulls = options.getNumber("pulls", true)
129-
const pity = options.getNumber("pity") ?? 0
142+
const pulls = options.getInteger("pulls", true)
143+
const pity = options.getInteger("pity") ?? 0
144+
const current = options.getInteger("current") ?? 0
145+
const guaranteedPity = options.getInteger("guaranteedPity") ?? 0
130146
const guaranteed = options.getBoolean("guaranteed") ?? false
131147

132-
return this.run(source, gacha, pulls, pity, guaranteed)
148+
return this.run(source, gacha, current, pulls, pity, guaranteed, guaranteedPity)
133149
}
134150

135151
async runMessage(source: Message, args: string[]): Promise<SendMessage | undefined> {
@@ -141,6 +157,8 @@ Example with 70 pulls, 10 pity and guaranteed for 5 star weapon banner: \`${conf
141157

142158
const pulls = parseInt(args[0] ?? "75")
143159
const pity = parseInt(args[1] ?? "0")
160+
const current = parseInt(args[2] ?? "-1")
161+
const guaranteedPity = parseInt(args[1] ?? "0")
144162

145163
let guaranteed = false
146164
if (args[2]?.match(/y(es)?|t(rue)?|g(uaranteed)?/))
@@ -149,19 +167,25 @@ Example with 70 pulls, 10 pity and guaranteed for 5 star weapon banner: \`${conf
149167
guaranteed = false
150168
else if (args[2])
151169
return sendMessage(source, "Invalid 50/50, should be y(es)/g(uaranteed) or n(o)/50/50")
152-
return this.run(source, gacha, pulls, pity, guaranteed)
170+
return this.run(source, gacha, current, pulls, pity, guaranteed, guaranteedPity)
153171
}
154172

155-
async run(source: CommandSource, gacha: string, pulls: number, pity: number, guaranteed: boolean): Promise<SendMessage | undefined> {
173+
async run(source: CommandSource, gacha: string, current: number, pulls: number, pity: number, guaranteed: boolean, guaranteedPity: number): Promise<SendMessage | undefined> {
156174
const banner = gachas[gacha]
157175

158176
if (isNaN(pulls) || pulls <= 0 || pulls > 9999)
159177
return sendMessage(source, "Invalid pulls amount, should be a number greater than 1")
160178
if (isNaN(pity) || pity < 0 || pity > banner.maxPity)
161179
return sendMessage(source, `Invalid pity amount, should be a number between 0 and ${banner.maxPity}`)
180+
if (isNaN(current) || current > banner.maxConst)
181+
return sendMessage(source, `Invalid currently owned, should be a number between -1 (not owned) and ${banner.maxConst}`)
182+
if (isNaN(guaranteedPity) || guaranteedPity < 0 || guaranteedPity > (banner.guaranteedPity ?? 0))
183+
return sendMessage(source, `Invalid guaranteed pity/Epitomized Path, should be a number between 0 and ${banner.guaranteedPity ?? 0}`)
184+
185+
if (current < banner.minConst) current = banner.minConst
162186

163187
const start = Date.now()
164-
const sims = this.calcSims(pity, pulls, guaranteed, gacha)
188+
const sims = this.calcSims(current, pity, pulls, guaranteed, guaranteedPity, banner)
165189
const time = Date.now() - start
166190
Logger.info(`Calculation done in ${time}ms`)
167191

@@ -171,16 +195,14 @@ ${createTable(
171195
[banner.constName, "Rate"],
172196
sims
173197
.sort((a, b) => a.const - b.const)
174-
.map(k => [k.const < 0 ? "/" : `${banner.constFormat}${k.const}`, `${(k.rate * 100).toFixed(2)}%`])
198+
.map(k => [k.const == banner.minConst ? "/" : `${banner.constFormat}${k.const}`, `${(k.rate * 100).toFixed(2)}%`])
175199
)}
176200
\`\`\``)
177201
}
178202

179203
calcSims = memoize(this.calcSimsRegular, { max: 50 })
180204

181-
private calcSimsRegular(pity: number, pulls: number, guaranteed: boolean, bannerName: string): ReducedSim[] {
182-
const banner = gachas[bannerName]
183-
205+
private calcSimsRegular(current: number, pity: number, pulls: number, guaranteed: boolean, guaranteedPity: number, banner: Banner): ReducedSim[] {
184206
// Max pity / const
185207
if (banner.guaranteed >= 1 && pulls + pity >= banner.maxPity * (banner.maxConst * 2 - (guaranteed ? 1 : 0)))
186208
return [{
@@ -197,12 +219,13 @@ ${createTable(
197219
return this.calcSimsInt({
198220
pity,
199221
guaranteed,
200-
guaranteedPity: 0,
201-
const: -1,
222+
guaranteedPity,
223+
const: current,
202224
rate: 1
203225
}, pulls, banner)
204226
}
205227

228+
206229
private calcSimsInt(starterSim: Sim, pulls: number, banner: Banner): ReducedSim[] {
207230
const sims: Sim[] = this.calcSimsExact([starterSim], pulls, banner)
208231

@@ -232,7 +255,7 @@ ${createTable(
232255
const addOrMerge = (sim: Sim) => {
233256
if (sim.rate <= 0) return
234257

235-
const v = ((+sim.guaranteed) * (banner.maxConst + 2) + (sim.const + 1)) * (banner.maxPity + 5) + sim.pity
258+
const v = (((sim.const + 1) * (banner.maxPity + 5) + sim.pity) * 2 + (+sim.guaranteed)) * (banner.guaranteedPity ?? 1) + sim.guaranteedPity
236259
const other = newSims[v]
237260

238261
if (other) {
@@ -254,7 +277,10 @@ ${createTable(
254277
let rate = banner.rate(currentPity) / 100
255278
if (rate > 1) rate = 1
256279
else if (rate < 0) rate = 0
257-
const bannerRate = sim.guaranteed ? 1 : banner.banner
280+
const bannerRate = (
281+
sim.guaranteed ||
282+
(banner.guaranteedPity && sim.guaranteedPity >= banner.guaranteedPity - 1)
283+
) ? 1 : banner.banner
258284

259285
// Failed
260286
if (rate < 1)
@@ -277,22 +303,22 @@ ${createTable(
277303

278304
// Got banner item but not wanted (eg. wrong rate up 4* char/5* char)
279305
if (banner.guaranteed < 1)
280-
if (banner.guaranteedPity && sim.guaranteedPity >= banner.guaranteedPity)
281-
// https://www.hoyolab.com/article/533196
306+
if (banner.guaranteedPity && sim.guaranteedPity >= banner.guaranteedPity - 1)
307+
// https://www.hoyolab.com/article/533196
282308
addOrMerge({
283309
pity: 0,
284310
guaranteed: false,
285311
guaranteedPity: 0,
286312
const: sim.const + 1,
287-
rate: sim.rate * rate * bannerRate * (1-banner.guaranteed)
313+
rate: sim.rate * rate * bannerRate * (1 - banner.guaranteed)
288314
})
289315
else
290316
addOrMerge({
291317
pity: 0,
292318
guaranteed: false,
293319
guaranteedPity: sim.guaranteedPity + 1,
294320
const: sim.const,
295-
rate: sim.rate * rate * bannerRate * (1-banner.guaranteed)
321+
rate: sim.rate * rate * bannerRate * (1 - banner.guaranteed)
296322
})
297323

298324
// Failed banner items (eg. 4* char rate ups vs regular 4*)

0 commit comments

Comments
 (0)