# Pack Structure Definition in TCG Pocket Collection Tracker: How Shinies and Baby Pokémon Are Tracked

> Learn how TCG Pocket Collection Tracker defines pack structures to track shiny and baby Pokémon, ensuring accurate pull rates with this essential guide.

- Repository: [Marcel Panse/tcg-pocket-collection-tracker](https://github.com/marcelpanse/tcg-pocket-collection-tracker)
- Tags: internals
- Published: 2026-03-06

---

**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`](https://github.com/marcelpanse/tcg-pocket-collection-tracker/blob/main/frontend/src/types/index.ts), the tracker defines a `PackStructure` interface that describes the composition rules for any given expansion:

```typescript
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`](https://github.com/marcelpanse/tcg-pocket-collection-tracker/blob/main/frontend/assets/cards.json) do **not** carry a "shiny" flag. Instead, the probability engine in [`frontend/src/lib/stats.ts`](https://github.com/marcelpanse/tcg-pocket-collection-tracker/blob/main/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**:

1. **Pack-level flag**: The `containsBabies` boolean in the `PackStructure` indicates whether the expansion can yield baby Pokémon at all
2. **Card-level flag**: Each card entry in [`frontend/assets/cards.json`](https://github.com/marcelpanse/tcg-pocket-collection-tracker/blob/main/frontend/assets/cards.json) includes a `baby` field (`true` | `false`)

The `pullRateForCardSubset` function in [`frontend/src/lib/stats.ts`](https://github.com/marcelpanse/tcg-pocket-collection-tracker/blob/main/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`](https://github.com/marcelpanse/tcg-pocket-collection-tracker/blob/main/frontend/src/lib/CardsDB.ts). Here, each expansion object optionally includes its `packStructure` definition. For example, the *A4* ("wisdomofseaandsky") expansion declares:

```typescript
{
  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:

```typescript
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.ts`](https://github.com/marcelpanse/tcg-pocket-collection-tracker/blob/main/frontend/src/lib/CardsDB.ts) contains the expansion definitions.

### Checking Card-Level Baby Status

Since baby status is stored per-card, filter the card database directly:

```typescript
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.json`](https://github.com/marcelpanse/tcg-pocket-collection-tracker/blob/main/frontend/assets/cards.json) includes the `"baby": true/false` field on card entries.

### Calculating Pull Rates

The `pullRateForCardSubset` function consumes the pack structure to compute accurate probabilities:

```typescript
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.ts`](https://github.com/marcelpanse/tcg-pocket-collection-tracker/blob/main/frontend/src/lib/stats.ts) implements the probability logic that respects pack structure flags.

## Summary

- The **pack structure definition** lives in the `PackStructure` interface at [`frontend/src/types/index.ts`](https://github.com/marcelpanse/tcg-pocket-collection-tracker/blob/main/frontend/src/types/index.ts), defining four key properties: `containsShinies`, `containsBabies`, `containsLinkedCards`, and `cardsPerPack`.
- **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 `containsBabies` flag with individual card `baby` properties in [`frontend/assets/cards.json`](https://github.com/marcelpanse/tcg-pocket-collection-tracker/blob/main/frontend/assets/cards.json).
- The **probability engine** in [`frontend/src/lib/stats.ts`](https://github.com/marcelpanse/tcg-pocket-collection-tracker/blob/main/frontend/src/lib/stats.ts) uses 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`](https://github.com/marcelpanse/tcg-pocket-collection-tracker/blob/main/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`](https://github.com/marcelpanse/tcg-pocket-collection-tracker/blob/main/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`](https://github.com/marcelpanse/tcg-pocket-collection-tracker/blob/main/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.