OhMyGame
OhMyGame은 AI로 게임을 만들 수 있는 오픈소스 스튜디오로, 디자인·구축·플레이테스트·출판을 하나의 앱에서 모델 키와 함께 수행합니다
저장소 정보는 아직 수집하지 않았습니다.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
Chkobba (شكوبة), the Tunisian card game, in the browser: play a smart bot offline or 2–4 friends online in real time. A server referee on Cloudflare Workers deals and checks every move, so nobody can peek.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다.
README 전문 보기 ↗The classic Tunisian card game in the browser: play a bot offline, or share a link and play friends online in real time.
▶ Play it online: chkooba.pages.dev
A multiplayer browser implementation of Chkobba (شكوبة), the popular Tunisian card game. A small game server is the referee: it shuffles, deals, checks every move and keeps score, and each player's browser only ever sees its own hand. The server runs on Cloudflare Workers + Durable Objects (free plan) or locally on Node. The page itself is vanilla HTML, CSS and JavaScript with no build step.
i on a face-up card to see its value, what it scores and what it can take right now.DEAL 4/6.i on a face-up card to open its info panel.The server is the referee. Each room is one instance of public/js/room.js, a referee with no network code of its own: on Cloudflare it lives in a Durable Object (one per room code), locally it lives in a Map in server.js. Browsers talk to it over a plain WebSocket.
browser (seat 1) room CODE (Durable Object) browser (seat 0)
──────────────── ────────────────────────── ────────────────
move {game, seq, cardId, captureIds} ──▶ check: turn, card in hand, legal capture
apply, score, run bot seats, timers
state: public table + MY hand only ◀─── one snapshot per seat after every change ───▶ public table + MY hand only
ack {ok | error, reason, mustTake} ◀───
public/js/net.js sends requests with acks, pings every 8 seconds, reconnects with backoff, and asks for a full snapshot after any drop or timeout. Every button that waits on the server shows it, then comes back on the answer, an error or a timeout.localStorage for that room, so a reload resumes the same seat and hand. A dropped seat is held for 60 seconds, then the others decide: a bot plays it, or the game ends with a win for the side still there.location.origin + '/?room=CODE', with a Copy button in the waiting room and the game menu.room.js inside the page through a local transport, so the offline game and the online game share one referee.Node 18 or newer:
npm install
npm start # http://localhost:3110 (PORT=8080 npm start to change it)
npm start serves public/ and the game rooms from one Node process (server.js, express + ws). Open the page in two browser windows to play yourself.
To run the Cloudflare version locally instead (no account needed):
npx wrangler dev --port 8794
Both parts run on the free plan. No keys or environment secrets are needed.
1. The Worker (game server + the site). In the Cloudflare dashboard: Workers & Pages → Create application → Continue with GitHub → pick this repo. Project name: chkobba. Deploy command: npx wrangler deploy. wrangler.jsonc already declares the static assets (public/), the Room Durable Object and its migration. Once deployed, the whole game works at https://chkobba.<your-account>.workers.dev.
2. Optional: a Pages copy of the site. Workers & Pages → Create application → Continue to Pages → pick this repo. Framework preset: None. Build command: leave empty. Build output directory: public. On a *.pages.dev host, public/server.js points the page at the Worker (https://chkobba.hamza-benismail-6.workers.dev), so the rooms still run in the Worker.
If Cloudflare gives the Pages site a name other than chkobba.pages.dev, add that host to ALLOWED_HOSTS in wrangler.jsonc (comma-separated; subdomains such as preview deploys are included) and redeploy the Worker. If the Worker's own URL is different, change it in public/server.js.
The Worker only accepts /api/* and /ws requests from pages with no Origin, its own origin, localhost/127.0.0.1, or an https host listed in ALLOWED_HOSTS (or a subdomain of one). Any other site gets 403.
localStorage.The older audit of the Firebase version is kept in docs/AUDIT.md, with a note on what the server move fixed.
chkoba/
├── public/ # Everything the browser loads (served by the Worker or by npm start)
│ ├── index.html # Single-page app: menu, options, join, waiting room, game, overlays
│ ├── server.js # Where the game server is (the Pages copy points at the Worker)
│ ├── game.js # Client: screens, snapshots, render queue + animation, requests to the room
│ ├── css/
│ │ ├── tokens.css # Design tokens and the three themes (html[data-theme])
│ │ ├── game.css # Layout (phones included), frames and pills, cards, effects, overlays
│ │ └── debug.css # The debug panel (debug() in the console)
│ ├── js/
│ │ ├── rules.js # Pure rules: legal captures, move check, bot move, round scoring
│ │ ├── room.js # The referee: seats, dealing, turns, scoring, bots, timers, snapshots
│ │ ├── net.js # WebSocket client (acks, heartbeat, reconnect) + the local transport
│ │ ├── i18n.js # Labels (Latin / Arabic / English) and rule text
│ │ ├── shader.js # WebGL swirl background (themes that use one)
│ │ ├── fire.js # Pixel fire for the chkobba slam and the tally
│ │ ├── juice.js # Motion: tags, slam, flights, count-ups, shake, text-fill, sounds
│ │ ├── cards.js # Card renderer: photo / flat / pixel, French or coin suits
│ │ ├── look.js # Room look (host-owned) and personal comfort settings
│ │ └── debug.js # The debug panel: debug() in the console
│ └── images/ # 40 PNG card faces
├── worker/index.js # Cloudflare Worker: assets, POST /api/rooms, /ws -> Room Durable Object
├── wrangler.jsonc # Worker config: assets, Durable Object binding and migration, ALLOWED_HOSTS
├── server.js # Local server: express static + ws rooms (npm start)
├── src/origin.js # Origin allowlist shared by the Worker and the local server
├── tests/
│ ├── rules.test.js # Rules and scoring
│ ├── room.test.js # The referee: hidden hands, illegal and stale moves, seats, teams, holds
│ ├── server.test.js # The local server over real HTTP and WebSockets
│ └── e2e/e2e.py # Playwright end-to-end flows (see Tests)
├── experimentals/ # Standalone UI lab used to design the current look
└── docs/
├── AUDIT.md # Code and security audit (of the Firebase version)
├── CHANGES-since-netlify.md # What changed for players since the Netlify version
└── screenshots/
ws.accept() rather than hibernation so the room's timers (bot turns, the turn timer, the 60 s seat hold) keep running. The room is also saved to the object's storage, so a deploy or an eviction does not lose a game.public/js/room.js, which uses the same rules.js as before, so the scoring is unchanged.npm test
runs the unit tests with node --test: the rules and scoring (rules.test.js), the referee (room.test.js: no hand or deck order in any message to another seat, illegal moves refused, duplicate and stale moves ignored, reconnecting with the seat token, 4-player team scoring, two rooms side by side, the host leaving, the 60 s hold, timers) the local server (server.test.js: bad and unknown room codes, a full room, origins and CORS, reconnecting over a real socket) and the bot levels (bot-levels.test.js: easy and medium pinned move for move, the hard bot legal with and without "Bots see all cards"). node tests/bot-strength.js (not part of npm test) plays matches between the hard bot, the previous hard bot and the see-all hard bot and prints the win rates.
End-to-end checks drive real browser pages with Playwright for Python (pip install playwright && playwright install chromium), one browser with a context per player:
npm start # then:
python tests/e2e/e2e.py local http://127.0.0.1:3110 # 2-player round by invite link, 4-player teams, reload, offline bot
python tests/e2e/e2e.py away http://127.0.0.1:3110 # a drop, the 60 s hold, a bot takes the seat
npx wrangler dev --port 8794 # then:
python tests/e2e/e2e.py online http://127.0.0.1:8794
Type debug() in the browser console to open the debug panel (type it again, or press Escape, to close it), on any device, whenever you want. It opens verbose logging too. Online, only the room's host can open it: for a guest debug() answers "only the host can use the debug menu" and nothing opens.
Every button sends one debug message to the room, and the referee (public/js/room.js, DEBUG_OPS) does the change, so the offline and the online panel are the same code: every hand, the table, the deck, add or remove cards, undo, snapshots, set the turn, scores and chkobbas, move cards between zones, force a deal, a score or a win, bots, teams, "play as" another seat, freeze and spy mode, and Sees all cards per bot in Bot Management (off by default; All bots On/Off sets every bot at once): a checked bot, whatever its level, plays the hard bot with every hand and the deck order in view, searching the real cards; unchecking it puts that bot back at once. Each bot also gets a Win rate slider (0 to 100%, or Off, the default: its normal level): while it is set, the bot mixes the see-all best move with deliberately bad ones so that it wins about that share of matches against a normal hard bot (calibrated with node tests/bot-strength.js 100 calibrate); at 100% it always plays the see-all best move. Deal Control sets every seat, the host included, to Normal (a fair deal), Good cards (the 7 of diamonds, sevens, diamonds, cards that take or sweep; several Good seats pick in turn by seat order) or Bad cards (low plain cards that take nothing, with a table they cannot take from), from the next deal on; All normal resets every seat. The deck stays the 40 cards with the usual hand sizes, only its order changes. These settings live in the room only, go to debug connections only (a guest cannot tell a seat is rigged) and are never saved.
For verbose logging without the panel, add ?debug=1 to the URL, or set localStorage.chkoba_debug = "true".
| Layer | Technology |
|---|---|
| UI | Vanilla HTML + CSS (custom design tokens, three themes), WebGL background shader |
| Logic | Vanilla JavaScript (no framework, no build step) |
| Game server | Cloudflare Workers + Durable Objects (free plan), or Node with express + ws |
| Transport | Plain WebSockets with acks, heartbeat and reconnect |
Design and themes by Jihed Jarboui (@Jyhed).
© 2026 Hamza Ben Ismail. All rights reserved.