Przejdź do treści

~/projekty / first-million

First Million

Gra giełdowa w czasie rzeczywistym: wybierasz kapitał startowy i cel, handlujesz na czas i ścigasz się z celem solo albo z do trzema rywalami w jednym pokoju, gdzie wszyscy handlują tą samą ceną.

rola
full stack: wspólny silnik symulacji, backend realtime, frontend w Vue
czas
paź 2026
typ
własny
stack
TypeScript · Vue 3 · Vite · Cloudflare Workers · Durable Objects · Firebase Auth · Zod
demo.mp4

01 / problem

Gra giełdowa wymaga jednej ceny dla wszystkich

Gra handlowa wydaje się uczciwa tylko wtedy, gdy każdy gracz w pokoju handluje tą samą ceną, a runda kończy się dla wszystkich w tym samym momencie. Cena nie może pochodzić z każdej przeglądarki osobno, bo gracze widzieliby różne rynki. Między rundami nic nie jest zapisywane, a całość ma działać za zero złotych.

02 / rozwiązanie

Cenę trzyma serwer, przeglądarka wysyła intencje

Gracz wybiera długość rundy: 3, 5 lub 10 minut, kapitał startowy od 1 000 do 100 000 $ i cel bez górnej granicy, a potem kupuje, sprzedaje lub zamyka pozycję o wielkości od 10 do 250 jednostek. Tempo to 1×, 3× lub 8×, a rundę można wstrzymać. Pokój do czterech graczy handluje jedną ceną z obiektu Durable Object, a gracz, którego kapitał spadnie do zera, odpada. Przeglądarka tylko wysyła zlecenia; serwer je sprawdza i prowadzi rundę.

  • Ustawienia: kapitał startowy, cel i długość rundy

    Ustawienia: kapitał startowy, cel i długość rundy

  • Margin call: kapitał spada do zera i runda się kończy

    Margin call: kapitał spada do zera i runda się kończy

03 / architektura

Jak to działa

Monorepo pnpm z trzema pakietami. shared to czysty silnik w TypeScript bez API platformy: generator losowy i czas są przekazywane z zewnątrz, więc te same reguły działają w przeglądarce w grze solo i w Workerze w pokojach. worker działa na Cloudflare Workers, z jednym obiektem Lobby i jednym Room Durable Object na pokój, i weryfikuje tokeny ID Firebase. web to aplikacja Vue 3, która renderuje stan wytworzony przez czyste reducery. Stan żyje wyłącznie w pamięci, bez storage'u, KV i D1.

04 / wyniki

Efekt

05 / wnioski

Czego się nauczyłem

  • Napisanie silnika jako czystych funkcji, z generatorem losowym i zegarem przekazywanymi z zewnątrz, pozwoliło testować te same reguły ze stałym ziarnem i sztucznym zegarem, zarówno w grze solo, jak i w pokojach.
  • Trzymanie stanu tylko w pamięci oznacza, że restart kończy trwające pokoje, ale utrzymuje model w prostocie i koszt na zero.