Skip to content

Commit 8a4672c

Browse files
committed
Update gacha calc for lost pity hypotheses
1 parent ac61926 commit 8a4672c

File tree

1 file changed

+39
-7
lines changed

1 file changed

+39
-7
lines changed

pages/tools/gachacalc.tsx

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,29 @@ function pityRate(baseRate: number, pityStart: number, increaseRate?: number): (
2525

2626
const gachas: Record<string, Banner> = {
2727
char: {
28-
bannerName: "5* banner character [Genshin Impact (5.0+)]",
28+
bannerName: "5* banner character [Genshin Impact (5.0+, hypothese A)]",
29+
banner: [0.5, 0.5, 0.75, 1],
30+
guaranteed: 1,
31+
minConst: -1,
32+
maxConst: 6,
33+
constFormat: "C",
34+
constName: "Constellation",
35+
maxPity: 90,
36+
rate: pityRate(0.6, Math.ceil(44 / 0.6))
37+
},
38+
charHypoB: {
39+
bannerName: "5* banner character [Genshin Impact (5.0+, hypothese B)]",
40+
banner: [0.5, 0.57, 0.64, 0.71, 0.78, 0.85, 0.92, 0.99, 1],
41+
guaranteed: 1,
42+
minConst: -1,
43+
maxConst: 6,
44+
constFormat: "C",
45+
constName: "Constellation",
46+
maxPity: 90,
47+
rate: pityRate(0.6, Math.ceil(44 / 0.6))
48+
},
49+
charFlatRate: {
50+
bannerName: "5* banner character [Genshin Impact (5.0+, assumed flat 55/45)]",
2951
banner: 0.55,
3052
guaranteed: 1,
3153
minConst: -1,
@@ -119,7 +141,7 @@ const gachas: Record<string, Banner> = {
119141

120142
type Banner = {
121143
bannerName: string
122-
banner: number
144+
banner: number | number[]
123145
guaranteed: number
124146
guaranteedPity?: number
125147
minConst: number
@@ -134,6 +156,7 @@ type Sim = ReducedSim & {
134156
pity: number
135157
guaranteed: boolean
136158
guaranteedPity: number
159+
lostPity: number
137160
}
138161
type ReducedSim = {
139162
const: number
@@ -147,14 +170,15 @@ export default function GachaCalc({ location }: { location: string }) {
147170
const [pulls, setPulls] = useState(90)
148171
const [guaranteed, setGuaranteed] = useState(false)
149172
const [guaranteedPity, setGuaranteedPity] = useState(0)
173+
const [lostPity, setLostPity] = useState(0)
150174

151175
const [gachaName, setGacha] = useState(Object.values(gachas).map(g => g.bannerName)[0])
152176

153177
const banner = Object.values(gachas).find(x => x.bannerName == gachaName) ?? Object.values(gachas)[0]
154178

155179
const calculated = useMemo(
156-
() => calcSimsRegular(current, pity, pulls, guaranteed, guaranteedPity, banner),
157-
[current, pity, pulls, guaranteed, guaranteedPity, banner]
180+
() => calcSimsRegular(current, pity, pulls, guaranteed, guaranteedPity, lostPity, banner),
181+
[current, pity, pulls, guaranteed, guaranteedPity, lostPity, banner]
158182
)
159183

160184
function delayed(f: EffectCallback) {
@@ -167,6 +191,7 @@ export default function GachaCalc({ location }: { location: string }) {
167191

168192
useEffect(() => delayed(() => { if (pity >= banner.maxPity) setPity(banner.maxPity - 1) }), [banner, pity])
169193
useEffect(() => delayed(() => { if (banner.guaranteedPity && guaranteedPity >= banner.guaranteedPity) setGuaranteedPity(banner.guaranteedPity) }), [banner, guaranteedPity])
194+
useEffect(() => delayed(() => { if (Array.isArray(banner.banner) && lostPity >= banner.banner.length) setLostPity(banner.banner.length - 1) }), [banner, lostPity])
170195
useEffect(() => delayed(() => { if (current > banner.maxConst) setCurrent(banner.maxConst) }), [banner, current])
171196
useEffect(() => delayed(() => { if (current < banner.minConst) setCurrent(banner.minConst) }), [current, banner])
172197

@@ -204,6 +229,7 @@ export default function GachaCalc({ location }: { location: string }) {
204229
<NumberInput label="Current pity" set={setPity} value={pity} min={0} max={banner.maxPity - 1} />
205230
<CheckboxInput label="Next is guaranteed" set={setGuaranteed} value={guaranteed} />
206231
{banner.guaranteedPity && <NumberInput label="Epitomized Path" set={setGuaranteedPity} value={guaranteedPity} min={0} max={banner.guaranteedPity - 1} />}
232+
{Array.isArray(banner.banner) && <NumberInput label="Lost pity (Capturing Radiance)" set={setLostPity} value={lostPity} min={0} max={banner.banner.length - 1} />}
207233

208234
<h3 className="text-lg font-bold pt-1" id="results">Results</h3>
209235
<div className="columns-1 md:columns-2 mr-2 mb-2">
@@ -335,7 +361,7 @@ export default function GachaCalc({ location }: { location: string }) {
335361
<p>The calculator uses the statistical model for drop-rates of Cgg/<FormattedLink href="https://genshin-wishes.com/" target="_blank">genshin-wishes.com</FormattedLink>.
336362
For more information about drop rates, please refer to <FormattedLink href="https://www.hoyolab.com/article/497840" target="_blank"> their HoYoLAB post</FormattedLink>.</p>
337363
<p>Rates indicate the chance to get exactly {banner.constFormat}x within Y pulls, cumulative rate chance to get {banner.constFormat}x or higher within Y pulls. Big graph indicates cumulative rate at each pull (read: Z% to get {banner.constFormat}x <i>within</i> Y pulls).</p>
338-
<p>Exact details of &apos;Capturing Radiance&apos; are not yet known. The calculator assumes the consolidated rate of 55% mentioned in the <a href="https://www.hoyolab.com/article/32168979">HoYoLAB article</a>.</p>
364+
<p>Exact details of &apos;Capturing Radiance&apos; are not yet known. Hypothese A assumes a lost pity model where rate goes from 50%/50%, to 50%/50% to 75%/25% to 100%/0% and resets when you win the 50/50. Hypothese B assumes a 7% increase for each lost pity. The other version assumes a flat consolidated rate of 55% mentioned in the <a href="https://www.hoyolab.com/article/32168979">HoYoLAB article</a>.</p>
339365
<p><i><b>NOTE</b>: To reduce the amount of calculations, the 4-star character banner calculator will assume there are no 5-stars being dropped.
340366
These can prevent a 4 star from dropping, but they still increase the pity counter. It is possible (in-game) to not get a 4-star within 10 pity,
341367
but the next pull is guaranteed to be a 4-star if it&apos;s not a 5-star.</i></p>
@@ -407,12 +433,13 @@ function SelectInput({ value, set, label, options }: { value: string, set: (newV
407433
</label></div>
408434
}
409435

410-
function calcSimsRegular(current: number, pity: number, pulls: number, guaranteed: boolean, guaranteedPity: number, banner: Banner): ReducedSim[][] {
436+
function calcSimsRegular(current: number, pity: number, pulls: number, guaranteed: boolean, guaranteedPity: number, lostPity: number, banner: Banner): ReducedSim[][] {
411437
return calcSimsInt({
412438
pity,
413439
guaranteed,
414440
guaranteedPity,
415441
const: current,
442+
lostPity,
416443
rate: 1
417444
}, pulls, banner)
418445
}
@@ -480,14 +507,15 @@ function calcSimsExact<T>(sims: Sim[], pulls: number, banner: Banner, prune = 1e
480507
const bannerRate = (
481508
sim.guaranteed ||
482509
(banner.guaranteedPity && sim.guaranteedPity >= banner.guaranteedPity - 1)
483-
) ? 1 : banner.banner
510+
) ? 1 : (Array.isArray(banner.banner) ? banner.banner[sim.lostPity] : banner.banner)
484511

485512
// Failed
486513
if (rate < 1)
487514
addOrMerge({
488515
pity: currentPity,
489516
guaranteed: sim.guaranteed,
490517
guaranteedPity: sim.guaranteedPity,
518+
lostPity: sim.lostPity,
491519
const: sim.const,
492520
rate: sim.rate * (1 - rate)
493521
})
@@ -497,6 +525,7 @@ function calcSimsExact<T>(sims: Sim[], pulls: number, banner: Banner, prune = 1e
497525
pity: 0,
498526
guaranteed: false,
499527
guaranteedPity: 0,
528+
lostPity: sim.guaranteed ? sim.lostPity : 0, // Keep lost pity if it was guaranteed, otherwise we won 50/50
500529
const: sim.const + 1,
501530
rate: sim.rate * rate * bannerRate * banner.guaranteed
502531
})
@@ -509,6 +538,7 @@ function calcSimsExact<T>(sims: Sim[], pulls: number, banner: Banner, prune = 1e
509538
pity: 0,
510539
guaranteed: false,
511540
guaranteedPity: 0,
541+
lostPity: 0, // No idea what to do here as it isn't relevant (combination doesn't exist ingame)
512542
const: sim.const + 1,
513543
rate: sim.rate * rate * bannerRate * (1 - banner.guaranteed)
514544
})
@@ -517,6 +547,7 @@ function calcSimsExact<T>(sims: Sim[], pulls: number, banner: Banner, prune = 1e
517547
pity: 0,
518548
guaranteed: false,
519549
guaranteedPity: banner.guaranteedPity ? sim.guaranteedPity + 1 : 0,
550+
lostPity: 0, // No idea what to do here as it isn't relevant (combination doesn't exist ingame)
520551
const: sim.const,
521552
rate: sim.rate * rate * bannerRate * (1 - banner.guaranteed)
522553
})
@@ -527,6 +558,7 @@ function calcSimsExact<T>(sims: Sim[], pulls: number, banner: Banner, prune = 1e
527558
pity: 0,
528559
guaranteed: true,
529560
guaranteedPity: banner.guaranteedPity ? sim.guaranteedPity + 1 : 0,
561+
lostPity: Array.isArray(banner.banner) ? sim.lostPity + 1 : 0, // increase lost pity if it was a lost 50/50
530562
const: sim.const,
531563
rate: sim.rate * rate * (1 - bannerRate)
532564
})

0 commit comments

Comments
 (0)