# Game Mechanics

This page covers the technical details of how WAX Duel games work, including the game state machine and auto-battler system.

***

## Game State Machine <a href="#state-machine" id="state-machine"></a>

This describes the game states and transitions as a game is created, played, and completed.

### Game States

| Status            | Value | Description                             |
| ----------------- | ----- | --------------------------------------- |
| CREATED           | 0     | Game created, awaiting P1 deposit       |
| P1\_DEPOSITED     | 1     | P1 deposited, awaiting P2 deposit       |
| ALL\_DEPOSITED    | 2     | Both deposited, awaiting P2 to join     |
| ALL\_JOINED       | 3     | P2 joined with team, awaiting P1 reveal |
| READY\_TO\_FIGHT  | 4     | P1 revealed, battle ready               |
| (unused)          | 5     | Reserved (legacy)                       |
| P1\_WIN           | 6     | Player 1 won the battle                 |
| P2\_WIN           | 7     | Player 2 won the battle                 |
| P1\_CANCELED      | 8     | P1 canceled before P2 joined            |
| P1\_WIN\_INACTIVE | 9     | P1 won due to P2 timeout                |
| P2\_WIN\_INACTIVE | 10    | P2 won due to P1 timeout                |

### State Flow Diagram

```
                              +-------------+
                              |   CREATED   |
                              |     (0)     |
                              +------+------+
                                     | P1 transfers FIGHT
                                     | (on_transfer)
                              +------v------+
                              | P1_DEPOSITED|<------------+
                              |     (1)     |             |
                              +------+------+             |
                                     | P2 transfers FIGHT |
                                     | (on_transfer)      |
                              +------v------+             |
            +-----------------+ALL_DEPOSITED|             |
            |                 |     (2)     |             |
            |                 +------+------+             |
            |                        | P2 calls joingame  |
            |                        |                    |
            |                 +------v------+             |
            |  +--------------+ ALL_JOINED  |             |
            |  |              |     (3)     |             |
            |  |              +------+------+             |
            |  |                     | P1 calls reveal    |
            |  |                     | (battle executes   |
            |  |                     |  immediately)      |
            |  |              +------v------+             |
            |  |              |   BATTLE    |             |
            |  |              |  COMPLETE   |             |
            |  |              +------+------+             |
            |  |               +-----+-----+              |
            |  |        +------v--+     +--v------+       |
            |  |        | P1_WIN  |     | P2_WIN  |       |
            |  |        |   (6)   |     |   (7)   |       |
            |  |        +---------+     +---------+       |
            |  |                                          |
            |  |    TIMEOUT PATHS                         |
            |  |    (after inactive_min_hours)            |
            |  |                                          |
            |  |  +-----------------+                     |
            |  +->|P2_WIN_INACTIVE  | P1 didn't reveal    |
            |     |      (10)       |                     |
            |     +-----------------+                     |
            |                                             |
            |     +-----------------+                     |
            +---->|P1_WIN_INACTIVE  | P2 didn't join      |
                  |      (9)        |                     |
                  +-----------------+                     |

            CANCEL PATH
            (P1 only, before P2 deposits)

            +-----------------+
            |  P1_CANCELED    |<-- P1 calls cancel
            |      (8)        |
            +-----------------+
```

### State Transitions

#### Normal Game Flow

| From           | To              | Action       | Who Can Call                     |
| -------------- | --------------- | ------------ | -------------------------------- |
| CREATED        | P1\_DEPOSITED   | on\_transfer | P1 (via token transfer)          |
| P1\_DEPOSITED  | ALL\_DEPOSITED  | on\_transfer | P2 (via token transfer)          |
| ALL\_DEPOSITED | ALL\_JOINED     | joingame     | P2                               |
| ALL\_JOINED    | P1\_WIN/P2\_WIN | reveal       | P1 (battle executes immediately) |

#### Alternative Paths

