| | #!/bin/bash |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | set -euo pipefail |
| |
|
| | MODE="${1:-backend}" |
| |
|
| | echo "╔══════════════════════════════════════════════════════════════╗" |
| | echo "║ ChessEcon — Multi-Agent Chess RL ║" |
| | echo "║ TextArena + Meta OpenEnv + GRPO | Hackathon 2026 ║" |
| | echo "╚══════════════════════════════════════════════════════════════╝" |
| | echo "" |
| | echo "Mode: $MODE" |
| | echo "Model: ${PLAYER_MODEL:-Qwen/Qwen2.5-0.5B-Instruct}" |
| | echo "RL Method: ${RL_METHOD:-grpo}" |
| | echo "" |
| |
|
| | |
| | check_env() { |
| | local var_name="$1" |
| | local required="${2:-false}" |
| | if [ -z "${!var_name:-}" ]; then |
| | if [ "$required" = "true" ]; then |
| | echo "ERROR: Required environment variable $var_name is not set." |
| | echo " Please set it in your .env file or Docker environment." |
| | exit 1 |
| | else |
| | echo "WARNING: Optional variable $var_name is not set." |
| | fi |
| | fi |
| | } |
| |
|
| | |
| | check_env "HF_TOKEN" "true" |
| |
|
| | |
| | if [ "${ENABLE_CLAUDE_COACHING:-true}" = "true" ]; then |
| | check_env "ANTHROPIC_API_KEY" "true" |
| | fi |
| |
|
| | |
| | MODEL_NAME="${PLAYER_MODEL:-Qwen/Qwen2.5-0.5B-Instruct}" |
| | MODEL_CACHE_DIR="/app/models/$(echo $MODEL_NAME | tr '/' '_')" |
| |
|
| | if [ ! -d "$MODEL_CACHE_DIR" ] || [ "${FORCE_DOWNLOAD:-false}" = "true" ]; then |
| | echo "Downloading model: $MODEL_NAME" |
| | echo "Cache directory: $MODEL_CACHE_DIR" |
| | python3 -c " |
| | from huggingface_hub import snapshot_download |
| | import os |
| | snapshot_download( |
| | repo_id='${MODEL_NAME}', |
| | local_dir='${MODEL_CACHE_DIR}', |
| | token=os.environ.get('HF_TOKEN'), |
| | ignore_patterns=['*.bin', '*.pt'] if os.environ.get('USE_SAFETENSORS', 'true') == 'true' else [] |
| | ) |
| | print('Model downloaded successfully.') |
| | " |
| | echo "Model ready at: $MODEL_CACHE_DIR" |
| | else |
| | echo "Model already cached at: $MODEL_CACHE_DIR" |
| | fi |
| |
|
| | export MODEL_LOCAL_PATH="$MODEL_CACHE_DIR" |
| |
|
| | |
| | case "$MODE" in |
| | backend) |
| | echo "" |
| | echo "Starting ChessEcon API server on port ${PORT:-8000}..." |
| | echo "Dashboard: http://localhost:${PORT:-8000}" |
| | echo "API docs: http://localhost:${PORT:-8000}/docs" |
| | echo "WebSocket: ws://localhost:${PORT:-8000}/ws" |
| | echo "" |
| | exec python3 -m uvicorn backend.main:app \ |
| | --host 0.0.0.0 \ |
| | --port "${PORT:-8000}" \ |
| | --workers "${WORKERS:-1}" \ |
| | --log-level "${LOG_LEVEL:-info}" |
| | ;; |
| |
|
| | train) |
| | echo "" |
| | echo "Starting RL training..." |
| | echo "Method: ${RL_METHOD:-grpo}" |
| | echo "Games per batch: ${GAMES_PER_BATCH:-8}" |
| | echo "Training steps: ${MAX_TRAINING_STEPS:-1000}" |
| | echo "" |
| | exec python3 -m training.run \ |
| | --method "${RL_METHOD:-grpo}" \ |
| | --model-path "$MODEL_LOCAL_PATH" \ |
| | --games-per-batch "${GAMES_PER_BATCH:-8}" \ |
| | --max-steps "${MAX_TRAINING_STEPS:-1000}" \ |
| | --output-dir "/app/data/training" \ |
| | --log-dir "/app/logs" |
| | ;; |
| |
|
| | selfplay) |
| | echo "" |
| | echo "Starting self-play data collection..." |
| | echo "Games: ${SELFPLAY_GAMES:-100}" |
| | echo "" |
| | exec python3 -m training.run \ |
| | --method selfplay \ |
| | --model-path "$MODEL_LOCAL_PATH" \ |
| | --games "${SELFPLAY_GAMES:-100}" \ |
| | --output-dir "/app/data/games" |
| | ;; |
| |
|
| | download) |
| | echo "Model download complete. Exiting." |
| | exit 0 |
| | ;; |
| |
|
| | demo) |
| | echo "" |
| | echo "Running 3-game demo..." |
| | exec python3 -c " |
| | import asyncio |
| | import sys |
| | sys.path.insert(0, '/app') |
| | from backend.chess.engine import ChessEngine |
| | from backend.economy.ledger import EconomicConfig, WalletManager, TournamentOrganizer |
| | |
| | async def run_demo(): |
| | config = EconomicConfig() |
| | wallets = WalletManager(config) |
| | wallets.create_wallet('white', 100.0) |
| | wallets.create_wallet('black', 100.0) |
| | organizer = TournamentOrganizer(config, wallets) |
| | |
| | for game_num in range(1, 4): |
| | print(f'\n--- Game {game_num} ---') |
| | engine = ChessEngine() |
| | game_id = organizer.open_game('white', 'black') |
| | print(f'Game ID: {game_id}') |
| | print(f'Prize pool: {organizer.games[game_id].prize_pool}') |
| | |
| | move_count = 0 |
| | while not engine.is_game_over() and move_count < 20: |
| | legal = engine.get_legal_moves() |
| | if not legal: |
| | break |
| | import random |
| | move = random.choice(legal) |
| | engine.make_move(move) |
| | move_count += 1 |
| | |
| | result = engine.get_result() or '1/2-1/2' |
| | winner = 'white' if result == '1-0' else ('black' if result == '0-1' else None) |
| | payout = organizer.close_game(game_id, winner) |
| | print(f'Result: {result} | White: {payout[\"white\"]:.1f} | Black: {payout[\"black\"]:.1f}') |
| | print(f'Wallets — White: {wallets.get_balance(\"white\"):.1f} | Black: {wallets.get_balance(\"black\"):.1f}') |
| | |
| | print('\nDemo complete.') |
| | |
| | asyncio.run(run_demo()) |
| | " |
| | ;; |
| |
|
| | *) |
| | echo "Unknown mode: $MODE" |
| | echo "Valid modes: backend | train | selfplay | download | demo" |
| | exit 1 |
| | ;; |
| | esac |
| |
|