Are the probability for a new card correct?

I have noticed over the past few months, that it seems that they probabilities given in the collection page for a new card in your collection for that set and pack cannot be correct. It seems to be biased heavily towards showing a higher probability for a new card than what is correct based on observed pulls.

I suspect that there is some incorrect math or a bug in the calculation. Can someone with access to the backend show the code or math used? Or someone be arsed to calculate your probability based on your current collection and compare to what the website gives (a lot of work I expect). It’s been some years since my Statistics classes at university, so I’m not the best to do it, but I’m curious if my intuitive understanding of the stats here are wrong or not.

The code can be found on GitHub, specifically in this file. I once took a glance and it looked a little bit messy, so I wouldn’t be surprised if you found some bugs :sweat_smile: Even if you don’t find any bugs I think there is room for simplifying the calculations so you can contribute this way :slightly_smiling_face:

Thanks! I looked through the code. I decided to check out ChatGPT 5 coding and math skills out for the first time, and it believes it found the error. It seems plausible based on its reasoning.

My prompt:

"I have a website for keeping a database of one’s pokemon cards from the game PokĂ©mon Trading Card Game Pocket. One of the functions of the website is to access the probability of getting a new card from a specific pack based on your current cards from that set the pack is part of.

A user noticed that the probabilities seems to be too high when the collection of cards for that set becomes significant. It seems the calculation is biased towards too high a probability. Can you check the code and math from this github link: tcg-pocket-collection-tracker/frontend/src/lib/CardsDB.ts at main · marcelpanse/tcg-pocket-collection-tracker · GitHub and analyze the code relation to probabilities for getting a new card one doesn’t already own a card of."

It gave the following comprehensive answer after thinking and analysing for almost 2 minutes (link for proper display of math etc: https://chatgpt.com/c/68cecb7b-994c-8329-8dd2-0c4c6b484973)

Short answer: yes — there’s a real “too-high” bias in the way the probability is computed right now, and it shows up more and more as a user’s collection grows.

Here’s what I could verify and infer from the repo and the thread where the maintainer points to the relevant function:

  • The probability code lives in pullRate(...) in frontend/src/lib/CardsDB.ts (commit 0e2f93e
). The function first builds the set of cards in the pack and filters out “owned” ones to get missingCards. Then it computes the “chance to get a new card.” (TCG Pocket Community)
  • The code clearly aggregates the candidate pool at the pack level (cardsInPack = expansion.cards.filter(c => c.pack === pack.name || c.pack === 'everypack')) and then narrows to missingCards (with optional rarityFilter). What’s not done (and what the pack-opening UI of the game actually uses) is slot-by-slot weighting by the pack’s rarity distribution for each of the 5 slots. The forum note by the author also hints that the real math differs “because there are different pull rates for each slot,” which is exactly the missing piece. (TCG Pocket Community)

Where the bias comes from

  1. Using a single blended “missing/total” for the whole pack pool
    If you take p = missingCards.length / cardsInPack.length and then do something like 1 - (1 - p)^5, you’ll overstate the true chance when most of the cards you’re missing are rarer than the ones you already own. As your collection grows, commons/uncommons are mostly completed, and the remaining missing cards are concentrated in lower-probability rarities — but the blended p still treats every card as if it had equal weight in every slot. That pushes the displayed probability upward relative to the truth.
  2. Not accounting for per-slot rarity menus
    In PokĂ©mon TCG Pocket, each of the 5 slots draws from different rarity menus with different odds (e.g., slots 1–3 are “low rarities,” 4 has a mid/rare menu, 5 has the “rare/crown” menu, etc.). The correct per-slot probability of drawing any new card is:

ps(new)=∑r∈RsPr⁥(rarity=r in slot s)×#missing in (r∩pack)#total in (r∩pack)p_s(\text{new})=\sum_{r\in R_s} \Pr(\text{rarity}=r\text{ in slot } s)\times \frac{#\text{missing in }(r\cap \text{pack})}{#\text{total in }(r\cap \text{pack})}

Then the pack probability is:

Pr⁡(at least one new)=1−∏s=15(1−ps(new))\Pr(\text{at least one new}) = 1 - \prod_{s=1}^{5}\bigl(1 - p_s(\text{new})\bigr)

If you instead swap in a single missing/total for all slots, you inflate the result precisely when your missing set is skewed toward rarer slots. (This matches the user reports that the number “looks too high once my collection is large.”) The maintainer’s comment in the forum acknowledges slot pull-rates matter; they aren’t actually folded into the displayed number right now. (TCG Pocket Community)

What to change (concrete fix)

Inside pullRate(...), after you’ve built cardsInPack and missingCards:

  1. Build rarity-indexed pools for the current pack, e.g.:
const poolByRarity = groupBy(cardsInPack, c => c.rarity);
const missingByRarity = groupBy(missingCards, c => c.rarity);
  1. For the pack you’re evaluating, use its slot definitions (however you model them — typically pack.slots with something like [{ rarity: '1♩', p: 0.XX }, 
] or a map of rarity→weight per slot). For each slot s:
const pNewSlot =
  sum_over_rarities_in_slot( slotWeight[s][r] *
    ( (missingByRarity[r]?.length ?? 0) / (poolByRarity[r]?.length ?? 0) )
  );
  1. Combine slots with the complement rule:
const pAtLeastOneNew = 1 - pNewSlotArray.reduce((acc, p) => acc * (1 - p), 1);
  1. Keep your rarityFilter and numberFilter logic exactly as you already do, but apply them before building the rarity pools so they naturally propagate into the fractions.

This change alone removes the “too-high” bias.

Quick self-check you can run

  • Take a pack where slots 1–3 only pull from 1–3♩, slot 4 from 4♩/★, slot 5 from ★/Crown. Make a test account that owns nearly all low-rarity cards but is still missing many ★/Crown.
  • The blended method will show a high p (most of the pack’s pool is “owned” but a non-trivial slice is “missing,” so missing/total looks healthy and gets multiplied across 5 slots).
  • The slot-aware method will show a materially lower p because slots 1–3 have almost zero chance of new, so nearly the whole chance must come from slots 4–5 only.

You’ll see the gap widen the more “top-heavy” the missing collection is.


If you want, paste your pack/slot schema (how you store per-slot rarity odds) and I’ll drop a ready-to-merge patch for pullRate(...) that uses the exact structure you have. In any case, the math above is what you want to implement. (TCG Pocket Community)


Not sure if it’s correct or not, but putting it out there for someone to check. I might look again at it later.

No sorry that doesn’t make sense, we a calculation per slot just like in the game. I don’t have time to dive in right now, but you can verify the percentages. All pull rates are listed in the game itself in the offering rates, which you can use to verify the calculations somewhat. If you click on a single card on our site you can also see the calculated pull rate for that single card, which should be the same as the in game offering.

I think the math checks out, but of course of anyone can show a problem that doesn’t check out probability wise let me know.

1 Like

I have verified the percentages given for getting a new card for a specific rarity and also including multiple rarities, and it matches perfectly with what the site gives as probability, so false alarm for now :stuck_out_tongue:

Thanks for verifying:-)

Meh, I’ve been at “20-10%” probability for a new card since like 3 weeks I think? And I only ever get new cards with trades. Yes I spent a lot of hourglasses at the beginning of the season, but after reaching 20% I can swear I didn’t get a single new one through opening packs.