@@ -25,7 +25,29 @@ function pityRate(baseRate: number, pityStart: number, increaseRate?: number): (
2525
2626const 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
120142type 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}
138161type 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 'Capturing Radiance' 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 'Capturing Radiance' 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'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