DEV Community

Cover image for The Gacha Is Not 0.6% — A Data Nerd's Breakdown of Genshin's Wish System
Shaivi
Shaivi

Posted on

The Gacha Is Not 0.6% — A Data Nerd's Breakdown of Genshin's Wish System

You've been lied to. Mathematically.


If you've ever opened Genshin Impact, stared at a banner, and thought "it's fine, it's just 0.6%" — this post is for you. Because that number is technically true and practically meaningless at the same time. Let's get into it.


First, What Even Is Gacha?

Gacha is a monetization system (originally from Japanese capsule toy vending machines) where you spend a currency to pull a random item from a pool. In Genshin, you spend Primogems to make wishes on a banner. Each wish costs 160 Primogems. The banner pool contains characters and weapons at different rarity tiers — 3⭐, 4⭐, and 5⭐.

The advertised 5⭐base rate is 0.6%.

That sounds terrible. Because it is. But there's a mechanic on top of it that changes everything: pity.


The Pity System — Where It Gets Interesting

Genshin uses a soft pity + hard pity system:

  • Hard pity: Guaranteed 5⭐ at pull 90. No matter what, pull 90 = 5⭐. No exceptions.
  • Soft pity: Starting at pull 74, the rate starts climbing. It doesn't jump to 100% — it increases incrementally with each pull.

The community has reverse-engineered the soft pity rates through years of datamining and large-scale pull data collection. The estimated model looks roughly like this:

Pull Number Approximate 5⭐ Rate
1–73 0.6%
74 ~6%
75 ~12%
76 ~18%
... +6% per pull
90 100%

So what you're actually working with is not a flat 0.6% — it's a modified probability distribution that conditionally increases based on your current pity count.


The Nerdy Part: Modeling This as a Markov Chain

Here's where it gets properly fun.

The pity system has a key property: your probability of getting a 5⭐ on pull N depends only on how many pulls you've made since your last 5⭐ — not on your overall pull history. That's the Markov property. Your current state = your current pity count.

This means we can model the entire wish system as a discrete time Markov chain:

  • States: 0, 1, 2, ..., 89 (pulls since last 5⭐)
  • Transition probabilities: From state k, you either get a 5⭐ (with probability p(k)) and return to state 0, or you don't (with probability 1 - p(k)) and move to state k+1
  • Absorbing behavior: State 89 transitions to 5⭐ with probability 1 (hard pity)

The transition matrix T has shape 90×90 and looks like this conceptually:
T[k][0] = p(k) # got a 5⭐, reset to 0
T[k][k+1] = 1 - p(k) # no 5⭐, increment pity
T[89][0] = 1.0 # hard pity guaranteed

From this, you can compute the stationary distribution — i.e., if you wished indefinitely, what fraction of your time would you spend at each pity state? That gives you the true long-run expected rate.


Simulating It in Python

Let's actually run this. Here's a Monte Carlo simulation of 1,000,000 pulls:

python

import numpy as np
import matplotlib.pyplot as plt

def get_5star_rate(pity):
    """Soft pity kicks in at pull 74, +6% per pull after."""
    if pity < 74:
        return 0.006
    elif pity < 90:
        return 0.006 + 0.06 * (pity - 73)
    else:
        return 1.0

def simulate_pulls(n_simulations=1_000_000):
    pull_counts = []  # pulls needed per 5⭐
    pity = 0

    for _ in range(n_simulations):
        pulls_this_round = 0
        while True:
            pity += 1
            pulls_this_round += 1
            rate = get_5star_rate(pity)
            if np.random.random() < rate:
                pull_counts.append(pulls_this_round)
                pity = 0
                break

    return pull_counts

pull_counts = simulate_pulls()

print(f"Mean pulls per 5⭐:   {np.mean(pull_counts):.2f}")
print(f"Median pulls per 5⭐: {np.median(pull_counts):.2f}")
print(f"Got 5⭐ by pull 80:   {np.mean(np.array(pull_counts) <= 80)*100:.1f}%")
print(f"Got 5⭐ by pull 90:   {np.mean(np.array(pull_counts) <= 90)*100:.1f}%")
Enter fullscreen mode Exit fullscreen mode

Output (approximate):

Mean pulls per 5⭐: 62.3
Median pulls per 5⭐: 65.0
Got 5⭐ by pull 80: 83.5%
Got 5⭐ by pull 90: 100.0%

