dubs-server 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +280 -0
- package/CLAUDE.md +46 -0
- package/CONNECT4_PRODUCTION_DEPLOY.md +155 -0
- package/CURRENT_SESSION.md +171 -0
- package/CURRENT_SESSION_DRAW.md +516 -0
- package/MARCH_MADNESS_SURVIVOR.md +254 -0
- package/PANDA.md +166 -0
- package/Procfile +4 -0
- package/README.md +476 -0
- package/controllers/livescoresController.js +376 -0
- package/controllers/pickemController.js +554 -0
- package/controllers/survivorAdminController.js +887 -0
- package/controllers/survivorController.js +623 -0
- package/cron/oracleMonitor.js +77 -0
- package/cron/pickemOracleMonitor.js +73 -0
- package/data/jackpot-history.json +952 -0
- package/data/ncaaTeams.js +406 -0
- package/documentation/API_SECURITY_GUIDE.md +327 -0
- package/documentation/ARCADE_API.md +593 -0
- package/documentation/ARCADE_IMPLEMENTATION_SUMMARY.md +399 -0
- package/documentation/ARCADE_QUICKSTART.md +242 -0
- package/documentation/AUTOMATIC_MODE_ORACLE.md +321 -0
- package/documentation/BUG_FIX_COHORT_DATE_DISPLAY.md +171 -0
- package/documentation/CLAIM_MIGRATION_INSTRUCTIONS.md +52 -0
- package/documentation/CLAIM_STATUS_FIX.md +67 -0
- package/documentation/CLI_TOOL_GUIDE.md +372 -0
- package/documentation/COHORT_RETENTION_ANALYSIS.md +295 -0
- package/documentation/COHORT_RETENTION_IMPLEMENTATION_COMPLETE.md +461 -0
- package/documentation/COHORT_RETENTION_SUMMARY.md +204 -0
- package/documentation/COMPLETE_PROJECT_SUMMARY.md +490 -0
- package/documentation/DATABASE_QUERIES.md +269 -0
- package/documentation/DATABASE_RETENTION_POLICY.md +390 -0
- package/documentation/DATABASE_SETUP_GUIDE.md +361 -0
- package/documentation/DATABASE_SETUP_SUMMARY.md +247 -0
- package/documentation/DEMO_API_CURL_COMMANDS.md +656 -0
- package/documentation/DEPLOYMENT_SUMMARY.txt +100 -0
- package/documentation/DUPLICATE_NOTIFICATIONS_FIXED.md +201 -0
- package/documentation/EXCHANGE_RATES_INTEGRATION.md +371 -0
- package/documentation/FINAL_API_PROTECTION_TABLE.md +175 -0
- package/documentation/GAME_START_NOTIFICATIONS_DEPLOYMENT.md +256 -0
- package/documentation/GAME_START_NOTIFICATIONS_INTEGRATION.md +275 -0
- package/documentation/HEROKU_DEPLOYMENT.md +134 -0
- package/documentation/HEROKU_SCHEDULER_SETUP.md +271 -0
- package/documentation/JACKPOT_API.md +521 -0
- package/documentation/JACKPOT_DEPLOYMENT_GUIDE.md +362 -0
- package/documentation/JWT_IMPLEMENTATION_SUMMARY.md +373 -0
- package/documentation/JWT_QUICK_SETUP.md +268 -0
- package/documentation/JWT_TESTING_GUIDE.md +404 -0
- package/documentation/KEEPER_RECOVERY_GUIDE.md +381 -0
- package/documentation/KEEPER_SETUP.md +206 -0
- package/documentation/KEEPER_STATE_MACHINE.md +423 -0
- package/documentation/LATEST_PRODUCTION_SETUP.md +387 -0
- package/documentation/LOCAL_VOTING_TEST.md +279 -0
- package/documentation/ORACLE_FIXES_SUMMARY.md +188 -0
- package/documentation/ORACLE_POSTGRESQL_UPDATE.md +202 -0
- package/documentation/PAYMENT_DEPLOYMENT.md +209 -0
- package/documentation/PNL_TRACKING_SETUP.md +189 -0
- package/documentation/PREVENTING_LOCKUP_ERRORS.md +472 -0
- package/documentation/PRODUCTION_READY_SUMMARY.md +227 -0
- package/documentation/PUBLIC_VS_PRIVATE_ENDPOINTS.md +278 -0
- package/documentation/QUICK_AUTH_SETUP.md +99 -0
- package/documentation/QUICK_DEPLOY.md +224 -0
- package/documentation/QUICK_FIX.md +114 -0
- package/documentation/QUICK_START.md +152 -0
- package/documentation/REFEREE_MODE_GUIDE.md +392 -0
- package/documentation/RETENTION_CORE_ACTION_UPDATE.md +313 -0
- package/documentation/RETENTION_UPDATE_SUMMARY.md +108 -0
- package/documentation/RUN_MIGRATION_NOW.md +39 -0
- package/documentation/SCRIPTS_UPDATE_SUMMARY.md +251 -0
- package/documentation/SETUP_GUIDE.md +184 -0
- package/documentation/STATE_MACHINE_IMPLEMENTATION.md +250 -0
- package/documentation/TELEGRAM_NOTIFICATIONS_DIAGNOSIS.md +361 -0
- package/documentation/UNIFIED_ARCHITECTURE.md +231 -0
- package/documentation/VOTING_DEPLOYMENT_SUMMARY.md +392 -0
- package/documentation/WEBSOCKET_ARCHITECTURE.md +881 -0
- package/documentation/WHAT_WE_BUILT_TODAY.md +369 -0
- package/documentation/latest/LATEST_PRODUCTION_SETUP.md +865 -0
- package/ecosystem.config.js +65 -0
- package/env.template +125 -0
- package/middleware/apiKeyAuth.js +136 -0
- package/middleware/authenticate.js +214 -0
- package/middleware/developerUserAuth.js +76 -0
- package/middleware/socketAuth.js +69 -0
- package/package.json +49 -0
- package/postman/Dubs-API-v1-With-Voting.postman_collection.json +555 -0
- package/postman/Dubs-API-v1.postman_collection.json +205 -0
- package/postman/Dubs_Developer_API.postman_collection.json +662 -0
- package/postman/QUICKSTART.md +118 -0
- package/postman/QUICK_REFERENCE.md +246 -0
- package/postman/README.md +71 -0
- package/postman/VOTING_API_GUIDE.md +426 -0
- package/refactor/Animations.md +148 -0
- package/refactor/Chat.md +252 -0
- package/routes/actionsRoutes.js +699 -0
- package/routes/adminRoutes.js +370 -0
- package/routes/analyticsRoutes.js +1262 -0
- package/routes/arcadeRoutes.js +557 -0
- package/routes/authRoutes.js +2310 -0
- package/routes/avatarRoutes.js +85 -0
- package/routes/botRoutes.js +211 -0
- package/routes/chatRoutes.js +377 -0
- package/routes/cryptoPriceRoutes.js +105 -0
- package/routes/developerRoutes.js +4201 -0
- package/routes/deviceRoutes.js +214 -0
- package/routes/dmRoutes.js +167 -0
- package/routes/esportsRoutes.js +806 -0
- package/routes/exchangeRateRoutes.js +233 -0
- package/routes/gamesRoutes.js +3028 -0
- package/routes/jackpotRoutes.js +754 -0
- package/routes/keeperMonitoringRoutes.js +156 -0
- package/routes/keeperWebhookRoutes.js +466 -0
- package/routes/livescoresRoutes.js +31 -0
- package/routes/pickemAdminRoutes.js +199 -0
- package/routes/pickemRoutes.js +231 -0
- package/routes/playerStatsRoutes.js +147 -0
- package/routes/portfolioRoutes.js +217 -0
- package/routes/promoRoutes.js +418 -0
- package/routes/referralEarningsRoutes.js +392 -0
- package/routes/socialRoutes.js +459 -0
- package/routes/sportsRoutes.js +1271 -0
- package/routes/survivorAdminRoutes.js +345 -0
- package/routes/survivorRoutes.js +756 -0
- package/routes/uploadRoutes.js +256 -0
- package/routes/userProfileRoutes.js +244 -0
- package/routes/whatsNewRoutes.js +331 -0
- package/scripts/.claude/settings.local.json +15 -0
- package/scripts/README.md +170 -0
- package/scripts/RESTART_EVERYTHING.sh +104 -0
- package/scripts/add-claim-columns.sql +48 -0
- package/scripts/add-crypto-prices-cache.sql +27 -0
- package/scripts/add-exchange-rates-cache.sql +40 -0
- package/scripts/add-game-invite-column.sql +23 -0
- package/scripts/add-game-invite-notification.sql +33 -0
- package/scripts/add-game-invite-telegram-pref.sql +16 -0
- package/scripts/add-game-joined-notification.sql +16 -0
- package/scripts/add-game-joined-pref.js +40 -0
- package/scripts/add-game-joined-preference.sql +6 -0
- package/scripts/add-game-start-notifications.sql +41 -0
- package/scripts/add-notification-flags-to-games.sql +55 -0
- package/scripts/add-pending-game-dismissals.sql +19 -0
- package/scripts/add-preferred-currency.sql +34 -0
- package/scripts/add-winner-columns.js +61 -0
- package/scripts/add_mention_system.sql +53 -0
- package/scripts/add_payment_system.sql +96 -0
- package/scripts/add_sports_event_id_column.sql +22 -0
- package/scripts/analyze-cohort-data-heroku.js +276 -0
- package/scripts/analyze-cohort-data.js +295 -0
- package/scripts/analyze-prod-cohorts.sh +10 -0
- package/scripts/backfill-matchup-images.js +245 -0
- package/scripts/backfill-missing-signatures.js +175 -0
- package/scripts/backfill-referral-earnings.js +202 -0
- package/scripts/check-chat-schema.js +130 -0
- package/scripts/check-db.sh +14 -0
- package/scripts/check_oracle_in_game.js +54 -0
- package/scripts/cleanup-database.js +193 -0
- package/scripts/clear-notification-cache.js +85 -0
- package/scripts/convert-mnemonic.js +50 -0
- package/scripts/create-users-table.sql +44 -0
- package/scripts/debug-cohort-counts.js +248 -0
- package/scripts/debug-winner-calc.js +84 -0
- package/scripts/deploy-payment-system.sh +118 -0
- package/scripts/deploy-to-heroku.sh +63 -0
- package/scripts/diagnose-locked-round.js +143 -0
- package/scripts/dubs-cli.js +720 -0
- package/scripts/dump-account.js +65 -0
- package/scripts/find-vrf-offset.js +48 -0
- package/scripts/fix-chat-notifications-constraint.sql +122 -0
- package/scripts/fix-claim-columns.js +124 -0
- package/scripts/fix-constraint-now.js +44 -0
- package/scripts/fix-lock-timestamps.js +96 -0
- package/scripts/fix-locked-round.sh +126 -0
- package/scripts/fix-missing-badges.sql +91 -0
- package/scripts/fix-payment-notifications.sql +41 -0
- package/scripts/force-new-round.js +55 -0
- package/scripts/force-resolve-and-claim.js +278 -0
- package/scripts/important/README.md +115 -0
- package/scripts/important/authority-force-lock.js +197 -0
- package/scripts/important/authority-resolve-game.js +267 -0
- package/scripts/important/check-game-status.js +373 -0
- package/scripts/important/list-pending-games-by-version.js +270 -0
- package/scripts/important/reconcile-v1-v2-payouts.js +270 -0
- package/scripts/initialize-jackpot.js +111 -0
- package/scripts/jackpot/.claude/settings.local.json +10 -0
- package/scripts/jackpot/force-reset.js +84 -0
- package/scripts/jackpot/initialize-mainnet.js +100 -0
- package/scripts/jackpot/keeper.js +742 -0
- package/scripts/jackpot/status.js +107 -0
- package/scripts/jackpot/update-round-duration.js +143 -0
- package/scripts/keeper-bot.js +112 -0
- package/scripts/list-pending-games.js +131 -0
- package/scripts/migrate-chat-v2.js +127 -0
- package/scripts/migrate-chat-winners.js +84 -0
- package/scripts/migrate-chat.sh +17 -0
- package/scripts/migrate-game-invite.js +83 -0
- package/scripts/migrate-heroku-game-notifications.sh +159 -0
- package/scripts/migrations/001_analytics_tables.sql +422 -0
- package/scripts/migrations/002_add_matchup_image_url.sql +14 -0
- package/scripts/migrations/003_referral_earnings.sql +208 -0
- package/scripts/migrations/004_add_whats_new_notification_type.sql +62 -0
- package/scripts/migrations/005_add_connect4_your_turn_notification.sql +61 -0
- package/scripts/migrations/005_push_notifications.sql +55 -0
- package/scripts/migrations/006_add_draw_team_players.sql +28 -0
- package/scripts/migrations/006_add_game_cancelled_notification.sql +62 -0
- package/scripts/migrations/007_add_gif_url.sql +8 -0
- package/scripts/migrations/008_add_connect4_columns.sql +139 -0
- package/scripts/migrations/008_add_pool_tracking.sql +22 -0
- package/scripts/migrations/009_create_survivor_pool_tables.sql +174 -0
- package/scripts/migrations/010_add_survivor_pool_outcome.sql +28 -0
- package/scripts/migrations/011_create_developer_tables.sql +67 -0
- package/scripts/migrations/011_fix_keeper_tables.sql +85 -0
- package/scripts/migrations/012_create_developer_webhooks.sql +31 -0
- package/scripts/migrations/013_add_network_mode.sql +18 -0
- package/scripts/migrations/014_create_developer_app_users.sql +19 -0
- package/scripts/migrations/015_add_ui_config.sql +4 -0
- package/scripts/migrations/016_add_resolution_secret.sql +4 -0
- package/scripts/migrations/017_add_external_game_id.sql +3 -0
- package/scripts/migrations/018_create_pickem_tables.sql +115 -0
- package/scripts/migrations/019_expo_push_tokens.sql +19 -0
- package/scripts/migrations/create_whats_new_tables.sql +88 -0
- package/scripts/migrations/drop_live_games_tables.sql +34 -0
- package/scripts/open-jackpot-round.js +85 -0
- package/scripts/purge-all-data.sh +329 -0
- package/scripts/purge-all-data.sql +142 -0
- package/scripts/purge-heroku-data.sh +149 -0
- package/scripts/purge-heroku-data.sql +62 -0
- package/scripts/rebuild-heroku-database.sh +113 -0
- package/scripts/recover-funds.js +357 -0
- package/scripts/regenerate-epl-images.js +278 -0
- package/scripts/resize-s3-matchup-images.js +374 -0
- package/scripts/resolve-direct.js +88 -0
- package/scripts/resolve-mock-game.js +124 -0
- package/scripts/resolve-pickem-game.js +55 -0
- package/scripts/resolve-round-manual.js +83 -0
- package/scripts/resolve-stuck-game.js +382 -0
- package/scripts/resolve-stuck-round.js +42 -0
- package/scripts/run-connect4-migration.sh +16 -0
- package/scripts/run-mention-migration.sh +32 -0
- package/scripts/run-payment-migration.sh +51 -0
- package/scripts/run-preferred-currency-migration.sh +31 -0
- package/scripts/run-referral-earnings-migration.sh +32 -0
- package/scripts/run-survivor-outcome-migration.sh +16 -0
- package/scripts/seed-test-users.js +346 -0
- package/scripts/setup-auth-tables.js +78 -0
- package/scripts/setup-complete-database.sql +992 -0
- package/scripts/setup-database-fresh.sh +359 -0
- package/scripts/setup-heroku-keeper.sh +48 -0
- package/scripts/setup-keeper-database.js +83 -0
- package/scripts/setup-keeper-state-db.sql +110 -0
- package/scripts/setup-oracle.sh +39 -0
- package/scripts/setup-pnl-tracking.js +111 -0
- package/scripts/start-devnet.sh +14 -0
- package/scripts/test-arcade-devnet.sh +160 -0
- package/scripts/test-arcade-match.sh +109 -0
- package/scripts/test-automatic-mode.sh +239 -0
- package/scripts/test-connect4-cancel-claim.js +370 -0
- package/scripts/test-connect4-e2e.js +369 -0
- package/scripts/test-connect4-resolve.js +369 -0
- package/scripts/test-game-state-endpoint.js +136 -0
- package/scripts/test-invite-notification.js +86 -0
- package/scripts/test-jackpot-api.sh +71 -0
- package/scripts/test-poll-confirmation.js +267 -0
- package/scripts/test-resolve-game.js +271 -0
- package/scripts/test-resolve-signature.js +223 -0
- package/scripts/test-signature-preservation.js +124 -0
- package/scripts/test-state-machine.js +291 -0
- package/scripts/test-webhook-receiver.js +60 -0
- package/scripts/update-notification-constraint.js +52 -0
- package/scripts/verify-account-layout.js +145 -0
- package/scripts/verify-winner-algorithm.js +278 -0
- package/server.js +5259 -0
- package/services/arcadeMatchService.js +763 -0
- package/services/automaticGameOracle.js +1596 -0
- package/services/chatService.js +1612 -0
- package/services/connect4GameService.js +1049 -0
- package/services/connect4NotificationService.js +374 -0
- package/services/cryptoPriceService.js +223 -0
- package/services/customGameResolver.js +260 -0
- package/services/db.js +79 -0
- package/services/directMessageService.js +389 -0
- package/services/discordNotifications.js +160 -0
- package/services/exchangeRateService.js +289 -0
- package/services/expoPushService.js +314 -0
- package/services/gamesCacheService.js +539 -0
- package/services/jackpotHistory.js +331 -0
- package/services/jackpotService.js +856 -0
- package/services/keeperStateService.js +355 -0
- package/services/matchupImageService.js +591 -0
- package/services/notificationCacheService.js +407 -0
- package/services/pickemOracle.js +440 -0
- package/services/playerStatsService.js +389 -0
- package/services/portfolioService.js +555 -0
- package/services/promoService.js +757 -0
- package/services/promoTreasuryService.js +239 -0
- package/services/pushNotifications.js +353 -0
- package/services/redisService.js +422 -0
- package/services/referralEarningsService.js +728 -0
- package/services/s3Service.js +396 -0
- package/services/socialService.js +1202 -0
- package/services/survivorOracle.js +469 -0
- package/services/survivorSimulator.js +475 -0
- package/services/telegramNotifications.js +461 -0
- package/services/userProfileStatsService.js +1185 -0
- package/services/whatsNewService.js +388 -0
- package/utils/urlHelper.js +95 -0
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Survivor Pool Simulator Service
|
|
3
|
+
* Development/testing tools for March Madness Survivor Pool
|
|
4
|
+
*
|
|
5
|
+
* Provides mock tournament data and simulation controls for testing
|
|
6
|
+
* without waiting for actual tournament games.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const { pool } = require('./db');
|
|
10
|
+
const survivorController = require('../controllers/survivorController');
|
|
11
|
+
|
|
12
|
+
// ============================================
|
|
13
|
+
// MOCK TOURNAMENT DATA
|
|
14
|
+
// ============================================
|
|
15
|
+
|
|
16
|
+
// ESPN logo URL helper - uses ESPN's CDN
|
|
17
|
+
const espnLogo = (espnId) => `https://a.espncdn.com/i/teamlogos/ncaa/500/${espnId}.png`;
|
|
18
|
+
|
|
19
|
+
// 68 teams for March Madness (64 + 4 First Four) with ESPN team IDs for logos
|
|
20
|
+
const MOCK_TEAMS = [
|
|
21
|
+
// EAST Region
|
|
22
|
+
{ id: 'e1', name: 'Duke', seed: 1, region: 'EAST', logo: espnLogo(150) },
|
|
23
|
+
{ id: 'e2', name: 'Alabama', seed: 2, region: 'EAST', logo: espnLogo(333) },
|
|
24
|
+
{ id: 'e3', name: 'Wisconsin', seed: 3, region: 'EAST', logo: espnLogo(275) },
|
|
25
|
+
{ id: 'e4', name: 'Arizona', seed: 4, region: 'EAST', logo: espnLogo(12) },
|
|
26
|
+
{ id: 'e5', name: 'Oregon', seed: 5, region: 'EAST', logo: espnLogo(2483) },
|
|
27
|
+
{ id: 'e6', name: 'BYU', seed: 6, region: 'EAST', logo: espnLogo(252) },
|
|
28
|
+
{ id: 'e7', name: 'St. Marys', seed: 7, region: 'EAST', logo: espnLogo(2608) },
|
|
29
|
+
{ id: 'e8', name: 'Louisville', seed: 8, region: 'EAST', logo: espnLogo(97) },
|
|
30
|
+
{ id: 'e9', name: 'Creighton', seed: 9, region: 'EAST', logo: espnLogo(156) },
|
|
31
|
+
{ id: 'e10', name: 'Colorado', seed: 10, region: 'EAST', logo: espnLogo(38) },
|
|
32
|
+
{ id: 'e11', name: 'Memphis', seed: 11, region: 'EAST', logo: espnLogo(235) },
|
|
33
|
+
{ id: 'e12', name: 'UC San Diego', seed: 12, region: 'EAST', logo: espnLogo(301) },
|
|
34
|
+
{ id: 'e13', name: 'Yale', seed: 13, region: 'EAST', logo: espnLogo(43) },
|
|
35
|
+
{ id: 'e14', name: 'Lipscomb', seed: 14, region: 'EAST', logo: espnLogo(288) },
|
|
36
|
+
{ id: 'e15', name: 'Robert Morris', seed: 15, region: 'EAST', logo: espnLogo(2523) },
|
|
37
|
+
{ id: 'e16', name: 'American', seed: 16, region: 'EAST', logo: espnLogo(44) },
|
|
38
|
+
|
|
39
|
+
// WEST Region
|
|
40
|
+
{ id: 'w1', name: 'Florida', seed: 1, region: 'WEST', logo: espnLogo(57) },
|
|
41
|
+
{ id: 'w2', name: 'St. Johns', seed: 2, region: 'WEST', logo: espnLogo(2599) },
|
|
42
|
+
{ id: 'w3', name: 'Texas Tech', seed: 3, region: 'WEST', logo: espnLogo(2641) },
|
|
43
|
+
{ id: 'w4', name: 'Maryland', seed: 4, region: 'WEST', logo: espnLogo(120) },
|
|
44
|
+
{ id: 'w5', name: 'Memphis State', seed: 5, region: 'WEST', logo: espnLogo(235) },
|
|
45
|
+
{ id: 'w6', name: 'Missouri', seed: 6, region: 'WEST', logo: espnLogo(142) },
|
|
46
|
+
{ id: 'w7', name: 'Kansas', seed: 7, region: 'WEST', logo: espnLogo(2305) },
|
|
47
|
+
{ id: 'w8', name: 'UConn', seed: 8, region: 'WEST', logo: espnLogo(41) },
|
|
48
|
+
{ id: 'w9', name: 'Oklahoma', seed: 9, region: 'WEST', logo: espnLogo(201) },
|
|
49
|
+
{ id: 'w10', name: 'Arkansas', seed: 10, region: 'WEST', logo: espnLogo(8) },
|
|
50
|
+
{ id: 'w11', name: 'Drake', seed: 11, region: 'WEST', logo: espnLogo(2181) },
|
|
51
|
+
{ id: 'w12', name: 'Grand Canyon', seed: 12, region: 'WEST', logo: espnLogo(2253) },
|
|
52
|
+
{ id: 'w13', name: 'UNC Wilmington', seed: 13, region: 'WEST', logo: espnLogo(350) },
|
|
53
|
+
{ id: 'w14', name: 'North Dakota', seed: 14, region: 'WEST', logo: espnLogo(155) },
|
|
54
|
+
{ id: 'w15', name: 'Omaha', seed: 15, region: 'WEST', logo: espnLogo(2437) },
|
|
55
|
+
{ id: 'w16', name: 'Norfolk State', seed: 16, region: 'WEST', logo: espnLogo(2450) },
|
|
56
|
+
|
|
57
|
+
// SOUTH Region
|
|
58
|
+
{ id: 's1', name: 'Auburn', seed: 1, region: 'SOUTH', logo: espnLogo(2) },
|
|
59
|
+
{ id: 's2', name: 'Michigan State', seed: 2, region: 'SOUTH', logo: espnLogo(127) },
|
|
60
|
+
{ id: 's3', name: 'Iowa State', seed: 3, region: 'SOUTH', logo: espnLogo(66) },
|
|
61
|
+
{ id: 's4', name: 'Texas A&M', seed: 4, region: 'SOUTH', logo: espnLogo(245) },
|
|
62
|
+
{ id: 's5', name: 'Michigan', seed: 5, region: 'SOUTH', logo: espnLogo(130) },
|
|
63
|
+
{ id: 's6', name: 'Ole Miss', seed: 6, region: 'SOUTH', logo: espnLogo(145) },
|
|
64
|
+
{ id: 's7', name: 'Marquette', seed: 7, region: 'SOUTH', logo: espnLogo(269) },
|
|
65
|
+
{ id: 's8', name: 'Boise State', seed: 8, region: 'SOUTH', logo: espnLogo(68) },
|
|
66
|
+
{ id: 's9', name: 'Georgia', seed: 9, region: 'SOUTH', logo: espnLogo(61) },
|
|
67
|
+
{ id: 's10', name: 'New Mexico', seed: 10, region: 'SOUTH', logo: espnLogo(167) },
|
|
68
|
+
{ id: 's11', name: 'Vanderbilt', seed: 11, region: 'SOUTH', logo: espnLogo(238) },
|
|
69
|
+
{ id: 's12', name: 'Liberty', seed: 12, region: 'SOUTH', logo: espnLogo(2335) },
|
|
70
|
+
{ id: 's13', name: 'Akron', seed: 13, region: 'SOUTH', logo: espnLogo(2006) },
|
|
71
|
+
{ id: 's14', name: 'Montana', seed: 14, region: 'SOUTH', logo: espnLogo(149) },
|
|
72
|
+
{ id: 's15', name: 'Wofford', seed: 15, region: 'SOUTH', logo: espnLogo(2747) },
|
|
73
|
+
{ id: 's16', name: 'Alabama State', seed: 16, region: 'SOUTH', logo: espnLogo(2011) },
|
|
74
|
+
|
|
75
|
+
// MIDWEST Region
|
|
76
|
+
{ id: 'm1', name: 'Houston', seed: 1, region: 'MIDWEST', logo: espnLogo(248) },
|
|
77
|
+
{ id: 'm2', name: 'Tennessee', seed: 2, region: 'MIDWEST', logo: espnLogo(2633) },
|
|
78
|
+
{ id: 'm3', name: 'Kentucky', seed: 3, region: 'MIDWEST', logo: espnLogo(96) },
|
|
79
|
+
{ id: 'm4', name: 'Purdue', seed: 4, region: 'MIDWEST', logo: espnLogo(2509) },
|
|
80
|
+
{ id: 'm5', name: 'Clemson', seed: 5, region: 'MIDWEST', logo: espnLogo(228) },
|
|
81
|
+
{ id: 'm6', name: 'Illinois', seed: 6, region: 'MIDWEST', logo: espnLogo(356) },
|
|
82
|
+
{ id: 'm7', name: 'UCLA', seed: 7, region: 'MIDWEST', logo: espnLogo(26) },
|
|
83
|
+
{ id: 'm8', name: 'Gonzaga', seed: 8, region: 'MIDWEST', logo: espnLogo(2250) },
|
|
84
|
+
{ id: 'm9', name: 'Baylor', seed: 9, region: 'MIDWEST', logo: espnLogo(239) },
|
|
85
|
+
{ id: 'm10', name: 'Texas', seed: 10, region: 'MIDWEST', logo: espnLogo(251) },
|
|
86
|
+
{ id: 'm11', name: 'NC State', seed: 11, region: 'MIDWEST', logo: espnLogo(152) },
|
|
87
|
+
{ id: 'm12', name: 'McNeese State', seed: 12, region: 'MIDWEST', logo: espnLogo(2377) },
|
|
88
|
+
{ id: 'm13', name: 'Vermont', seed: 13, region: 'MIDWEST', logo: espnLogo(261) },
|
|
89
|
+
{ id: 'm14', name: 'Troy', seed: 14, region: 'MIDWEST', logo: espnLogo(2653) },
|
|
90
|
+
{ id: 'm15', name: 'Siena', seed: 15, region: 'MIDWEST', logo: espnLogo(2561) },
|
|
91
|
+
{ id: 'm16', name: 'SFA', seed: 16, region: 'MIDWEST', logo: espnLogo(2617) },
|
|
92
|
+
|
|
93
|
+
// First Four teams (play-in games)
|
|
94
|
+
{ id: 'ff1', name: 'Texas Southern', seed: 16, region: 'FIRST_FOUR', logo: espnLogo(2640) },
|
|
95
|
+
{ id: 'ff2', name: 'Jackson State', seed: 16, region: 'FIRST_FOUR', logo: espnLogo(2296) },
|
|
96
|
+
{ id: 'ff3', name: 'Wagner', seed: 16, region: 'FIRST_FOUR', logo: espnLogo(2718) },
|
|
97
|
+
{ id: 'ff4', name: 'Bryant', seed: 16, region: 'FIRST_FOUR', logo: espnLogo(2803) },
|
|
98
|
+
];
|
|
99
|
+
|
|
100
|
+
// Generate mock bracket with standard seeding matchups
|
|
101
|
+
function generateMockBracket(poolId) {
|
|
102
|
+
const games = [];
|
|
103
|
+
let gameIndex = 0;
|
|
104
|
+
|
|
105
|
+
// Helper to create game object
|
|
106
|
+
const createGame = (team1, team2, round, region) => ({
|
|
107
|
+
round,
|
|
108
|
+
espnGameId: `mock-${poolId}-${++gameIndex}`,
|
|
109
|
+
gameDate: new Date(Date.now() + (round * 2 + gameIndex) * 3600000), // Stagger games
|
|
110
|
+
team1Id: team1.id,
|
|
111
|
+
team1Name: team1.name,
|
|
112
|
+
team1Seed: team1.seed,
|
|
113
|
+
team1Logo: team1.logo || null,
|
|
114
|
+
team2Id: team2.id,
|
|
115
|
+
team2Name: team2.name,
|
|
116
|
+
team2Seed: team2.seed,
|
|
117
|
+
team2Logo: team2.logo || null,
|
|
118
|
+
region,
|
|
119
|
+
status: 'scheduled'
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Round 1: First Four (4 games)
|
|
123
|
+
const firstFourTeams = MOCK_TEAMS.filter(t => t.region === 'FIRST_FOUR');
|
|
124
|
+
for (let i = 0; i < firstFourTeams.length; i += 2) {
|
|
125
|
+
games.push(createGame(firstFourTeams[i], firstFourTeams[i + 1], 1, 'FIRST_FOUR'));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Round 2: Round of 64 (32 games - standard 1v16, 2v15, etc.)
|
|
129
|
+
const regions = ['EAST', 'WEST', 'SOUTH', 'MIDWEST'];
|
|
130
|
+
for (const region of regions) {
|
|
131
|
+
const regionTeams = MOCK_TEAMS.filter(t => t.region === region);
|
|
132
|
+
// Standard bracket: 1v16, 8v9, 5v12, 4v13, 6v11, 3v14, 7v10, 2v15
|
|
133
|
+
const matchups = [
|
|
134
|
+
[1, 16], [8, 9], [5, 12], [4, 13], [6, 11], [3, 14], [7, 10], [2, 15]
|
|
135
|
+
];
|
|
136
|
+
for (const [seed1, seed2] of matchups) {
|
|
137
|
+
const team1 = regionTeams.find(t => t.seed === seed1);
|
|
138
|
+
const team2 = regionTeams.find(t => t.seed === seed2);
|
|
139
|
+
if (team1 && team2) {
|
|
140
|
+
games.push(createGame(team1, team2, 2, region));
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Note: Rounds 3-7 will be generated dynamically based on Round 2 winners
|
|
146
|
+
// For simulation purposes, we'll generate placeholder games
|
|
147
|
+
|
|
148
|
+
return games;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
class SurvivorSimulator {
|
|
152
|
+
/**
|
|
153
|
+
* Generate mock tournament games for a pool
|
|
154
|
+
*/
|
|
155
|
+
async generateTournamentBracket(poolId) {
|
|
156
|
+
const games = generateMockBracket(poolId);
|
|
157
|
+
const imported = await survivorController.importTournamentGames(poolId, games);
|
|
158
|
+
|
|
159
|
+
console.log(`[Simulator] Generated ${imported.length} tournament games for pool ${poolId}`);
|
|
160
|
+
return imported;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Simulate a round with configurable upset chance
|
|
165
|
+
* @param {number} poolId - Pool ID
|
|
166
|
+
* @param {object} options - Simulation options
|
|
167
|
+
* @param {number} options.upsetChance - Probability of upset (0-1, default 0.2)
|
|
168
|
+
* @param {boolean} options.favorChalk - If true, favorites always win
|
|
169
|
+
*/
|
|
170
|
+
async simulateRound(poolId, options = {}) {
|
|
171
|
+
const { upsetChance = 0.2, favorChalk = false } = options;
|
|
172
|
+
|
|
173
|
+
// Get pool info
|
|
174
|
+
const poolResult = await pool.query(
|
|
175
|
+
'SELECT * FROM survivor_pools WHERE id = $1',
|
|
176
|
+
[poolId]
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
if (poolResult.rows.length === 0) {
|
|
180
|
+
throw new Error('Pool not found');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const survivorPool = poolResult.rows[0];
|
|
184
|
+
const currentRound = survivorPool.current_round;
|
|
185
|
+
|
|
186
|
+
// Get unresolved games for current round
|
|
187
|
+
const gamesResult = await pool.query(
|
|
188
|
+
`SELECT * FROM survivor_tournament_games
|
|
189
|
+
WHERE pool_id = $1 AND round = $2 AND status != 'final'`,
|
|
190
|
+
[poolId, currentRound]
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
const games = gamesResult.rows;
|
|
194
|
+
console.log(`[Simulator] Simulating ${games.length} games for round ${currentRound}`);
|
|
195
|
+
|
|
196
|
+
const results = [];
|
|
197
|
+
|
|
198
|
+
for (const game of games) {
|
|
199
|
+
// Determine winner based on seed and upset chance
|
|
200
|
+
const team1Seed = game.team1_seed || 8;
|
|
201
|
+
const team2Seed = game.team2_seed || 8;
|
|
202
|
+
const team1Favored = team1Seed < team2Seed;
|
|
203
|
+
|
|
204
|
+
let winnerId;
|
|
205
|
+
let winnerName;
|
|
206
|
+
|
|
207
|
+
if (favorChalk) {
|
|
208
|
+
// Chalk: favorite always wins
|
|
209
|
+
winnerId = team1Favored ? game.team1_id : game.team2_id;
|
|
210
|
+
winnerName = team1Favored ? game.team1_name : game.team2_name;
|
|
211
|
+
} else {
|
|
212
|
+
// Random with upset chance
|
|
213
|
+
const isUpset = Math.random() < upsetChance;
|
|
214
|
+
if (team1Favored) {
|
|
215
|
+
winnerId = isUpset ? game.team2_id : game.team1_id;
|
|
216
|
+
winnerName = isUpset ? game.team2_name : game.team1_name;
|
|
217
|
+
} else {
|
|
218
|
+
winnerId = isUpset ? game.team1_id : game.team2_id;
|
|
219
|
+
winnerName = isUpset ? game.team1_name : game.team2_name;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Generate realistic scores
|
|
224
|
+
const baseScore = 65 + Math.floor(Math.random() * 25);
|
|
225
|
+
const margin = Math.floor(Math.random() * 20) + 3;
|
|
226
|
+
const winnerScore = baseScore + Math.floor(margin / 2);
|
|
227
|
+
const loserScore = baseScore - Math.floor(margin / 2);
|
|
228
|
+
|
|
229
|
+
const team1Score = winnerId === game.team1_id ? winnerScore : loserScore;
|
|
230
|
+
const team2Score = winnerId === game.team2_id ? winnerScore : loserScore;
|
|
231
|
+
|
|
232
|
+
// Update game result
|
|
233
|
+
await pool.query(
|
|
234
|
+
`UPDATE survivor_tournament_games
|
|
235
|
+
SET winner_id = $3, team1_score = $4, team2_score = $5, status = 'final', updated_at = NOW()
|
|
236
|
+
WHERE pool_id = $1 AND id = $2`,
|
|
237
|
+
[poolId, game.id, winnerId, team1Score, team2Score]
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
results.push({
|
|
241
|
+
gameId: game.id,
|
|
242
|
+
espnGameId: game.espn_game_id,
|
|
243
|
+
winner: winnerName,
|
|
244
|
+
team1Score,
|
|
245
|
+
team2Score,
|
|
246
|
+
isUpset: (team1Favored && winnerId === game.team2_id) || (!team1Favored && winnerId === game.team1_id)
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
console.log(` ${game.team1_name} (${team1Seed}) vs ${game.team2_name} (${team2Seed}): ${winnerName} wins ${team1Score}-${team2Score}`);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Now resolve the round: process picks, eliminate losers, and advance to next round
|
|
253
|
+
console.log(`[Simulator] Resolving round ${currentRound}...`);
|
|
254
|
+
const survivorOracle = require('./survivorOracle');
|
|
255
|
+
const resolution = await survivorOracle.resolveCurrentRound(poolId);
|
|
256
|
+
|
|
257
|
+
return {
|
|
258
|
+
poolId,
|
|
259
|
+
round: currentRound,
|
|
260
|
+
gamesSimulated: results.length,
|
|
261
|
+
results,
|
|
262
|
+
resolution
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Generate test entries with random picks for current round
|
|
268
|
+
*/
|
|
269
|
+
async generateTestEntries(poolId, count = 10) {
|
|
270
|
+
// Get pool
|
|
271
|
+
const poolResult = await pool.query(
|
|
272
|
+
'SELECT * FROM survivor_pools WHERE id = $1',
|
|
273
|
+
[poolId]
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
if (poolResult.rows.length === 0) {
|
|
277
|
+
throw new Error('Pool not found');
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const survivorPool = poolResult.rows[0];
|
|
281
|
+
|
|
282
|
+
// Get available teams for current round
|
|
283
|
+
const gamesResult = await pool.query(
|
|
284
|
+
`SELECT * FROM survivor_tournament_games
|
|
285
|
+
WHERE pool_id = $1 AND round = $2`,
|
|
286
|
+
[poolId, survivorPool.current_round]
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
const teams = [];
|
|
290
|
+
for (const game of gamesResult.rows) {
|
|
291
|
+
teams.push({
|
|
292
|
+
id: game.team1_id,
|
|
293
|
+
name: game.team1_name,
|
|
294
|
+
seed: game.team1_seed,
|
|
295
|
+
espnGameId: game.espn_game_id,
|
|
296
|
+
opponent: game.team2_name
|
|
297
|
+
});
|
|
298
|
+
teams.push({
|
|
299
|
+
id: game.team2_id,
|
|
300
|
+
name: game.team2_name,
|
|
301
|
+
seed: game.team2_seed,
|
|
302
|
+
espnGameId: game.espn_game_id,
|
|
303
|
+
opponent: game.team1_name
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const entries = [];
|
|
308
|
+
|
|
309
|
+
for (let i = 0; i < count; i++) {
|
|
310
|
+
// Create test user if needed
|
|
311
|
+
const walletAddress = `TestWallet${Date.now()}${i}`;
|
|
312
|
+
const username = `TestUser${i + 1}`;
|
|
313
|
+
|
|
314
|
+
const userResult = await pool.query(
|
|
315
|
+
`INSERT INTO users (wallet_address, username)
|
|
316
|
+
VALUES ($1, $2)
|
|
317
|
+
ON CONFLICT (wallet_address) DO UPDATE SET username = EXCLUDED.username
|
|
318
|
+
RETURNING id`,
|
|
319
|
+
[walletAddress, username]
|
|
320
|
+
);
|
|
321
|
+
const userId = userResult.rows[0].id;
|
|
322
|
+
|
|
323
|
+
// Join pool
|
|
324
|
+
const entryResult = await pool.query(
|
|
325
|
+
`INSERT INTO survivor_entries (pool_id, user_id, wallet_address)
|
|
326
|
+
VALUES ($1, $2, $3)
|
|
327
|
+
ON CONFLICT (pool_id, user_id) DO NOTHING
|
|
328
|
+
RETURNING *`,
|
|
329
|
+
[poolId, userId, walletAddress]
|
|
330
|
+
);
|
|
331
|
+
|
|
332
|
+
if (entryResult.rows.length === 0) {
|
|
333
|
+
continue; // Already joined
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const entry = entryResult.rows[0];
|
|
337
|
+
|
|
338
|
+
// Pick random team
|
|
339
|
+
const randomTeam = teams[Math.floor(Math.random() * teams.length)];
|
|
340
|
+
|
|
341
|
+
await pool.query(
|
|
342
|
+
`INSERT INTO survivor_picks
|
|
343
|
+
(entry_id, round, team_id, team_name, team_seed, espn_game_id, opponent_name)
|
|
344
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7)`,
|
|
345
|
+
[entry.id, survivorPool.current_round, randomTeam.id, randomTeam.name, randomTeam.seed, randomTeam.espnGameId, randomTeam.opponent]
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
entries.push({
|
|
349
|
+
...entry,
|
|
350
|
+
username,
|
|
351
|
+
pick: randomTeam.name
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
console.log(`[Simulator] Created entry: ${username} picked ${randomTeam.name}`);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
return {
|
|
358
|
+
poolId,
|
|
359
|
+
entriesCreated: entries.length,
|
|
360
|
+
entries
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Set round deadline to N minutes from now
|
|
366
|
+
*/
|
|
367
|
+
async setDeadline(poolId, minutes) {
|
|
368
|
+
const deadline = new Date(Date.now() + minutes * 60 * 1000);
|
|
369
|
+
|
|
370
|
+
await pool.query(
|
|
371
|
+
`UPDATE survivor_pools SET round_deadline = $2, updated_at = NOW() WHERE id = $1`,
|
|
372
|
+
[poolId, deadline]
|
|
373
|
+
);
|
|
374
|
+
|
|
375
|
+
console.log(`[Simulator] Set deadline for pool ${poolId} to ${deadline.toISOString()}`);
|
|
376
|
+
return { poolId, deadline };
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Reset pool to round 1 and clear all entries/picks
|
|
381
|
+
* @param {number} poolId - Pool ID
|
|
382
|
+
* @param {object} options - Reset options
|
|
383
|
+
* @param {boolean} options.deleteEntries - If true, also delete all entries (full reset)
|
|
384
|
+
*/
|
|
385
|
+
async resetPool(poolId, options = {}) {
|
|
386
|
+
const { deleteEntries = false } = options;
|
|
387
|
+
const client = await pool.connect();
|
|
388
|
+
|
|
389
|
+
try {
|
|
390
|
+
await client.query('BEGIN');
|
|
391
|
+
|
|
392
|
+
// Delete all picks
|
|
393
|
+
await client.query(
|
|
394
|
+
`DELETE FROM survivor_picks WHERE entry_id IN (SELECT id FROM survivor_entries WHERE pool_id = $1)`,
|
|
395
|
+
[poolId]
|
|
396
|
+
);
|
|
397
|
+
|
|
398
|
+
if (deleteEntries) {
|
|
399
|
+
// Full reset: delete all entries
|
|
400
|
+
await client.query(
|
|
401
|
+
`DELETE FROM survivor_entries WHERE pool_id = $1`,
|
|
402
|
+
[poolId]
|
|
403
|
+
);
|
|
404
|
+
console.log(`[Simulator] Deleted all entries for pool ${poolId}`);
|
|
405
|
+
} else {
|
|
406
|
+
// Soft reset: just reset entries to alive
|
|
407
|
+
await client.query(
|
|
408
|
+
`UPDATE survivor_entries
|
|
409
|
+
SET is_alive = true, eliminated_at_round = NULL, updated_at = NOW()
|
|
410
|
+
WHERE pool_id = $1`,
|
|
411
|
+
[poolId]
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Delete tournament games (so bracket can be regenerated fresh)
|
|
416
|
+
await client.query(
|
|
417
|
+
`DELETE FROM survivor_tournament_games WHERE pool_id = $1`,
|
|
418
|
+
[poolId]
|
|
419
|
+
);
|
|
420
|
+
|
|
421
|
+
// Reset pool to round 1 and open status
|
|
422
|
+
await client.query(
|
|
423
|
+
`UPDATE survivor_pools
|
|
424
|
+
SET current_round = 1, round_deadline = NULL, status = 'open', updated_at = NOW()
|
|
425
|
+
WHERE id = $1`,
|
|
426
|
+
[poolId]
|
|
427
|
+
);
|
|
428
|
+
|
|
429
|
+
await client.query('COMMIT');
|
|
430
|
+
|
|
431
|
+
const message = deleteEntries
|
|
432
|
+
? 'Pool fully reset (entries deleted)'
|
|
433
|
+
: 'Pool reset to round 1 (entries kept)';
|
|
434
|
+
console.log(`[Simulator] ${message}`);
|
|
435
|
+
return { poolId, message, entriesDeleted: deleteEntries };
|
|
436
|
+
} catch (error) {
|
|
437
|
+
await client.query('ROLLBACK');
|
|
438
|
+
throw error;
|
|
439
|
+
} finally {
|
|
440
|
+
client.release();
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Activate pool and set to round 2 (skip First Four)
|
|
446
|
+
*/
|
|
447
|
+
async activatePool(poolId) {
|
|
448
|
+
await pool.query(
|
|
449
|
+
`UPDATE survivor_pools SET status = 'active', current_round = 2, updated_at = NOW() WHERE id = $1`,
|
|
450
|
+
[poolId]
|
|
451
|
+
);
|
|
452
|
+
|
|
453
|
+
console.log(`[Simulator] Pool ${poolId} activated at round 2`);
|
|
454
|
+
return { poolId, status: 'active', currentRound: 2 };
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Delete test entries (cleanup)
|
|
459
|
+
*/
|
|
460
|
+
async deleteTestEntries(poolId) {
|
|
461
|
+
const result = await pool.query(
|
|
462
|
+
`DELETE FROM survivor_entries
|
|
463
|
+
WHERE pool_id = $1 AND wallet_address LIKE 'TestWallet%'
|
|
464
|
+
RETURNING id`,
|
|
465
|
+
[poolId]
|
|
466
|
+
);
|
|
467
|
+
|
|
468
|
+
console.log(`[Simulator] Deleted ${result.rowCount} test entries from pool ${poolId}`);
|
|
469
|
+
return { poolId, deletedCount: result.rowCount };
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
module.exports = new SurvivorSimulator();
|
|
474
|
+
module.exports.SurvivorSimulator = SurvivorSimulator;
|
|
475
|
+
module.exports.MOCK_TEAMS = MOCK_TEAMS;
|