# Card hunt example

This is the panel from Hype Stack's Wrapped, rebuilt from the two packages and nothing else. Five cards hide in a grid of 210 cells. The scan finds them one at a time, each with its tier's reveal, and what was opened is remembered on this device.

> Live preview: see it play at https://bettertyped.github.io/react-cards-opening/docs/examples/card-hunt/
>
> Scan, then press a badge or a lit cell to see a card again.

## The code

`hunt.tsx`

```tsx
import { useOpenedCards } from "@react-cards-opening/core";
import { CardHunt, themeOf } from "@react-cards-opening/ui";
import "@react-cards-opening/ui/styles.css";

import { cards } from "./cards";

/** Thirty weeks of activity, 0 (nothing) to 4 (a lot). Use your own data here. */
const levels = Array.from({ length: 30 * 7 }, (_unused, day) => [0, 1, 2, 0, 3, 2, 4, 1, 0, 2][(day * 7) % 10]);

const describeSpot = (cell: number) => `week ${Math.floor(cell / 7) + 1}, day ${(cell % 7) + 1}`;

export function Hunt() {
  // Keyed by the deal, so a new deal starts unopened.
  const { opened, open, ready } = useOpenedCards("example-deal-1");
  if (!ready) return null;
  return (
    <CardHunt
      cards={cards}
      opened={opened}
      onOpen={open}
      levels={levels}
      theme={themeOf("grape", "dark")}
      describeSpot={describeSpot}
      sessionKey="example-deal-1:spots"
    />
  );
}
```

## What to change first

- **`levels`**: your real data. The shape of the grid is what makes the hunt feel like it belongs to the person.
- **`describeSpot`**: what a cell is called. A date, a match, a square of a board.
- **The key**: who the deal is for and when it was made.
- **`labels`**: the words. See [Labels and translation](https://bettertyped.github.io/react-cards-opening/docs/guides/labels-and-translation/).
- **`landing`**: send found cards somewhere other than their badge. See [Origin and landing](https://bettertyped.github.io/react-cards-opening/docs/guides/origin-and-landing/).

## Take it apart

`CardHunt` is a layout, and you may want another. The same hunt from its parts is [ScanGrid](https://bettertyped.github.io/react-cards-opening/docs/components/scan-grid/) for the grid, [CardOpening](https://bettertyped.github.io/react-cards-opening/docs/components/card-opening/) for the reveal and [useCardOpening](https://bettertyped.github.io/react-cards-opening/docs/core/use-card-opening/) between them, with `scan.seek()` as the reveal's `launch`.

```tsx
const scan = useRef<ScanGridHandle>(null);
const hunt = useCardOpening({ cards, opened, onOpen: open });
const [spots, setSpots] = useCardSpots("example-deal-1:spots");

<ScanGrid handle={scan} levels={levels} idle={!hunt.running} />
<button onClick={() => hunt.start()}>Scan</button>
<CardOpening
  opening={hunt.opening}
  launch={(card, show) => {
    const cell = spots[card.id] ?? pickSpot(levels, Object.values(spots));
    return scan.current?.seek(cell, show, { onLock: () => setSpots((now) => ({ ...now, [card.id]: cell })) }) ?? null;
  }}
/>
```

## Related

- [CardHunt](https://bettertyped.github.io/react-cards-opening/docs/components/card-hunt/)
- [ScanGrid](https://bettertyped.github.io/react-cards-opening/docs/components/scan-grid/)
- [Hiding places](https://bettertyped.github.io/react-cards-opening/docs/core/spots/)
- [Collection example](https://bettertyped.github.io/react-cards-opening/docs/examples/collection/)

Source: https://bettertyped.github.io/react-cards-opening/docs/examples/card-hunt/
