NON-PRODUCTION

Mines — tài liệu ghép client

Chọn ô trên lưới, tránh mìn. Cashout hoặc clear board để nhận thưởng. Client nói HTTP JSON với server game. Trang này chỉ được serve khi ENVIRONMENT != production.

1. Tổng quan

MụcGiá trị
Boardlevels[].row_size × col_size. Size lấy từ /gameplay, đừng hardcode.
Indexposition tuyến tính row-major: index = row * col_size + col. Ô (0,0) = 0.
TiềnTrừ bet lúc /start (reason BET). WIN lúc /stop hoặc clear board. Nổ mìn = 0, không hoàn bet.
MultiplierMảng theo số ô an toàn đã mở. current_multiplier sau gem; next_multiplier nếu mở tiếp.
Base path/api/v1
Session in-memory. Vào game bắt buộc GET /api/v1/gameplay. Nếu is_playing thì restore ô đã mở, không gọi /start lại.

2. Demo bàn 5×5

Số trên ô = position. Xanh = gem đã mở, đỏ = mìn lộ, xám = chưa mở.

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

Ví dụ: ô hàng 3 cột 3 → 3 * 5 + 3 = 18 (mìn). Client highlight theo opened_positions / mine_positions, không tự random mìn.

3. Auth & envelope

Authorization: Bearer <access_token>
Content-Type: application/json

Mọi API dưới /api/v1 cần JWT. Trang docs không cần token. HTTP 200 + code = 0 là thành công; payload nằm trong msg.

{
  "code": 0,
  "msg": { ... }
}
codeÝ nghĩa
0OK
-1Unknown / thiếu JWT / token invalid (HTTP 401)
-2Invalid input (body JSON sai)
69Wrong game state — chưa /gameplay, đang chơi mà /start, bet/size không hợp lệ, mở ô đã mở
1000Popup error (i18n)
POST /start bị middleware.Block: user đang room PvP khác → HTTP 400 body "blocked".

4. GET /api/v1/gameplay

Gọi 1 lần khi vào. Set session. Thiếu bước này thì action sau trả code 69.

FieldTypeClient dùng để
user_moneyint64Chip hiện tại
betint64Bet đang chọn trên server
betsint64[]Mức bet hợp lệ — chỉ gửi đúng 1 giá trị này
show_betsint64[]Giá trị hiển thị UI (cùng index với bets)
levelsLevel[]Size bàn + cấu hình mìn
is_playingboolĐang giữa ván → restore grid
opened_positionsint[]Ô gem đã mở (khi playing)
number_of_minesintSố mìn ván hiện tại / mặc định level 0
current_multiplierfloatHệ số cashout hiện tại
next_multiplierfloatHệ số nếu mở thêm 1 gem
current_row_size / current_col_sizeintKích thước bàn đang chơi
multipliersfloat[]Toàn bộ nấc hệ số theo số gem đã mở

Level

FieldÝ nghĩa
row_size, col_sizeGửi đúng cặp này khi /start
mines[]{number_of_mines, init_multiplier} — số mìn hợp lệ + hệ số lúc 0 gem
quick_minesShortcut UI (vd 1 / 5 / 10)
{
  "code": 0,
  "msg": {
    "user_money": 500000,
    "bet": 1000,
    "bets": [200, 1000, 2000, 10000],
    "show_bets": [200, 1000, 2000, 10000],
    "levels": [
      {"row_size": 5, "col_size": 5, "mines": [{"number_of_mines": 3, "init_multiplier": 1.12}], "quick_mines": [1, 3, 5]}
    ],
    "is_playing": true,
    "opened_positions": [0, 3, 6, 12],
    "number_of_mines": 3,
    "current_multiplier": 1.48,
    "next_multiplier": 1.72,
    "current_row_size": 5,
    "current_col_size": 5,
    "multipliers": [1.12, 1.28, 1.48, 1.72]
  }
}

5. POST /api/v1/start

