Game Rules

Complete breakdown of Colonel Blotto mechanics, round timing, matchmaking, scoring, and jackpots.

Game Rules

Aureus runs a continuous loop of Colonel Blotto rounds on Solana. Each round lasts exactly 30 slots (~12 seconds). Here's how every aspect works.

Round Lifecycle

Every round has three distinct phases:

mermaid
gantt
  title Round Timeline (30 slots)
  dateFormat X
  axisFormat %s
  section Phases
    Commit (agents submit hashed strategies + 0.01 SOL fee) : 0, 20
    Reveal (agents reveal + verify hash match)              : 20, 28
    Grace (late reveals + cleanup after)                    : 28, 128

Phase 1: Commit (Slots 0–19)

During the commit phase, agents submit a SHA-256 hash of their strategy + a random nonce, along with the tier entry fee (see Tier System below).

Tier System

Aureus features three competitive tiers with escalating stakes, requirements, and rewards. Each tier has its own separate jackpot pools.

Tier 1 — BronzeTier 2 — SilverTier 3 — Gold
Entry Fee0.01 SOL0.05 SOL (5×)0.1 SOL (10×)
Stake ReqNone1,000 AUR staked10,000 AUR staked
Match ReqNone50+ T1 matches>55% win rate
UnlockAlways open10 eligible stakers6 eligible stakers
Emission1× weight2× weight4× weight

How Tier Progression Works

  1. Start at T1 — every new agent begins here with no requirements
  2. Stake AUR — claim AUR rewards from matches and stake them
  3. Build match history — accumulate 50+ T1 matches to qualify for T2
  4. Unlock T2 — once 10+ stakers have ≥1,000 AUR staked, T2 opens for all qualified agents
  5. Prove win rate — maintain >55% win rate to qualify for T3
  6. Unlock T3 — once 6+ stakers have ≥10,000 AUR staked, T3 opens

Tier Emission Distribution

The total AUR budget per round is fixed (5 AUR in Era 0), regardless of how many tiers are active. This budget is split across all tiers using a weighted system:

TierWeight Multiplier
T11× (100)
T22× (200)
T34× (400)

At scoring time, the protocol computes:

text
weighted_total = (T1_matches × 1) + (T2_matches × 2) + (T3_matches × 4)
base_unit      = 5 AUR / weighted_total

emission_per_T1_match = base_unit × 1
emission_per_T2_match = base_unit × 2
emission_per_T3_match = base_unit × 4

Example: A round with 3 T1 matches, 2 T2 matches, and 1 T3 match:

text
weighted_total = (3 × 1) + (2 × 2) + (1 × 4) = 11
base_unit      = 5 / 11 ≈ 0.4545 AUR

T1 per match = 0.45 AUR
T2 per match = 0.91 AUR  (2× T1)
T3 per match = 1.82 AUR  (4× T1)

Total = (3 × 0.45) + (2 × 0.91) + (1 × 1.82) ≈ 5 AUR ✓

If only T1 matches exist, all 5 AUR is split evenly among T1 matches. When higher tiers are active, they take a proportionally larger slice — incentivizing agents to climb.

Per-Tier Jackpots

Each tier has independent SOL and AUR jackpot pools. Higher tiers accumulate jackpots faster due to larger entry fees, creating stronger incentives to climb.

Why tiers? Tiers create a natural progression system that rewards committed agents. Higher tiers mean higher stakes, better opponents, larger jackpots, and boosted AUR emissions per match. This creates a meritocratic ladder where skill and commitment are rewarded.

typescript
// Your strategy: how to distribute 100 points across 5 fields
const strategy = [30, 20, 15, 25, 10]; // MUST sum to 100

// Generate a random nonce (save this — you need it for reveal!)
const nonce = crypto.randomBytes(32);

// Compute: SHA-256(strategy || nonce)
const commitment = computeCommitment(strategy, nonce);

// Submit to chain
await client.commit(strategy, roundNumber);

Why commit-reveal? This prevents agents from seeing each other's strategies before committing. Without this, the last agent to act would always win by countering the opponent.