| From                  | To                | Action        | Condition                     |
| --------------------- | ----------------- | ------------- | ----------------------------- |
| CREATED/P1\_DEPOSITED | P1\_CANCELED      | cancel        | P1 cancels before P2 deposits |
| ALL\_DEPOSITED        | P1\_WIN\_INACTIVE | claiminactive | P2 timeout (24h default)      |
| ALL\_JOINED           | P2\_WIN\_INACTIVE | claiminactive | P1 timeout (24h default)      |

### Hash Commitment System

To prevent P2 from seeing P1's team before joining:

1. **Commit Phase:** P1 creates game with `team_hash = SHA256(nonce:asset_id1,asset_id2,...)`
2. **Join Phase:** P2 joins with their visible team
3. **Reveal Phase:** P1 provides nonce and asset\_ids; contract verifies hash matches

This ensures:

* P1 commits to their team before seeing P2's team
* P2 cannot reverse-engineer P1's team from the hash
* P1 cannot change their team after seeing P2's selection

### Nonce-Based Randomness

WAX Duel uses a cryptographic commitment scheme for provably fair randomness, combining nonces from both players:

#### How It Works

1. **P1 Creates Game:** Generates a secret `nonce` and computes `team_hash = SHA256(nonce:asset_ids)`
2. **P2 Joins:** Provides their own `nonce` along with their team (visible immediately)
3. **P1 Reveals:** Provides original nonce and asset\_ids; contract verifies hash matches
4. **Random Value Computed:** `random_value = SHA256(p1_nonce + p2_nonce)`
5. **Battle Executes:** Immediately using the combined randomness

#### Security Properties

* **P1 Commitment:** Cannot change nonce after seeing P2's choice (hash is locked)
* **P2 Independence:** Chooses nonce without knowing P1's actual value
* **Deterministic:** Same nonces always produce same battle outcome
* **Verifiable:** Anyone can verify the hash and randomness computation
* **No External Dependency:** No oracle or external service required

### Timeout Rules

| Scenario           | Timeout                         | Result                         |
| ------------------ | ------------------------------- | ------------------------------ |
| P2 hasn't joined   | 24 hours (inactive\_min\_hours) | P1 can claim via claiminactive |
| P1 hasn't revealed | 24 hours (inactive\_min\_hours) | P2 can claim via claiminactive |

For inactive wins, the winner receives the **full pot** (no house cut).

### Prize Distribution

On normal game completion (P1\_WIN or P2\_WIN):

* **Winner receives:** `total_pot x winner_pct / 10000` (default 80%)
* **House receives:** `total_pot x house_pct / 10000` (default 20%)

On inactive win (P1\_WIN\_INACTIVE or P2\_WIN\_INACTIVE):

* **Winner receives:** Full pot (100%)
* **House receives:** Nothing

***

## Auto Battler <a href="#auto-battler" id="auto-battler"></a>

The auto-battler executes immediately when P1 reveals their team. The combined nonce from both players seeds the random number generator, and the battle runs entirely on-chain with all actions logged to the `gameturns` table.

### Battle Overview

* **Team Size:** 5 characters per player
* **Turn Order:** Players alternate, with first attacker randomly determined
* **Max Rounds:** 100 (prevents infinite loops)
* **Win Condition:** Eliminate all enemy characters

### Stat Resolution

Characters have **tiers** (1=LOW, 2=MEDIUM, 3=HIGH) for each stat. The game mode defines actual values for each tier.

#### Example Stat Resolution

```
Game Mode Configuration:
  attack_values = [1, 2, 4]    // [LOW, MEDIUM, HIGH]
  health_values = [4, 8, 16]   // [LOW, MEDIUM, HIGH]

Character "Ryu":
  attack_tier = 2 (MEDIUM)
  health_tier = 2 (MEDIUM)

Resolved Stats:
  attack = attack_values[2-1] = 2
  health = health_values[2-1] = 8
```

### Stat Effects