Debit bet ngay. Không gọi khi is_playing.

RequestTypeÝ nghĩa
betint64Phải thuộc bets
row_size, col_sizeintMột level từ levels
number_of_minesintMột giá trị trong levels[].mines
cheat_mine_positionsint[]?Chỉ non-prod / cheater. Index tuyến tính.
ResponseÝ nghĩa
user_moneyChip sau khi trừ bet
bet, number_of_mines, row_size, col_sizeEcho config ván
current_multiplier0 lúc mới start (chưa mở ô)
next_multiplierHệ số gem đầu = multipliers[0]
multipliersToàn bộ nấc; length = cells − mines
POST /api/v1/start
{ "bet": 1000, "row_size": 5, "col_size": 5, "number_of_mines": 3 }

{
  "code": 0,
  "msg": {
    "user_money": 499000,
    "bet": 1000,
    "number_of_mines": 3,
    "current_multiplier": 0,
    "next_multiplier": 1.12,
    "multipliers": [1.12, 1.28, 1.48, 1.72],
    "col_size": 5,
    "row_size": 5
  }
}

6. POST /api/v1/open

FieldKhi nào cóÝ nghĩa
positionluônÔ vừa mở
multipliergem= current_multiplier sau gem này
next_multipliercòn chơiHệ số gem kế
is_minenổtrue → thua, win = 0
is_stoppedhết vánNổ mìn hoặc đã mở hết ô an toàn
mine_positionshết vánLộ toàn bộ mìn để vẽ
win_amount, user_moneyclear boardCredit WIN END. Nổ mìn thì không có win
POST /api/v1/open
{ "position": 6 }

{
  "code": 0,
  "msg": {
    "position": 6,
    "multiplier": 1.28,
    "next_multiplier": 1.48,
    "is_stopped": false,
    "is_mine": false,
    "user_money": 499000
  }
}
{
  "code": 0,
  "msg": {
    "position": 18,
    "is_stopped": true,
    "is_mine": true,
    "mine_positions": [8, 18, 22],
    "user_money": 499000
  }
}
Mở ô đã mở / ngoài bàn / không đang chơi → code 69. Client disable ô trong opened_positions.

7. POST /api/v1/stop

Cashout. Cần đã mở ≥ 1 gem. Credit round(current_multiplier × bet) reason WIN STOP. Lộ mine_positions.

POST /api/v1/stop

{
  "code": 0,
  "msg": {
    "win_amount": 1480,
    "user_money": 500480,
    "mine_positions": [8, 18, 22]
  }
}

8. GET /top-win · GET /history

GET /api/v1/top-win
{ "code": 0, "msg": { "winners": [{ "rank": 1, "user_id": 1001, "username": "alice", "total_win": 880000 }] } }

GET /api/v1/history
{ "code": 0, "msg": { "logs": [{ "user_money": 500480, "win_amount": 1480, "bet": 1000, "multiplier": 1.48, "row_size": 5, "col_size": 5, "created_at": 1720000000 }] } }

9. Luồng ghép

  1. Mở game → GET /gameplay. Render levels, bets, money. Nếu is_playing: vẽ gem theo opened_positions, enable Stop.
  2. User chọn bet + size + số mìn → POST /start. Cập nhật user_money, vẽ lưới trống, hiện next_multiplier.
  3. Tap ô → POST /open {position}. Gem: highlight, cập nhật multiplier. Mine: lộ mine_positions, kết thúc, win = 0.
  4. Stop bất kỳ lúc đã có gem, hoặc đợi clear hết ô an toàn (server tự credit).
  5. Luôn lấy chip từ user_money của response vừa nhận.

10. Error & tiền

Tình huốngHành vi client
HTTP 401Token hết hạn — relogin
code 69GET /gameplay lại, sync state, đừng spam open
code -1 khi startCó thể hết tiền — refresh money
HTTP 400 blockedUser đang PvP — không cho start

11. Checklist client