Skip to main content

Practice Arena System - Current Implementation Summary

✅ Your Requirements (ALL IMPLEMENTED)

1. ✅ Remove Seat Limits for Practice Arenas (NOT Tournaments)

Status: FULLY IMPLEMENTED

  • Practice Arenas: NO seat limits - unlimited players can join
  • Tournaments: KEEP seat limits (unchanged - still use totalSeats field)

The practice arena schema has been completely transformed:

  • Removed fields: totalSeats, numberOfPlayers, entryFee, winner, tournament, startAt, endedAt
  • Added fields: currentWeekStartDate, currentWeekEndDate, isWeeklyCycle

2. ✅ Winner Declared on Weekend

Status: FULLY IMPLEMENTED

  • Winners are declared at the end of each week (Sunday 23:59:59)
  • Automated cron job runs every hour to check if week has ended
  • When Sunday ends, the system:
    1. Finds the winner (lowest non-zero score)
    2. Marks winner in weekly leaderboard
    3. Creates reward distribution entry
    4. Sends winner notification

3. ✅ Arena Resets After Weekend for New Week

Status: FULLY IMPLEMENTED

  • After Sunday night winner declaration, arena automatically resets
  • New week cycle starts Monday 00:00:00
  • Previous week's leaderboard is preserved for history
  • New leaderboard created for the new week

System Architecture

Weekly Cycle Flow

Monday 00:00:00 ──────────────────────────> Sunday 23:59:59
│ │
│ Players join (unlimited) │
│ Players submit scores (unlimited) │
│ Leaderboard updates in real-time │
│ │
└──────────────────────────────────────────┘

Cron Job Triggers

┌──────┴──────┐
│ Find Winner │
│ (Lowest > 0)│
└──────┬──────┘

┌──────┴──────────┐
│ Create Reward │
│ (Pending Admin) │
└──────┬──────────┘

┌──────┴──────┐
│ Reset Arena │
│ (New Week) │
└─────────────┘

Database Schema

PracticeArena Model

{
game: ObjectId, // Reference to Game
status: 'ACTIVE' | 'INACTIVE',
rules: string,
name: string,
currentWeekStartDate: Date, // Monday 00:00:00
currentWeekEndDate: Date, // Sunday 23:59:59
isWeeklyCycle: boolean // Always true
}

Key Differences from Tournament:

  • ❌ NO totalSeats field
  • ❌ NO entryFee field
  • ❌ NO numberOfPlayers field
  • ❌ NO winner field (winners stored in ArenaWeeklyLeaderboard)
  • ✅ Weekly cycle dates instead
  • ✅ Status is ACTIVE/INACTIVE (not OPEN/IN_PROGRESS/OVER)

Tournament Model (UNCHANGED - Still Has Seat Limits)

{
game: ObjectId,
product: ObjectId,
seller: string,
status: 'OPEN' | 'IN_PROGRESS' | 'OVER' | 'UNFILLED',
entryFee: number, // ✅ Tournaments still have fees
totalSeats: number, // ✅ Tournaments still have seat limits
numberOfPlayers: number, // ✅ Tournaments track player count
expectedPlayers: number,
winner: ObjectId,
// ... other tournament-specific fields
}

Cron Job Configuration

File: src/cron/arenas.cron.ts

Schedule: Runs every 1 hour (60 minutes)

  • Checks if any arena's currentWeekEndDate has passed
  • Processes all ended weeks in a single run

Startup Delay: 30 seconds after server start

setTimeout(runWeeklyArenas, 30_000)     // Initial run after 30s
setInterval(runWeeklyArenas, 60*60*1000) // Then every hour

Why hourly?

  • Catches ended weeks promptly (within 1 hour of Sunday midnight)
  • Low overhead (quick query, only processes ended weeks)
  • Reliable and simple

Winner Selection Logic

Criteria (in order):

  1. Lowest score > 0 (zero scores excluded as invalid)
  2. First submission time (tiebreaker via updatedAt)
const winner = await ArenaWeeklyLeaderboard.findOne({
arena: arena._id,
weekStartDate: arena.currentWeekStartDate,
weekEndDate: arena.currentWeekEndDate,
score: { $gt: 0 } // Exclude zero scores
})
.sort({ score: 1, updatedAt: 1 }) // Lowest score, earliest submission

API Endpoints

Public Endpoints (No Auth)

  • GET /api/v1/practice-arenas - List all active arenas
  • GET /api/v1/practice-arenas?game=:gameId - Get arena by game
  • GET /api/v1/practice-arenas/:id - Get specific arena
  • GET /api/v1/practice-arenas/:id/leaderboard - Get current week leaderboard