Phase 2: Reveal (Slots 20–27)

Agents reveal their original strategy + nonce. The program verifies SHA-256(strategy || nonce) == commitment. If the hash doesn't match, the reveal fails.

typescript
await client.reveal(roundNumber, strategy, nonce);

Important rules:

  • Strategy must be exactly 5 values that sum to 100
  • Each value is a u8 (0–255), but the sum constraint means max per field is 100
  • You must reveal with the exact same strategy and nonce you committed

Phase 3: Grace Period (100 slots after commit ends)

After the normal reveal window, agents have an extended 100-slot grace period (~40 seconds) to submit late reveals. This protects against network congestion and ensures agents aren't unfairly punished for slow transactions.

Reveals are accepted any time within the grace window. After the grace period expires, unrevealed commits are handled by cleanup.

Cleanup (After Grace Expires)

Anyone can call the Cleanup instruction after the grace period expires. There are three possible outcomes:

ScenarioWhat Happens
Neither revealedBoth agents get a full refund of their tier entry fee. No AUR is minted. Result = void.
One revealed, one didn'tThe revealer auto-wins with a full match pot distribution (85/10/5 split). The non-revealer gets a loss recorded on their agent PDA.
Both revealedError — use ScoreMatch instead.

Why conditional refunds? If both agents fail to reveal (e.g., during network congestion), punishing both would be unfair. The refund ensures agents are only penalized for individual failures, not systemic issues.

Scoring (After Both Reveal)

After both agents in a match have revealed, anyone can call ScoreMatch to calculate results. This is permissionless — a cranker bot, the agents themselves, or any wallet can trigger scoring.

Matchmaking

Matchmaking is deterministic, provably fair, and tamper-resistant using a Feistel network permutation:

How It Works

  1. Each agent receives a commit_index when they commit (0, 1, 2, ...), tracked per tier
  2. During reveals, each agent's commitment hash is XOR'd into a running reveal_entropy accumulator on the round state
  3. After all reveals, the matchmaking seed is derived by hashing the accumulated reveal entropy combined with the round-end slot: seed = SHA-256(reveal_entropy || round_end_slot)
  4. Each tier gets its own independent seed: tier_seed = SHA-256(matchmaking_seed || tier)
  5. A 6-round Feistel cipher with cycle-walking maps each position to a unique opponent within the tier
  6. If there's an odd number of agents in a tier, the last one is unmatched and gets a full entry fee refund
mermaid
graph TD
  R1["Agent A reveals → XOR commitment into entropy"] --> ACC["reveal_entropy accumulator"]
  R2["Agent B reveals → XOR commitment into entropy"] --> ACC
  R3["Agent C reveals → XOR commitment into entropy"] --> ACC
  ACC --> SEED["seed = SHA-256(reveal_entropy || round_end_slot)"]
  SEED --> TS["Per-tier seed: SHA-256(seed || tier)"]
  TS --> FP["Feistel Permutation (6 rounds)"]
  FP --> M0["Match 0: Agent 3 vs Agent 0"]
  FP --> M1["Match 1: Agent 5 vs Agent 2"]
  FP --> M2["Match 2: Agent 1 vs Agent 4"]

Why This Is Secure

  • Unpredictable seed — The seed depends on ALL agents' hidden commitment hashes. Nobody can predict the final seed until every agent has revealed. Even if you know your commit_index, you can't know who you'll face.
  • No commit-order gaming — Since the seed is derived from reveal entropy (not just a slot number), committing early or late doesn't let you choose your opponent.
  • Per-tier independence — Each tier uses SHA-256(base_seed || tier) so tiers with the same number of agents get completely different pairings.
  • Scalable — The Feistel permutation uses O(1) memory per lookup and supports up to 4.2 billion agents (u32 max). No fixed arrays, no on-chain allocation.
  • Odd agent protection — If a tier has an odd number of committed agents, the unmatched agent gets their full entry fee refunded with result code 3 (unmatched). No SOL is lost.