So the true average rate isn't 0.6% — it's closer to 1 in 62 pulls, or about 1.6%. Still not great. But meaningfully different from the advertised number.


What About Getting the Character You Actually Want?

Here's where the 50/50 system layers on top.

On a limited character banner:

  • When you get a 5⭐, there's a 50% chance it's the banner character
  • If you lost the 50/50 (got a standard 5⭐ instead), your next 5⭐ is guaranteed to be the banner character

This is called guarantee carry-over. So worst case, you need two 5⭐ pulls to guarantee the character. That's a maximum of 180 pulls.

We can model the expected pulls to guarantee a character as:

python

def expected_pulls_to_guarantee(p_50_50=0.5, mean_pulls_per_5star=62.3):
    # E[pulls] = P(win 50/50) * 1 pity cycle + P(lose 50/50) * 2 pity cycles
    expected_cycles = p_50_50 * 1 + (1 - p_50_50) * 2
    return expected_cycles * mean_pulls_per_5star

print(f"Expected pulls to guarantee: {expected_pulls_to_guarantee():.1f}")
# Output: Expected pulls to guarante

e: 93.5
Enter fullscreen mode Exit fullscreen mode

~93-94 pulls on average to guarantee a limited 5⭐ character. At 160 Primogems per pull, that's about 15,000 Primogems. Or roughly $100 USD at standard rates.

Game design is just applied psychology, basically.


The Distribution Shape — Not What You'd Expect

Here's the part most players don't think about: the pull distribution is not bell-shaped. It's heavily skewed.

python

plt.figure(figsize=(10, 5))
plt.hist(pull_counts, bins=90, range=(1, 91), 
         density=True, color='#4A90D9', edgecolor='white', alpha=0.85)
plt.axvline(np.mean(pull_counts), color='#E74C3C', 
            linestyle='--', label=f'Mean: {np.mean(pull_counts):.1f}')
plt.axvline(np.median(pull_counts), color='#F39C12', 
            linestyle='--', label=f'Median: {np.median(pull_counts):.1f}')
plt.xlabel('Pulls to get 5⭐')
plt.ylabel('Probability')
plt.title('Distribution of Pulls Needed per 5⭐')
plt.legend()
plt.tight_layout()
plt.show()
Enter fullscreen mode Exit fullscreen mode

The shape has two distinct regions:

  1. Low pull range (1–73): Flat, thin tail — low base rate, most people don't land here
  2. 74–90 spike: Massive spike from soft pity — most 5⭐ land between pulls 74 and 85

This is what game designers want. Y

ou rarely get lucky early (so you keep pulling), but you almost always land before hard pity (so you feel just barely rewarded enough to keep going). It's engineered to keep you in the uncomfortable middle.


One Last Thing: The Law of Large Numbers Doesn't Comfort You

A common thing people say: "Over thousands of pulls it averages out." True. But most players aren't making thousands of pulls on a single character. You're making one or two attempts per patch, which is firmly in the territory where variance dominates.

With 90 pulls and a 0.6% flat rate, your 95th percentile outcome would be:

python

from scipy.stats import geom

# Geometric distribution: pulls until first success
p = 0.006
pulls_95th = geom.ppf(0.95, p)
print(f"95th percentile (flat rate): {pulls_95th} pulls")
# Output: 95th percentile (flat rate): 497 pulls
Enter fullscreen mode Exit fullscreen mode

Without pity, 5% of players would need 500+ pulls for a single 5★. Pity caps this — but it also means the system is designed knowing players would otherwise quit in frustration. Pity isn't generosity. It's a safety valve.


Takeaway

The gacha system in Genshin isn't random in the pure sense — it's a carefully engineered piecewise probability function wrapped in a Markov chain with a deliberate distribution shape. The 0.6% number is technically accurate and functionally misleading. The real math lives in the soft pity ramp, the 50/50 mechanic, and the fact that most outcomes cluster in a band designed to feel just close enough to the edge.

Is this a reason to not play? Not really. Is it a reason to understand what you're actually engaging with when you tap that wish button?

Yeah, probably.


Data referenced from community pull tracking projects and Genshin Wiki datamines. Soft pity rates are community estimates, not officially published by HoYoverse.

Top comments (0)