Bấm Bắt đầu mở sáu lá Cứu Nổ có biểu cảm riêng, đếm ngược 5 giây. Chạm/bàn phím chọn một lá trống. Hết giờ server tự cấp lá cho người chưa chọn rồi mới chia thêm bảy lá. Refresh giữ lựa chọn. Quy tắc ứng dụng công khai trong Luật phòng và rule contract.
Bốn lá Mèo Nổ có biểu cảm riêng. Có thanh chọn người để ném trứng/bom đồ chơi/đá nhỏ đồng bộ phòng, không đổi luật. Nút/chọn bài có phản hồi và SFX; chức năng có sticker, nổ/Defuse có cảnh riêng, bị loại xám/K.O., chia bài và thắng có hoạt ảnh. Results giữ hiệu ứng cuối ván. Mute, reduced motion và phụ đề được giữ.
SVG/sticker của cập nhật là đồ họa gốc. Audio dùng WebAudio và các tệp meme có sẵn trong public/audio; mẫu phát giới hạn theo từng hiệu ứng (0,8–3,5 giây), qua Master/Music/SFX/Mute và ducking. Asset dùng BASE_URL cho /kittens/. Không mô tả gói âm thanh meme là âm thanh gốc.
Game bài nhiều người chơi theo thời gian thực, 2–5 người mỗi phòng, không cần tài khoản. Mỗi trình duyệt tự chọn Tiếng Việt/English, âm lượng và mức chuyển động. Cả bốn nét vẽ cùng xuất hiện trong một bộ bài; mỗi loại lá có minh họa cố định cho mọi người. Mã phòng và liên kết mời đưa bạn bè vào cùng một ván.
Chơi ngay: Game production trên VPS. Nginx trên vps-cong phục vụ tại http://localhost:9000/kittens/; HTTP trực tiếp tại cổng 9000. Backend chỉ bind 127.0.0.1:9005, qua proxy /kittens/socket.io/. Ván 2–5 người đã được kiểm tra bằng nhiều Chromium thật trên URL production.
Các nét bút bi, giấy đóng dấu, pixel và hình học là SVG/CSS nguyên bản. Không sao chép artwork hoặc hình minh họa từ bộ bài thương mại. Bộ 56 lá và lượt cơ bản tham khảo hướng dẫn Original Edition và field guide chính thức. Tám loại lá thêm (mỗi loại ba lá) và Hồi Sinh là quy tắc mở rộng của ứng dụng, được ghi cụ thể trong rule contract và bảng luật ở lobby; không được trình bày như luật gốc.
Bốn nét bút bi, đóng dấu, pixel và hình học được phối chung theo loại bài, không còn chọn style riêng trong game hoặc kho bài. cardPresentation.ts quyết định minh họa cố định; cài đặt kittens.style cũ không ảnh hưởng hiển thị. Bài trên tay, bộ bỏ, khay xem trước, kho bài và hoạt ảnh dùng cùng minh họa. Module nét vẽ được tải khi một lá thuộc nét đó xuất hiện; bộ xem trước ở trang vào game dùng cả bốn.
22 loại lá có mèo, biểu cảm, tư thế, đạo cụ và mảng màu nguyên bản. Nền giấy hồng ấm, mặt sau đỏ gạch; Cứu Nổ xanh, Mèo Nổ đỏ, các chức năng và họ combo có màu riêng. Tên, nhóm và mô tả luôn ở cùng vị trí. Kho bài có bộ lọc, luật VI/EN và xem hoạt ảnh; trên mobile cuộn theo một cột.
Yêu cầu Node.js 22+ và npm 11+.
npm ci
npm run devMở http://localhost:5173. Server ở http://localhost:3001; GET /health dùng để kiểm tra sức khỏe. Mở hai cửa sổ trình duyệt, nhập hai nickname, tạo phòng ở cửa sổ đầu và vào bằng mã hoặc link ở cửa sổ sau. Mọi người bấm Sẵn sàng; chủ phòng bắt đầu. Đánh các lá hợp lệ, hoặc rút để kết thúc lượt. Người sống cuối cùng thắng; chủ phòng chọn chơi lại để tạo ván mới.
Tạo apps/web/.env.local khi web và server ở hai origin:
VITE_SERVER_URL=http://localhost:3001
VITE_SOCKET_PATH=/socket.ioServer nhận PORT, CORS_ORIGIN (danh sách origin phân tách bằng dấu phẩy) và SOCKET_PATH. Xem .env.example.
npm run test
npm run typecheck
npm run build
npm run check:encoding
npm audit --omit=devEngine có test chia bài, bảo toàn lá, Attack/Skip/Nope, mọi khe Defuse, combo, tám lá mở rộng và Hồi Sinh. Server có test Socket.IO thật cho 2–5 người, ván đến người thắng và rematch, reconnect, hành động đồng thời/lặp/cũ, spectator và quyền riêng tư chèn bài. Kết quả chạy và giới hạn đo trình duyệt nằm ở báo cáo kiểm thử.
Kiểm thử production bằng UI và hai transport riêng:
$env:QA_BASE_URL='https://beatsync-server.zney295.id.vn/kittens/'
$env:QA_ARTIFACT_DIR='docs/qa/production'
npm run test:e2e
node scripts/deployment-smoke.mjs
$env:SMOKE_TRANSPORT='polling'
node scripts/deployment-smoke.mjsKhung chat riêng có trong lobby, bàn chơi và màn kết quả. Desktop hiển thị bên cạnh bàn; mobile có nút Trò chuyện kèm số tin chưa đọc. Gửi bằng Enter hoặc nút Gửi; Shift+Enter xuống dòng. Mỗi tin tối đa 240 ký tự, hiển thị người gửi và giờ gửi. Nhãn VI/EN đổi theo từng trình duyệt; nội dung người chơi giữ nguyên.
Server giữ 100 tin gần nhất của từng phòng, độc lập nhật ký hành động, để phục hồi sau reconnect và giữ khi chơi lại. Người xem và người bị loại vẫn có thể chat. Chỉ thành viên của phòng nhận tin; danh tính người gửi lấy từ guest session. Nội dung được render như văn bản, có validation và rate limit. Bản nháp không mất khi thu gọn, chuyển màn, gửi thất bại hoặc mất kết nối trong trang đang mở. Phòng, lịch sử và session hiện lưu trong RAM, nên restart server sẽ mất chúng.
Bàn chơi dùng nền đỏ rượu ấm và logo mèo riêng. Chạm từng lá để chọn; lá nhấc lên với viền vàng. Chọn hai/ba lá cùng tên tự nhận diện combo, chọn ++ với lá số tự nhận diện tăng chức năng. Chạm avatar đối thủ để chọn mục tiêu; bộ ba có danh sách gọi tên bài gọn. Đánh/Rút nằm ngay dưới tay bài. Xếp bài, chọn/bỏ chọn, mở menu và xem gợi ý chạy trên client, không gửi yêu cầu lên server.
Tay bài để lộ đầy đủ tên và biểu tượng, cuộn ngang khi nhiều lá. Nút Xếp bài gom các lá cùng tên cạnh nhau với nhãn số lượng, chia thành cụm Phòng thủ, Tấn công, Hỗ trợ, Xem bài, Ghép combo và Nguy hiểm khi có lá nổ. Mỗi lá vẫn chọn riêng được; xếp bài giữ nguyên combo và mục tiêu, lá mới tự vào đúng cụm. Bấm lại để về thứ tự ban đầu. Rê chuột xem lá lớn; nút kính lúp hoặc nhấn giữ trên màn hình cảm ứng mở minh họa và luật chi tiết, kể cả khi đang chờ lượt. Xem bài giữ nguyên combo và mục tiêu đã chọn, không gửi hành động; đồng hồ vẫn chạy. Bấm lá trên cùng bộ bỏ để đọc lại. Trước khi đánh, khung tóm tắt ghi mục tiêu, tác dụng, lá gọi tên và phần tăng của ++. Số lượt còn lại và giây nằm cạnh tên lượt; avatar có quạt bài úp và phản hồi ngắn. Màn hình ngang thấp đặt bàn cạnh tay bài để giữ nút thao tác trong khung nhìn.
Chat, lịch sử, đồ chơi, trợ giúp và tự chơi được thu gọn; cài đặt, kho bài, hướng dẫn, ngôn ngữ và rời phòng nằm trong Menu. Mã phòng mới là chuỗi sáu chữ số (giữ số 0 đầu); server vẫn nhận link phòng cũ khi nâng cấp. Ping là độ trễ khứ hồi đo mỗi 15 giây, tạm dừng khi tab ẩn. Khi đánh bài có thông báo chức năng ngắn; Xáo bài chỉ xáo hai chồng tại bộ rút khi hiệu lực được server xác nhận sau cửa sổ Nope. Giảm chuyển động giữ thông báo tĩnh. Âm lượng SFX mặc định thấp hơn và mức gain giảm cho cả tùy chọn đã lưu.
- Chạm trực tiếp từng lá; cặp, bộ ba và ++ được nhận diện từ các lá đã chọn. Lá đơn thay thế lựa chọn cũ; nhóm ghép chỉ nhận cùng tên, ++ chỉ đi với lá có giá trị số. Nút chọn người hiện tên, số bài và dấu xác nhận. Bộ ba có bảng gọi tên bài riêng. Xem lại tóm tắt rồi bấm Đánh bài.
- Lá được chọn nhấc lên với viền vàng. Menu Trợ giúp & tự chơi đề xuất xem tương lai, tránh mèo nổ đã được xem hoặc ghép bài mèo. Gợi ý chỉ dùng snapshot của người chơi, không biết bộ rút thật hay bài đối thủ; không bảo đảm thắng.
- Các bước Xin bài, Dơi và Hamster chỉ chọn đúng một lá mỗi lần. Hamster hiển thị số lá còn phải bỏ. Hướng dẫn nhanh có ngay trên bàn, dịch VI/EN độc lập.
- Đồng hồ và thanh thời gian dùng
serverNowđể bù lệch giờ trình duyệt. Mỗi phase ghi rõ người cần trả lời và fallback khi hết giờ. Trạng thái đã bỏ qua Nope giữ được sau reconnect. - Tự chơi tắt mặc định mỗi ván: tự rút khi còn 5 giây hoặc tự chơi cơ bản. Cơ bản ưu tiên xem tương lai, tránh nổ đã biết, ghép bài mèo; giữ Cứu nổ, bỏ qua Nope, chọn bài ít giá trị cho quyết định bắt buộc, chèn xuống đáy và chọn Búa. Mỗi lượt tối đa ba hành động đánh trước khi rút. Bấm một lá để dừng. Tự chơi chỉ chạy ở tab đang hiện và kết nối; mọi ý định vẫn phải qua validation của server. Chơi lại đặt về Tắt. Đây là tính năng hỗ trợ của ứng dụng.
- 22 loại bài có cảnh SVG riêng và màu chức năng; bốn nét vẽ phối chung trong một bộ bài, cùng vị trí thông tin.
Runner riêng chỉ dùng trên máy kiểm thử. scripts/qa-server.ts không được import hoặc đưa vào Docker runtime; cổng chuẩn bị bài 3013 chỉ bind loopback. Browser chơi qua Socket.IO thật và các chuyển trạng thái của engine.
# Terminal 1
npx tsx scripts/qa-server.ts
# Terminal 2
$env:VITE_SERVER_URL='http://localhost:3012'
npm run dev -w @kittens/web -- --port 5182
# Terminal 3
$env:QA_BASE_URL='http://localhost:5182'
$env:QA_FIXTURES='1'
$env:QA_ARTIFACT_DIR='docs/qa/improvements-final-local'
npm run test:e2eKhông bật QA_FIXTURES khi kiểm tra URL production. Ba kiểm tra cần chuẩn bị bài sẽ tự bỏ qua ở production; các kiểm tra full game và tự chơi vẫn chạy.
| Chế độ | Số lá trước khi chia | Thêm vào |
|---|---|---|
| Cơ bản | 56 | Luật lượt cơ bản, cặp/ba lá, Nope. |
| Mở rộng (mặc định) | 80 | Amateur Archaeology, Battle Hamster, Creepy Peeky, Hip Bat, Hip Cat, Plus Plus, Robin Hood, The Twins; mỗi loại ba lá. |
| Hồi Sinh tùy chọn (mặc định tắt) | +2 | Hai lá Hồi Sinh do ứng dụng thiết kế, không nằm trong bộ 56/80. |
Mỗi ván bắt đầu với một Defuse và bảy lá khác cho mỗi người; số lá Mèo Nổ trong bộ rút bằng số người chơi trừ một. Sau Defuse, người chèn tự chọn khe 0..N; những người khác chỉ biết TOP, BOTTOM hoặc MIDDLE_HIDDEN. Các quyết định hết giờ do server giải quyết. Bảng đầy đủ, gồm trường hợp thiếu bài, mất kết nối và quyền xem, nằm ở docs/RULE_CONTRACT.md.
Monorepo npm workspaces:
packages/shared: mã lá ổn định, schema Zod, type event/snapshot.packages/engine: state machine TypeScript thuần, RNG phía server, bất biến bảo toàn lá.apps/server: Express + Socket.IO, guest session, room queue, timer, lọc thông tin theo người xem.apps/web: Vite + React, responsive UI, dịch trên client, SVG/CSS, Web Audio và hoạt ảnh.
Sơ đồ state machine, mô tả socket/event và ranh giới quyền xem ở docs/ARCHITECTURE.md. Intent game gồm gameId, turnId, actionId, expectedRevision; server kiểm tra lại trước mỗi thay đổi. Event có seq/revision; reconnect lấy snapshot đã lọc. Client không tải thứ tự bộ rút hoặc tay bài người khác.
VPS đã có bản production, Docker healthcheck và Nginx9000 cho HTTP/WebSocket. Hướng dẫn cấu hình, backup, cập nhật và kiểm tra đầy đủ trong deploy/README.md. Trên VPS:
cd /home/ubuntu/exxplore-kittens
bash deploy/start-release.sh
bash deploy/verify-release.shTriển khai lại backend là thao tác bảo trì: container mới thay process và kết thúc các ván trong memory. File cấu hình Nginx được backup rồi kiểm tra trước reload. Với bản chỉ đổi web và tương thích giao thức hiện tại, chạy bash deploy/publish-web.sh: build image production, lấy static từ container tạm không chạy, giữ asset cũ và thay index cuối cùng. Script kiểm tra container backend và thời điểm khởi động không đổi; Nginx không cần reload. Xem hướng dẫn triển khai.
Backend một instance trên VPS:
cp .env.example .env
# Điền CORS_ORIGIN bằng origin HTTPS của web.
docker compose up -d --buildcompose.yaml chỉ mở backend trên loopback 127.0.0.1:9005. Reverse proxy cần chuyển WebSocket upgrade và path Socket.IO; đặt VITE_SERVER_URL và VITE_SOCKET_PATH tương ứng ở web build. Web có thể build npm run build -w @kittens/web rồi phục vụ apps/web/dist bằng static host, hoặc triển khai Vite app lên Vercel. Trang HTTPS cần backend HTTPS/WSS.
Hiện trạng lưu phòng và guest token trong bộ nhớ của một server. Khởi động lại process sẽ mất ván đang chơi. Để chạy nhiều replica hay giữ ván qua restart, phải thêm kho state bền vững, hàng đợi/khóa theo phòng và adapter Socket.IO; chỉ thêm adapter không đủ. Không lưu nickname hay tay bài vào log server. URL triển khai thực tế và kết quả kiểm thử đã ghi trong docs/TEST_REPORT.md.
Vercel dùng cấu hình tại vercel.json, cài tất cả workspace và devDependencies từ gốc repo. Xem hướng dẫn Vercel và ENV và file ENV mẫu. Web Vercel kết nối HTTPS đến VPS; domain web phải nằm trong exact CORS allowlist của backend. AGY_HANDOFF.md giữ trạng thái công việc; scripts/continue-agy.ps1 mở AGY với quyền thông thường.