| Stat    | Purpose          | Effect                         |
| ------- | ---------------- | ------------------------------ |
| Attack  | Offensive power  | Higher = more damage           |
| Defense | Damage reduction | Higher = less damage taken     |
| Health  | Hit points       | Reduced to 0 = defeated        |
| Speed   | Evasion ability  | Higher = better dodge chance   |
| Range   | Attack reach     | Higher = less distance penalty |
| Combo   | Multi-hit chance | Higher = more likely to combo  |

### Battle Initialization

1. Load both teams from game record
2. Resolve all character stats from tiers using game mode values
3. Set each character's current HP to their health stat
4. Seed random number generator with combined nonce hash: `SHA256(p1_nonce + p2_nonce)`
5. Randomly determine first attacker: `random(2) == 0` -> P1 first, else P2

### Turn Structure

Each round, fighters from both teams alternate attacks. The first attacker is determined randomly at battle start, then turns alternate between players.

#### Turn Order Within a Round

1. First player's Slot 0 fighter attacks a random enemy
2. Second player's Slot 0 fighter attacks a random enemy
3. First player's Slot 1 fighter attacks a random enemy
4. Second player's Slot 1 fighter attacks a random enemy
5. Continue alternating through Slots 2, 3, 4
6. Dead fighters are skipped
7. After all alive fighters have attacked once, the round ends

#### Example Round (if P1 attacks first)

```
Turn 1: P1 Slot 0 → attacks random P2 fighter
Turn 2: P2 Slot 0 → attacks random P1 fighter
Turn 3: P1 Slot 1 → attacks random P2 fighter
Turn 4: P2 Slot 1 → attacks random P1 fighter
... continues until all alive fighters attacked
```

Each round follows this pattern until one team is eliminated or 100 rounds are reached.

#### Attack Sequence

For each attack:

1. Select attacker (next alive character in slot order)
2. Select defender (random alive enemy)
3. Calculate dodge
4. If not dodged: calculate damage -> apply range penalty -> check combo -> apply damage
5. Log turn to `gameturns` table

### Combat Mechanics

#### 1. Dodge Check

The defender may dodge the attack entirely based on speed comparison.

```
speed_ratio = defender_speed / attacker_speed
random_value = random(0, dodge_max_random)

if speed_ratio >= random_value:
    DODGE - No damage dealt, turn ends
```

**Example:**

* Defender speed: 3, Attacker speed: 2 -> ratio = 1.5
* dodge\_max\_random: 10 -> random 0-10
* If random rolls 1, dodge succeeds (1.5 >= 1)
* If random rolls 5, dodge fails (1.5 < 5)

Higher speed ratio = higher dodge probability.

#### 2. Damage Calculation

Base damage is calculated from attack vs defense.

```
attack_power = attacker_attack / defender_defense
if attack_power < 1.0:
    attack_power = 1.0

base_damage = attack_power x attacker_attack
```

**Example:**

* Attacker: 4 attack, Defender: 2 defense
* attack\_power = 4 / 2 = 2.0
* base\_damage = 2.0 x 4 = 8

#### 3. Range Penalty

Distance between attacker and defender slots affects damage.

```
distance = |attacker_slot - defender_slot|
extra_range = distance - attacker_range

if extra_range > 0:
    penalty_index = min(extra_range - 1, 2)
    damage = damage x range_factors[penalty_index]
```

**Default range\_factors:** `[0.75, 0.5, 0.25]`

| Extra Range  | Damage Multiplier |
| ------------ | ----------------- |
| 0 (in range) | 100%              |
| 1            | 75%               |
| 2            | 50%               |
| 3+           | 25%               |

**Example:**

* Attacker in slot 0, Defender in slot 4, Attacker range: 1
* distance = |0 - 4| = 4
* extra\_range = 4 - 1 = 3
* penalty\_index = min(3-1, 2) = 2
* damage = damage x 0.25

#### 4. Combo Check

Attacker may land extra hits for bonus damage.

```
random_value = random(0, combo_max_random)

if attacker_combo >= random_value:
    COMBO - damage = damage x combo_factor
```