Authenticated Endpoints

  • POST /api/v1/practice-arenas/:id/join - Join arena (unlimited times)
  • POST /api/v1/practice-arenas/:id/score - Submit score (unlimited times)
  • GET /api/v1/practice-arenas/played - Get user's played arenas

Admin Endpoints

  • POST /api/admin/practice-arenas - Create new arena
  • GET /api/admin/practice-arenas - List all arenas
  • PUT /api/admin/practice-arenas/:id - Update arena
  • GET /api/admin/arena-rewards - List reward distributions
  • PUT /api/admin/arena-rewards/:id/approve - Approve reward
  • PUT /api/admin/arena-rewards/:id/reject - Reject reward
  • GET /api/admin/arena-rewards/config - Manage reward configs

Key Features

No Seat Limits - Unlimited players can join practice arenas ✅ No Entry Fees - Completely free to play ✅ Weekly Cycles - Monday to Sunday automatic cycles ✅ Weekend Winners - Declared at end of week (Sunday night) ✅ Auto Reset - Arena resets Monday morning for new week ✅ Historical Data - All past weeks preserved ✅ Real-time Leaderboard - Updates as scores submitted ✅ Fraud Prevention - Zero scores excluded ✅ Reward System - Country-specific rewards with admin approval ✅ Unlimited Plays - Join and submit scores unlimited times per week


Comparison: Practice Arena vs Tournament

FeaturePractice ArenaTournament
Seat Limits❌ NO (Unlimited)✅ YES (totalSeats)
Entry Fee❌ NO (Free)✅ YES (entryFee in points)
DurationWeekly cycle (Mon-Sun)Custom dates
Winner DeclarationEnd of week (Sunday)When seats fill
Auto Reset✅ YES (Every Monday)❌ NO (One-time)
Join LimitUnlimitedLimited by seats
Score LimitUnlimited submissionsUnlimited submissions
RewardsAdmin approval requiredAuto-distributed
StatusACTIVE/INACTIVEOPEN/IN_PROGRESS/OVER
Historical DataAll weeks preservedSingle instance

Current Status

✅ All Requirements Met

  1. Seat limits removed - Practice arenas have unlimited capacity
  2. Tournaments unchanged - Still use seat-based system
  3. Weekend winner declaration - Happens Sunday night via cron
  4. Weekly reset - Arena resets Monday morning automatically

No Code Changes Needed

The system already implements everything you requested! The only issue was outdated documentation, which has now been updated to reflect the current implementation.

Documentation Updated

  • docs/practice-arenas-api.md - Complete rewrite
  • docs/PRACTICE_ARENA_SUMMARY.md - This summary document
  • ARENA_REWARDS_IMPLEMENTATION.md - Already existed (detailed implementation notes)

Testing the System

1. Create a Practice Arena (Admin)

POST /api/admin/practice-arenas
{
"game": "<game_id>",
"name": "Weekly Challenge",
"rules": "Best score wins!"
}

2. Join Arena (User)

POST /api/v1/practice-arenas/:id/join
# Returns: { "ok": true, "arena": { "_id": "..." } }
# Can join unlimited times

3. Submit Scores (User)

POST /api/v1/practice-arenas/:id/score
{ "score": 150 }
# Can submit unlimited times
# Only best (lowest) score kept

4. Check Leaderboard

GET /api/v1/practice-arenas/:id/leaderboard
# See current week rankings

5. Wait for Sunday Night

  • Cron job runs automatically
  • Winner declared (lowest score > 0)
  • Arena resets for Monday

6. Approve Rewards (Admin)

GET /api/admin/arena-rewards
# See pending rewards

PUT /api/admin/arena-rewards/:id/approve
# Approve and distribute points

Monitoring & Logs

Check job logs to verify cron execution:

JobLog.find({ job: 'ARENA_WEEKLY_CYCLE' })
.sort({ runAt: -1 })
.limit(10)

Successful run meta:

{
"action": "processWeeklyArenas",
"total": 5,
"successes": [
{
"arenaId": "...",
"winner": "user123",
"score": 150,
"country": "US",
"rewardAmount": 100,
"ledgerType": "TOPUP"
}
],
"failures": []
}

Summary

Your practice arena system is fully operational with:

  1. Unlimited seats (no player cap)
  2. Free to play (no entry fees)
  3. Weekly cycles (Monday-Sunday)
  4. Weekend winner declaration (Sunday night)
  5. Automatic weekly reset (Monday morning)
  6. Tournaments unaffected (still use seat limits)

No code changes required - system already implements all your requirements!