This document is the authoritative reference for all arena game rules. It is consumed by the Combat Service, Game Service, and Arena UI.
- Match Lifecycle
- Grid
- Player State
- Actions
- Stamp-Sealed Actions
- Combat Resolution
- Resource Nodes
- Scoring
- Match End Conditions
- Rewards
PENDING → 2–6 players joining (30 s lobby timeout)
ACTIVE → Match running (3–5 min, server-authoritative tick)
FINISHED → Winner determined, rewards granted
- A match starts automatically when 6 players join, or after the lobby timeout (30 s) if at least 2 players are present.
- If fewer than 2 players join within 30 s, the match is cancelled and players are returned to matchmaking.
- Match duration is capped at 5 minutes. If time expires, the highest-score player wins.
- Size: 10 × 10 tiles
- Coordinate system: (0,0) is top-left; x increases east, y increases south
- Spawn positions: Players spawn at fixed corners and mid-edges, spaced as far apart as possible
| Players | Spawn positions |
|---|---|
| 2 | (0,0) · (9,9) |
| 3 | (0,0) · (9,0) · (4,9) |
| 4 | (0,0) · (9,0) · (0,9) · (9,9) |
| 5 | (0,0) · (9,0) · (0,9) · (9,9) · (4,4) |
| 6 | (0,0) · (9,0) · (0,9) · (9,9) · (0,4) · (9,4) |
| Type | Symbol | Description |
|---|---|---|
empty |
· | Passable, no special effect |
resource_node |
R | Contains collectible resources; depleted after collection |
wall |
# | Impassable; blocks movement and line-of-sight |
. . . . R . . . . .
. # # . . . . # # .
. # . . . . . . # .
. . . . R . . . . .
R . . R . . R . . R
. . . . R . . . . .
. . . . . . . . . .
. # . . . . . . # .
. # # . . . . # # .
. . . . R . . . . .
Additional maps may be introduced post-v1.
| Attribute | Initial | Notes |
|---|---|---|
hp |
100 | Integer. Eliminated when ≤ 0. |
score |
0 | Increases via kills, resource collection |
resources |
0 | Currency units accumulated in-match |
position |
spawn tile | Updated by move action |
shields |
0 | Temporary damage reduction, granted by defend action |
alive |
true | Set to false on elimination |
Every action is submitted via POST /matches/:matchId/actions (REST) or the action WebSocket event. All actions require an X-Idempotency-Key.
The server processes actions in tick order (100 ms ticks). Within a tick, actions are resolved in receipt order.
Move one tile in a cardinal direction.
| Rule | Detail |
|---|---|
| Distance | 1 tile per action |
| Directions | north · south · east · west |
| Blocked by | walls, grid boundary, another player's tile |
| Cooldown | None — one move per tick allowed |
Attack a player on an adjacent tile (including diagonals).
| Rule | Detail |
|---|---|
| Range | Chebyshev distance ≤ 1 (adjacent 8 tiles) |
| Base damage | 20 pts |
| Weapon bonus | +0 to +20 pts depending on equipped weapon |
| Critical hit | 10% chance — double damage |
| Blocked by | Walls between attacker and target break line-of-sight (cardinal only) |
| Cooldown | 1 tick (1 action per tick) |
| Target self | Not allowed |
payload: { "targetId": "uuid" }
Enter defensive stance for the current tick.
| Rule | Detail |
|---|---|
| Effect | Grants 20 shield points for 1 tick |
| Stacking | Does not stack — re-using removes old shield |
| Duration | Until the next tick begins |
| Cooldown | None |
payload: {} (no extra payload)
Collect resources from the current tile if it is a resource_node.
| Rule | Detail |
|---|---|
| Requirement | Player must be standing on a resource_node tile |
| Yield | 50–150 resource units (uniform random) |
| Depletion | Tile becomes empty after collection |
| Cooldown | 1 tick |
payload: {} (no extra payload)
Any action can be sealed with an idempo Stamp by setting useStamp: true in the request.
What sealing does:
| Guarantee | Mechanism |
|---|---|
| Exactly-once resolution | stampId stored as action_id with UNIQUE constraint |
| Duplicate immunity | Second request with same X-Idempotency-Key returns original response — action not re-applied |
| Irreversibility | Sealed actions cannot be cancelled or overridden — the commitment is the cost |
What sealing does NOT do:
- It does not increase damage or give any in-game advantage beyond the exactly-once guarantee
- It does not prevent the action from being invalid (e.g., attacking an out-of-range target still returns 409)
Stamp deduction rules:
- One Stamp is deducted atomically with the action insert — in the same DB transaction
- If the action is rejected as a duplicate (idempotency hit), the Stamp is not deducted again
- If the action is invalid (e.g., 409), the Stamp is not deducted (validation runs before deduction)
- If the player has
stamp_balance = 0and requestsuseStamp: true, the request returns 402 immediately
When an attack action is processed:
1. Validate attacker is alive
2. Validate target is alive
3. Validate range (Chebyshev distance ≤ 1)
4. Check line-of-sight (walls block cardinal directions only)
5. Roll critical hit (10% chance)
6. Calculate damage:
damage = base_damage (20)
+ weapon_bonus (0–20)
× (2 if critical, else 1)
7. Apply shield reduction:
effective_damage = max(0, damage - target.shields)
8. Deduct HP:
target.hp -= effective_damage
9. If target.hp ≤ 0:
Eliminate target (see §3.2)
10. Emit PlayerAttackedEvent
11. If eliminated: emit PlayerEliminatedEvent
When a player is eliminated:
aliveset tofalse- They drop 50% of their accumulated
resourceson their current tile (rounded down) - Eliminated player is removed from the grid
- Remaining players may
collectfrom their death tile (which becomes a temporary resource node) - Score awarded to the eliminating player: +100 pts
- 6 resource nodes placed symmetrically (see map layout in §2.2)
- Each node holds 50–150 resource units (randomised on match start, same seed for all clients)
- After collection the tile becomes
empty— nodes do not respawn during v1 - Resources accumulated in-match are converted to wallet currency at match end (1 resource = 1 currency unit)
| Event | Points awarded |
|---|---|
| Dealing damage | +1 pt per damage point dealt |
| Eliminating a player | +100 pts |
| Collecting a resource node | +50 pts |
| Surviving to match end | +25 pts |
Score is the projection used by the Leaderboard Service (CQRS read model, updated via ScoreUpdatedEvent).
The match ends when:
- Only one player remains alive — that player wins immediately, or
- The 5-minute time limit expires — highest-score player wins
In the event of a score tie at time limit, the player with more remaining HP wins. If still tied, the match is declared a draw and both players receive winner rewards.
Rewards are calculated by the Reward Service after MatchFinishedEvent is received. All grants are idempotent — redelivery of the event does not double-grant.
| Outcome | Currency | Items | Stamps |
|---|---|---|---|
| Winner | 500 | 1 random item (common–rare) | 3 |
| Survivor (not winner) | 200 + resources collected | — | 1 |
| Eliminated before end | resources collected at time of death | — | 0 |
| Rarity | Probability | Example |
|---|---|---|
| Common | 60% | iron_sword (+5 weapon bonus) |
| Uncommon | 30% | steel_sword (+10 weapon bonus) |
| Rare | 10% | rare_sword_01 (+20 weapon bonus) |
For HTTP and WebSocket contracts used to submit actions, see API.md. For idempotency implementation details, see SPEC.md §5.