Pack Structure Definition in TCG Pocket Collection Tracker: How Shinies and Baby Pokémon Are Tracked
The pack structure definition is an optional packStructure field on the Expansion type that declares whether packs contain shiny variants, baby Pokémon, or linked cards, working alongside per-card metadata to drive accurate pull-rate calculations.
The tcg-pocket-collection-tracker uses a sophisticated data model to represent Pokémon TCG Pocket expansions. According to the source code in marcelpanse/tcg-pocket-collection-tracker, each expansion can declare its pack composition through a dedicated structure that tracks special card variations. This system enables precise probability calculations and UI rendering for different booster pack types.
What Is the Pack Structure Definition?
In frontend/src/types/index.ts, the tracker defines a PackStructure interface that describes the composition rules for any given expansion:
export interface PackStructure {
/** Does the pack contain shiny versions of cards? */
containsShinies: boolean;
/** Does the pack contain baby‑Pokémon cards? */
containsBabies: boolean;
/** Are there linked‑card pairs (e.g., “partner” cards) in the pack? */
containsLinkedCards: boolean;
/** Number of cards drawn per pack – most sets use 5, some special packs use 4 */
cardsPerPack: 4 | 5;
}
This interface attaches to the Expansion type as an optional field. When present, it tells the application exactly what kinds of special cards can appear in packs from that set and how many cards each pack contains.
How Variations Are Tracked Per Expansion
The tracker employs a dual-layer approach: expansion-wide flags in the pack structure definition work together with per-card properties to identify special variations.
Shiny Pokémon Tracking
Shinies are tracked exclusively at the pack level via the containsShinies boolean. Unlike baby Pokémon, individual card objects in frontend/assets/cards.json do not carry a "shiny" flag. Instead, the probability engine in frontend/src/lib/stats.ts references expansion.packStructure.containsShinies to determine whether shiny-specific calculations should apply to that expansion's pull rates.
When containsShinies is true, the stats engine knows that any card in that expansion could potentially appear as a shiny variant when opened in a pack.
Baby Pokémon Tracking
Baby Pokémon use a two-part identification system:
- Pack-level flag: The
containsBabiesboolean in thePackStructureindicates whether the expansion can yield baby Pokémon at all - Card-level flag: Each card entry in
frontend/assets/cards.jsonincludes ababyfield (true|false)
The pullRateForCardSubset function in frontend/src/lib/stats.ts checks both values. When containsBabies is false, the engine skips baby-specific probability branches entirely. When true, it filters for cards where baby: true to calculate accurate pull rates.
Linked Cards
Some expansions feature paired cards (such as "partner" cards that reference each other). The containsLinkedCards boolean signals the presence of these pairs. This flag allows the statistics engine to treat linked cards as single draw entities rather than independent probabilities, ensuring correct pack opening simulations.
Implementation in Source Code
The master list of expansions resides in frontend/src/lib/CardsDB.ts. Here, each expansion object optionally includes its packStructure definition. For example, the A4 ("wisdomofseaandsky") expansion declares:
{
name: 'wisdomofseaandsky',
id: 'A4',
// ... other expansion metadata
packStructure: {
containsShinies: true,
containsBabies: true,
containsLinkedCards: false,
cardsPerPack: 5,
},
}
This declaration informs every downstream component—from probability calculators to UI renderers—about the possible variations in A4 booster packs.
Working with Pack Structures in Code
Accessing Pack Configuration
To check an expansion's pack properties, query the expansions array from the CardsDB module:
import { expansions } from '@/lib/CardsDB';
// Find the “A4” expansion
const a4 = expansions.find(e => e.id === 'A4');
if (a4?.packStructure) {
console.log('A4 pack size:', a4.packStructure.cardsPerPack);
console.log('Contains shinies?', a4.packStructure.containsShinies);
}
Source:
frontend/src/lib/CardsDB.tscontains the expansion definitions.
Checking Card-Level Baby Status
Since baby status is stored per-card, filter the card database directly:
import cards from '@/assets/cards.json';
const pichuCard = cards.find(c => c.card_id === 'Pichu');
if (pichuCard?.baby) {
console.log('This is a baby Pokémon!');
}
Source:
frontend/assets/cards.jsonincludes the"baby": true/falsefield on card entries.
Calculating Pull Rates
The pullRateForCardSubset function consumes the pack structure to compute accurate probabilities:
import { pullRateForCardSubset } from '@/lib/stats';
import { expansions } from '@/lib/CardsDB';
const expansion = expansions.find(e => e.id === 'A2b')!; // Shining Revelry
const targetCard = /* obtain Card object */;
const probability = pullRateForCardSubset(
[targetCard],
cardsInPack, // current pack contents
expansion.packStructure!, // pack definition with containsShinies: true
false // deckbuilding mode disabled
);
console.log(`Pull rate: ${probability * 100}%`);
Source:
frontend/src/lib/stats.tsimplements the probability logic that respects pack structure flags.
Summary
- The pack structure definition lives in the
PackStructureinterface atfrontend/src/types/index.ts, defining four key properties:containsShinies,containsBabies,containsLinkedCards, andcardsPerPack. - Shiny tracking operates at the expansion level only; cards do not store individual shiny flags, but the pack structure tells the stats engine to include shiny probabilities.
- Baby Pokémon tracking combines the expansion's
containsBabiesflag with individual cardbabyproperties infrontend/assets/cards.json. - The probability engine in
frontend/src/lib/stats.tsuses these structures to skip irrelevant calculation branches and produce accurate pull-rate predictions.
Frequently Asked Questions
How does the tracker know if a specific card has a shiny version?
The application does not store shiny status on individual card objects. Instead, it checks the expansion's packStructure.containsShinies flag. If true, the probability engine assumes any card in that expansion could appear as a shiny variant when calculating pull rates in frontend/src/lib/stats.ts.
Why do baby Pokémon need both a pack-level flag and a card-level flag?
The containsBabies pack-level flag acts as a performance optimization and logic gate. When false, the pullRateForCardSubset function immediately skips baby-specific calculation branches. The per-card baby boolean in frontend/assets/cards.json allows the engine to identify exactly which cards qualify as babies when the pack structure permits them.
Can a pack have fewer than 5 cards?
Yes. The cardsPerPack property accepts only 4 or 5 as valid values. Some special promotional or mini-expansions use 4-card packs instead of the standard 5-card configuration, and the tracker adjusts its probability math accordingly.
Where is the pack structure data actually defined?
Each expansion's pack structure is declared in frontend/src/lib/CardsDB.ts within the expansion object literal. For example, the A4 expansion includes a complete packStructure object specifying shinies, babies, pack size, and linked card presence.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →