Skip to content

Commit b436e1a

Browse files
How to play. The pack is live.
Private repo is first-bucket-studios (with an s). Stills and tape from a live Rockets 22 walk. Foil now turns the ten so the demo is not ten extra clicks.
1 parent 86a9f1c commit b436e1a

11 files changed

Lines changed: 176 additions & 26 deletions

File tree

HOW-TO-PLAY.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# How to play
2+
3+
Ninety seconds. One pack. One card.
4+
5+
## The loop
6+
7+
1. **Rip the pack.** A franchise, an era, and a luck lane land. New every time.
8+
2. **Tear the foil.** Ten cards flip. Tap five.
9+
3. **Lock five.** Eighty-two nights walk from those names. The number on the card is that walk.
10+
4. **Send the card.** Save the PNG. Copy the walk. The URL is the certificate.
11+
12+
![Home](docs/stills/01-home.jpg)
13+
14+
![Foil](docs/stills/02-foil.jpg)
15+
16+
![The card](docs/stills/03-result.jpg)
17+
18+
![Send](docs/stills/04-tray.jpg)
19+
20+
One pull on tape: [docs/tape/demo.mp4](docs/tape/demo.mp4)
21+
22+
## What each button does
23+
24+
| You click | What happens |
25+
| --- | --- |
26+
| **Rip the pack** | Deals a new room. New ten. |
27+
| **Rip pack** / tap the foil | Tears it. The ten turn. |
28+
| **Lock five** | Walks 82 nights. |
29+
| **Send the card** | Opens the tray. |
30+
| **Save the card** | Downloads `first-bucket-{team}-{wins}.png`. |
31+
| **Copy the walk** | Copies `/walk/v1.…` so you can paste it. |
32+
| **Open the walk** | Same five, same nights, forever. |
33+
34+
There is no “copy image.” The file is the card. The URL is the walk.
35+
36+
Save needs a top-level tab. Inside a preview iframe the host may swallow the download — open the walk, then Save.
37+
38+
## House job (optional)
39+
40+
Thunder · Positionless · Even is pinned so one walk never moves:
41+
42+
```
43+
/walk/v1.OKC.positionless.even.51.chet~dort~hartenstein~jalenw~sga
44+
```
45+
46+
51–31. Playoff lock. Same five every time. It hangs on The Wall. The door is the live pack.
47+
48+
## What you are sending
49+
50+
- **The PNG** is the lithograph. Night stock. Foil number. Five plates.
51+
- **The walk URL** is the certificate. Nights recompute from the ids. It is not a replay file.
52+
53+
Paste a walk anywhere. If the host can open `/walk/…`, the card comes back.
54+
55+
## Not this
56+
57+
Not a shop. Not a sportsbook. Not the NBA. Names are game tokens. Read [NOTICE](NOTICE).

README.md

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,28 @@
1-
# First Bucket Studio
1+
# First Bucket Studios
22

33
**Rip a pack. Five names. Eighty-two nights. Send the card.**
44

5-
A browser basketball pack. Not a shop. Not a sportsbook. Not the NBA.
5+
Private repo (log in as DigitalCurrensy):
6+
7+
**https://github.com/DigitalCurrensy/first-bucket-studios**
68

7-
[MIT](LICENSE). Forks on. [How to copy the loop →](FORK.md)
9+
Not the older `first-bucket-studio` (no *s*). This one.
10+
11+
A browser basketball pack. Not a shop. Not a sportsbook. Not the NBA.
812

9-
> Play the live build, then paste the URL at the top of this file before you flip the repo public.
13+
[MIT](LICENSE) · [How to play](HOW-TO-PLAY.md) · [Fork the loop](FORK.md)
1014

1115
---
1216

1317
## How to play
1418

15-
About ninety seconds.
19+
About ninety seconds. Full sheet: [HOW-TO-PLAY.md](HOW-TO-PLAY.md).
1620

1721
**1. Rip the pack.** A franchise, an era, and a luck lane land. No two pulls match.
1822

1923
![Home](docs/stills/01-home.jpg)
2024

21-
**2. Tear the foil.** Ten cards, face down.
25+
**2. Tear the foil.** Ten cards. Tap five.
2226

2327
![Foil](docs/stills/02-foil.jpg)
2428

@@ -32,7 +36,7 @@ About ninety seconds.
3236

3337
Tape of one pull: [docs/tape/demo.mp4](docs/tape/demo.mp4)
3438

35-
### The buttons, in order
39+
### The buttons
3640

3741
| You click | What it does |
3842
| --- | --- |
@@ -53,7 +57,7 @@ Thunder · Positionless · Even is pinned as a known certificate:
5357
/walk/v1.OKC.positionless.even.51.chet~dort~hartenstein~jalenw~sga
5458
```
5559

56-
51–31. Playoff lock. Same five every time. It hangs on [The Wall](/wall). The main pack is live.
60+
51–31. Playoff lock. Same five every time. It hangs on The Wall. The main pack is live.
5761

5862
---
5963

docs/stills/01-home.jpg

10.4 KB
Loading

docs/stills/02-foil.jpg

11.8 KB
Loading

docs/stills/03-result.jpg

4 KB
Loading

docs/stills/04-tray.jpg

7.9 KB
Loading

docs/tape/demo.mp4

-23.4 KB
Binary file not shown.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"name": "first-bucket-studio",
2+
"name": "first-bucket-studios",
33
"private": true,
44
"license": "MIT",
55
"description": "A 90-second basketball pack. Tear the foil. Send the card.",

scripts/capture-demo.mjs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { chromium } from "playwright";
2+
import { mkdirSync, readdirSync } from "node:fs";
3+
import { execSync } from "node:child_process";
4+
import path from "node:path";
5+
6+
const ROOT = "/workspace";
7+
const STILLS = path.join(ROOT, "docs/stills");
8+
const TAPE = path.join(ROOT, "docs/tape");
9+
const TMP = "/tmp/fbs-tape";
10+
mkdirSync(STILLS, { recursive: true });
11+
mkdirSync(TAPE, { recursive: true });
12+
mkdirSync(TMP, { recursive: true });
13+
14+
const browser = await chromium.launch({
15+
args: ["--no-sandbox", "--disable-dev-shm-usage"],
16+
});
17+
const context = await browser.newContext({
18+
viewport: { width: 1440, height: 900 },
19+
deviceScaleFactor: 1,
20+
reducedMotion: "reduce",
21+
recordVideo: { dir: TMP, size: { width: 1440, height: 900 } },
22+
});
23+
await context.route("https://grok.com/**", (route) => route.abort());
24+
const page = await context.newPage();
25+
page.setDefaultTimeout(25000);
26+
27+
async function shot(name) {
28+
await page.waitForTimeout(220);
29+
await page.screenshot({
30+
path: path.join(STILLS, name),
31+
type: "jpeg",
32+
quality: 86,
33+
});
34+
console.log("still", name, page.url());
35+
}
36+
37+
await page.goto("http://127.0.0.1:8080/", { waitUntil: "domcontentloaded" });
38+
await page.waitForSelector("text=Rip the pack. Send the card.");
39+
await page.waitForSelector(".rip-pack");
40+
await page.waitForTimeout(400);
41+
await shot("01-home.jpg");
42+
43+
await page.goto("http://127.0.0.1:8080/games/82-0", { waitUntil: "domcontentloaded" });
44+
await page.waitForSelector(".rip-pack");
45+
await page.waitForFunction(() => {
46+
const el = document.querySelector(".rip-pack");
47+
return Boolean(el && el.getAttribute("aria-label") && !/house prospect/i.test(el.getAttribute("aria-label") || ""));
48+
});
49+
await page.waitForTimeout(500);
50+
await shot("02-foil.jpg");
51+
52+
await page.locator(".rip-pack").click();
53+
await page.waitForSelector(".pack-grid button");
54+
await page.waitForTimeout(700);
55+
56+
const cards = page.locator(".pack-grid button");
57+
const n = await cards.count();
58+
console.log("cards", n);
59+
for (let i = 0; i < Math.min(5, n); i++) {
60+
await cards.nth(i).click();
61+
await page.waitForTimeout(70);
62+
}
63+
64+
await page.getByRole("button", { name: "Lock five" }).click();
65+
await page.waitForSelector("text=Send the card");
66+
await page.waitForTimeout(400);
67+
await shot("03-result.jpg");
68+
69+
await page.getByRole("button", { name: "Send the card" }).click();
70+
await page.waitForSelector("text=Save the card");
71+
await page.waitForTimeout(400);
72+
await shot("04-tray.jpg");
73+
74+
const walk = await page.locator("input[readonly]").inputValue().catch(() => "");
75+
console.log("walk", walk);
76+
77+
await context.close();
78+
await browser.close();
79+
80+
const videos = readdirSync(TMP).filter((f) => f.endsWith(".webm"));
81+
if (!videos.length) throw new Error("no video");
82+
const src = path.join(TMP, videos[0]);
83+
const dest = path.join(TAPE, "demo.mp4");
84+
execSync(
85+
`ffmpeg -y -i ${JSON.stringify(src)} -an -c:v libx264 -pix_fmt yuv420p -crf 26 -preset veryfast -movflags +faststart -vf "fps=24,scale=1280:-2" ${JSON.stringify(dest)}`,
86+
{ stdio: "inherit" },
87+
);
88+
console.log("tape", dest);

src/components/eighty-two.tsx

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ export function EightyTwo({ mode, challenge }: { mode: Mode; challenge?: Challen
195195
if (ripping || ripped) return;
196196
setRipping(true);
197197
const reduce = typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
198+
const ids = activePack.map((p) => p.id);
198199
window.setTimeout(() => {
199200
setRipped(true);
200201
setRipping(false);
@@ -203,7 +204,17 @@ export function EightyTwo({ mode, challenge }: { mode: Mode; challenge?: Challen
203204
setOpen(staged.ten.map((p) => p.id));
204205
setPicks(staged.five.map((p) => p.id));
205206
commitWalk(staged.five, staged.walked);
207+
return;
206208
}
209+
ids.forEach((id, i) => {
210+
window.setTimeout(
211+
() => {
212+
setOpen((cur) => (cur.includes(id) ? cur : [...cur, id]));
213+
ripTick();
214+
},
215+
reduce ? 0 : i * 70,
216+
);
217+
});
207218
}, reduce ? 0 : 480);
208219
}
209220

@@ -317,7 +328,7 @@ export function EightyTwo({ mode, challenge }: { mode: Mode; challenge?: Challen
317328
? "Start the corners."
318329
: wnba
319330
? "Forty nights."
320-
: "Pull the room. Rip the pack.";
331+
: "Rip the pack. Send the card.";
321332
const lead = cue
322333
? "Thunder. Positionless. Tear the foil. Send the card."
323334
: daily
@@ -326,14 +337,12 @@ export function EightyTwo({ mode, challenge }: { mode: Mode; challenge?: Challen
326337
? "The pack deals G/G/F/F/C. The corners have to hold. Then the season walks."
327338
: wnba
328339
? "One pull. A W club, era, luck. Ten face-down. Forty nights. Honest length."
329-
: "Pull a franchise, era, and luck. Rip ten face-down. Turn five. Then 82 nights play.";
330-
331-
const beatHref = wnba ? "/games/wnba" : corners ? "/games/corners" : "/games/82-0";
340+
: "A franchise lands. Ten cards. Five names. Eighty-two nights. No two walks match.";
332341

333342
return (
334343
<div>
335344
<PageIntro
336-
kicker={cue ? "House pack" : daily ? "Daily Bucket" : corners ? "Four corners" : wnba ? "WNBA walk" : "Build an 82-0"}
345+
kicker={cue ? "House pack" : daily ? "Daily Bucket" : corners ? "Four corners" : wnba ? "WNBA walk" : "Prospect pack"}
337346
title={title}
338347
lead={lead}
339348
mark={daily ? "daily" : corners ? "corners" : wnba ? "wnba" : "machine"}
@@ -586,12 +595,12 @@ export function EightyTwo({ mode, challenge }: { mode: Mode; challenge?: Challen
586595
<Button variant="ghost" onClick={copyWalk}>
587596
{walkCopied ? "Walk copied" : "Copy walk"}
588597
</Button>
589-
{!cue && (
598+
{mode !== "82-0" && !cue && (
590599
<Button variant="ghost" onClick={copyLine}>
591600
{copied ? "Copied" : "Copy line"}
592601
</Button>
593602
)}
594-
{!cue && (
603+
{mode !== "82-0" && !cue && (
595604
<Button variant="ghost" onClick={copyChallenge}>
596605
{challengeCopied ? "Challenge copied" : "Beat it"}
597606
</Button>
@@ -601,14 +610,6 @@ export function EightyTwo({ mode, challenge }: { mode: Mode; challenge?: Challen
601610
{cue ? "Rip again" : "Pull again"}
602611
</Button>
603612
)}
604-
{!cue && (
605-
<Link
606-
to={beatHref === "/games/82-0" ? "/games/daily" : "/games/82-0"}
607-
className={cn("inline-flex min-h-11 items-center px-4 text-sm text-muted")}
608-
>
609-
{mode === "82-0" ? "Daily Bucket" : "Free build"}
610-
</Link>
611-
)}
612613
</div>
613614
</JobTicket>
614615
</div>

0 commit comments

Comments
 (0)