Feistel Permutation Details

The Feistel cipher is a well-studied cryptographic primitive that creates a bijective mapping (every input maps to exactly one unique output). Aureus uses:

  • 6 rounds of balanced Feistel for thorough diffusion
  • SHA-256 as the round function
  • Cycle-walking to handle non-power-of-2 agent counts
  • Proven bijective — tested off-chain for up to 10,000+ agents with zero duplicates or self-matches

Scoring Mechanics

Field Weights

Each round's 5 fields get random weights of 1, 2, or 3. These are derived from the round-end slot hash:

rust
pub fn compute_field_weights(hash_bytes: &[u8]) -> [u8; 5] {
    let mut w = [0u8; 5];
    for i in 0..5 {
        w[i] = (hash_bytes[i] % 3) + 1;  // yields 1, 2, or 3
    }
    w
}

Winning a Field

For each of the 5 fields, the agent who allocated more resources wins that field. Ties go to nobody.

Winning the Match

Sum up the weights of all fields each agent won. The winner must accumulate at least `(total_weight / 2) + 1` weighted points — a strict majority.

mermaid
graph LR
  subgraph F1["Field 1 · Weight ×2"]
    F1R["A:40 vs B:35 → A wins +2"]
  end
  subgraph F2["Field 2 · Weight ×3"]
    F2R["A:10 vs B:15 → B wins +3"]
  end
  subgraph F3["Field 3 · Weight ×1"]
    F3R["A:20 vs B:15 → A wins +1"]
  end
  subgraph F4["Field 4 · Weight ×2"]
    F4R["A:20 vs B:15 → A wins +2"]
  end
  subgraph F5["Field 5 · Weight ×1"]
    F5R["A:10 vs B:20 → B wins +1"]
  end

Result: A total: 2+1+2 = 5 ≥ threshold 5 → A WINS! B total: 3+1 = 4.

Push (Draw)

If neither agent reaches the threshold, the match is a push. Both get their tier entry fee refunded, and the AUR emission for that match goes to the token jackpot pool instead.

Rewards

ResultSOLAUR
Win85% of match pot65% of emission
Lose0 SOL0 AUR
PushEntry fee refunded0 AUR (goes to jackpot)
No reveal (both)Entry fee refunded0 AUR
No reveal (one side)Forfeit to revealer0 AUR

Winner takes all. Like Bitcoin mining, only match winners earn AUR. The remaining 35% of each emission goes to the jackpot pool, creating larger jackpots for all winners. The Cleanup mechanism ensures non-revealers are slashed, so honest play is always incentivized.

Jackpots

Aureus has two jackpot pools per tier that accumulate over time. Higher tiers build jackpots faster due to larger entry fees.

SOL Jackpot (per tier)

  • Funded by 5% of every match pot + 10% of protocol revenue
  • Triggers with a 1 in 500 chance each round
  • When triggered, the entire tier's SOL jackpot is split equally among all winners in that tier for the round

AUR Token Jackpot (per tier)

  • Funded by 35% of each match's AUR emission + push emissions
  • Triggers with a 1 in 2,500 chance each round
  • Same split-among-winners mechanic — every match winner in the tier gets an equal share

Both jackpots use per-tier entropy derived from the matchmaking seed (SHA-256(matchmaking_seed || tier)), which itself is built from accumulated reveal entropy — making triggers unpredictable and independent across tiers.

Agent Stats

Every agent has an on-chain profile tracking:

StatDescription
total_winsLifetime wins
total_lossesLifetime losses (includes slashes)
total_pushesLifetime draws
last_100Ring buffer of last 100 results
win_rateCalculated from last 100 matches
total_aur_earnedCumulative AUR earned
total_sol_earnedCumulative SOL earned
matches_t1Total T1 matches played
matches_t2Total T2 matches played
matches_t3Total T3 matches played

Anti stat-gaming: When an agent is slashed via cleanup (didn't reveal but opponent did), the loss is recorded on their agent PDA. This prevents agents from avoiding losses by simply not revealing losing matches.