**Default combo\_factor:** `1.75`

**Example:**

* Attacker combo: 3, combo\_max\_random: 10
* If random rolls 2, combo succeeds (3 >= 2)
* damage = 8 x 1.75 = 14

#### 5. Apply Damage

```
final_damage = max(1, calculated_damage)  // Minimum 1 damage
defender_hp = defender_hp - final_damage

if defender_hp <= 0:
    defender_hp = 0
    defender is eliminated
```

### Winner Determination

Battle ends when:

* One team has no alive characters, OR
* Maximum rounds (100) reached

#### Win Priority

```
p1_alive = count of P1's alive characters
p2_alive = count of P2's alive characters

if p1_alive > 0 and p2_alive == 0:
    P1 WINS
else if p2_alive > 0 and p1_alive == 0:
    P2 WINS
else if p1_alive > p2_alive:
    P1 WINS (more survivors)
else if p2_alive > p1_alive:
    P2 WINS (more survivors)
else:
    P1 WINS (tiebreaker: first-mover advantage)
```

### Turn Logging

Every attack is recorded in the `gameturns` table:

| Field               | Description                       |
| ------------------- | --------------------------------- |
| round               | Current round number (1-100)      |
| attacker            | Player 1 or 2                     |
| attacker\_slot      | Character position (0-4)          |
| attacker\_asset\_id | NFT ID of attacker                |
| attacker\_char\_id  | Character ID                      |
| defender\_slot      | Target position                   |
| defender\_asset\_id | Target NFT                        |
| defender\_char\_id  | Target character                  |
| defender\_hp\_start | HP before this attack             |
| random\_dodge       | Random value used for dodge check |
| was\_dodge          | Did defender dodge?               |
| random\_combo       | Random value for combo check      |
| was\_combo          | Did attacker combo?               |
| damage\_dealt       | Final damage (0 if dodged)        |
| defender\_hp\_end   | HP after damage                   |
| was\_kill           | Did defender die?                 |
| range\_penalty      | Distance penalty applied          |

### Stat Updates

After battle completion:

#### Player Stats

* Increment `duels_entered` for both players
* Increment `duels_won` for winner, `duels_lost` for loser
* Add bet amounts to `fight_spent`
* Add prize to winner's `fight_won`
* Update `last_duel` timestamp

#### NFT Stats (per character)

* **kill\_count:** +1 for each enemy eliminated
* **death\_count:** +1 if character was eliminated
* **damage\_dealt:** Sum of all damage this character dealt
* **damage\_received:** Sum of all damage this character took
* **dodge\_count:** +1 for each successful dodge
* **combo\_count:** +1 for each successful combo
* **cooldown\_until:** Set to current\_time + cooldown\_secs (unless golden)

### Cooldown System

After battle, NFTs enter cooldown:

* **Standard NFTs:** 24 hours (configurable via `cooldown_secs`)
* **Golden NFTs:** No cooldown (can play again immediately)

NFTs on cooldown cannot be used in new games.

### Game Mode Configuration

Battle mechanics are configurable per game mode:

| Setting            | Default            | Description                 |
| ------------------ | ------------------ | --------------------------- |
| dodge\_max\_random | 10                 | Max random for dodge (0-10) |
| combo\_max\_random | 10                 | Max random for combo (0-10) |
| range\_factors     | \[0.75, 0.5, 0.25] | Distance damage penalties   |
| combo\_factor      | 1.75               | Combo damage multiplier     |
| attack\_values     | \[1, 2, 4]         | Attack by tier              |
| defense\_values    | \[1, 2, 4]         | Defense by tier             |
| health\_values     | \[4, 8, 16]        | Health by tier              |
| speed\_values      | \[1, 2, 4]         | Speed by tier               |
| range\_values      | \[1, 2, 4]         | Range by tier               |
| combo\_values      | \[1, 2, 4]         | Combo by tier               |


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://duel-2.gitbook.io/duel-white-paper/reference/game-mechanics.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
