Commit ae663aab authored by Mahmoud Aglan's avatar Mahmoud Aglan

feat: database-driven data — bots from API, games from game_plugins, config from system_config

- New /api/bots.php: proxies Stockfish API /api/chess/bots with 5min cache
- New /api/games.php: reads game_plugins table (game list, time controls, config)
- New /api/config.php: serves system_config (reward amounts, matchmaking, etc.)
- pages/bots.php: fetches bot list dynamically from API (names, ratings, styles)
- pages/games.php: renders game grid from game_plugins DB table
- pages/play.php: populates bot dropdown from API
- pages/game.php: loads bot name from API instead of hardcoded map
- pages/home.php: loads reward config from API, bot names for recent games
- pages/ludo.php: loads difficulty options from game config
- api/daily-reward.php: reads reward amounts from system_config table
- analysis.js: fetches bot names from API cache
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent 1e652731
<?php
require_once __DIR__ . '/../config/constants.php';
header('Content-Type: application/json');
$cacheFile = sys_get_temp_dir() . '/el3ab_bots_cache.json';
$cacheTTL = 300; // 5 minutes
if (file_exists($cacheFile) && (time() - filemtime($cacheFile)) < $cacheTTL) {
echo file_get_contents($cacheFile);
exit;
}
$ch = curl_init(STOCKFISH_API . '/api/chess/bots');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-Key: ' . STOCKFISH_MGMT_KEY
]);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 200 && $response) {
file_put_contents($cacheFile, $response);
echo $response;
} else {
if (file_exists($cacheFile)) {
echo file_get_contents($cacheFile);
} else {
http_response_code(502);
echo json_encode(['error' => 'bot_service_unavailable']);
}
}
<?php
require_once __DIR__ . '/../config/database.php';
header('Content-Type: application/json');
$method = $_SERVER['REQUEST_METHOD'];
if ($method === 'GET') {
$category = $_GET['category'] ?? '';
$endpoint = 'system_config?select=key,value,category&is_secret=eq.false';
if ($category) {
$endpoint .= '&category=eq.' . urlencode($category);
}
$res = supabase_rest('GET', $endpoint);
$config = [];
if (!empty($res['data'])) {
foreach ($res['data'] as $row) {
$config[$row['key']] = json_decode($row['value'], true) ?? $row['value'];
}
}
echo json_encode(['config' => $config]);
} else {
http_response_code(405);
echo json_encode(['error' => 'method not allowed']);
}
......@@ -32,7 +32,18 @@ if ($method === 'POST') {
$yesterday = date('Y-m-d', strtotime('-1 day'));
$streak = ($lastClaim === $yesterday) ? ($profile['daily_streak'] ?? 0) + 1 : 1;
$reward = 50 + ($streak - 1) * 10;
// Load reward config from system_config
$cfgRes = supabase_rest('GET', 'system_config?select=key,value&key=in.(daily_reward_base,daily_reward_streak_bonus)');
$rewardBase = 50;
$rewardBonus = 10;
if (!empty($cfgRes['data'])) {
foreach ($cfgRes['data'] as $cfg) {
if ($cfg['key'] === 'daily_reward_base') $rewardBase = (int)$cfg['value'];
if ($cfg['key'] === 'daily_reward_streak_bonus') $rewardBonus = (int)$cfg['value'];
}
}
$reward = $rewardBase + ($streak - 1) * $rewardBonus;
$newCoins = ($profile['coins'] ?? 0) + $reward;
supabase_rest('PATCH', "profiles?id=eq.{$profile['id']}", [
......
<?php
require_once __DIR__ . '/../config/database.php';
header('Content-Type: application/json');
$method = $_SERVER['REQUEST_METHOD'];
if ($method === 'GET') {
$res = supabase_rest('GET', 'game_plugins?select=game_key,name,name_ar,description_ar,icon_url,banner_url,is_enabled,is_beta,min_players,max_players,supports_ranked,supports_bot,default_time_controls,config,sort_order&order=sort_order.asc');
$games = $res['data'] ?? [];
echo json_encode(['games' => $games]);
} else {
http_response_code(405);
echo json_encode(['error' => 'method not allowed']);
}
<?php $pageTitle = 'EL3AB - البوتات'; ?>
<?php require __DIR__ . '/../includes/header.php'; ?>
<div class="space-y-6">
<div class="lobby-page">
<div class="text-center">
<h2 style="font-size:22px;font-weight:700;">اختر خصمك</h2>
<p class="text-muted text-sm">7 بوتات بمستويات مختلفة</p>
</div>
<a href="/play" class="breadcrumb">
<svg class="icon" style="width:14px;height:14px;"><use href="/public/icons/sprite.svg#icon-arrow-right"></use></svg>
العب شطرنج
</a>
<div class="space-y-3" id="bots-grid">
<?php
$bots = [
['id' => 'amina', 'name' => 'امينة', 'level' => 'مبتدئة', 'elo' => 800, 'letter' => 'A', 'gradient' => 'var(--bot-amina)', 'bar' => 11, 'barColor' => 'var(--success)'],
['id' => 'tarek', 'name' => 'طارق', 'level' => 'هاوي', 'elo' => 1000, 'letter' => 'T', 'gradient' => 'var(--bot-tarek)', 'bar' => 25, 'barColor' => 'var(--success)'],
['id' => 'nour', 'name' => 'نور', 'level' => 'متوسطة', 'elo' => 1200, 'letter' => 'N', 'gradient' => 'var(--bot-nour)', 'bar' => 40, 'barColor' => 'var(--warning)'],
['id' => 'omar', 'name' => 'عمر', 'level' => 'جيد', 'elo' => 1400, 'letter' => 'O', 'gradient' => 'var(--bot-omar)', 'bar' => 55, 'barColor' => 'var(--warning)'],
['id' => 'layla', 'name' => 'ليلى', 'level' => 'قوية', 'elo' => 1600, 'letter' => 'L', 'gradient' => 'var(--bot-layla)', 'bar' => 70, 'barColor' => 'var(--error)'],
['id' => 'ziad', 'name' => 'زياد', 'level' => 'خبير', 'elo' => 1800, 'letter' => 'Z', 'gradient' => 'var(--bot-ziad)', 'bar' => 85, 'barColor' => 'var(--error)'],
['id' => 'grandmaster', 'name' => 'الاستاذ الكبير', 'level' => 'جراند ماستر', 'elo' => 2200, 'letter' => 'GM', 'gradient' => 'var(--bot-gm)', 'bar' => 100, 'barColor' => 'var(--purple)'],
];
foreach ($bots as $bot): ?>
<div class="card card-hover bot-card" data-bot="<?= $bot['id'] ?>" data-elo="<?= $bot['elo'] ?>">
<div class="card-body" style="display:flex;align-items:center;gap:16px;">
<img src="https://stockfishapi.caprover.al-arcade.com/portraits/<?= $bot['id'] ?>.jpg"
class="avatar" style="object-fit:cover;"
onerror="this.style.display='none';this.nextElementSibling.style.display='flex';">
<div class="avatar" style="display:none;background:linear-gradient(135deg,<?= $bot['gradient'] ?>);color:var(--text-1);font-weight:700;font-size:18px;align-items:center;justify-content:center;"><?= $bot['letter'] ?></div>
<div style="flex:1;">
<p style="font-size:16px;font-weight:600;"><?= $bot['name'] ?></p>
<p class="text-muted text-xs"><?= $bot['level'] ?> - ELO <?= $bot['elo'] ?></p>
<div class="stat-bar" style="margin-top:6px;"><div class="stat-bar-fill" style="width:<?= $bot['bar'] ?>%;background:<?= $bot['barColor'] ?>;"></div></div>
</div>
<svg class="icon" style="color:var(--text-3);transform:scaleX(-1)"><use href="/public/icons/sprite.svg#icon-arrow-right"></use></svg>
</div>
</div>
<?php endforeach; ?>
<div class="text-center" style="margin-top:16px;">
<h2 class="lobby-title">اختر خصمك</h2>
<p class="text-muted text-sm" id="bots-subtitle">جاري التحميل...</p>
</div>
<div class="lobby-cards" id="bots-grid">
<div class="skeleton skeleton-card" style="height:80px;"></div>
<div class="skeleton skeleton-card" style="height:80px;"></div>
<div class="skeleton skeleton-card" style="height:80px;"></div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
document.addEventListener('DOMContentLoaded', async () => {
if (!App.isLoggedIn()) {
window.location.href = '/login';
return;
}
document.querySelectorAll('.bot-card').forEach(card => {
card.style.cursor = 'pointer';
card.addEventListener('click', () => {
const bot = card.dataset.bot;
const color = Math.random() < 0.5 ? 'w' : 'b';
window.location.href = '/game?bot=' + bot + '&color=' + color + '&time=600&inc=0&rated=true';
const grid = document.getElementById('bots-grid');
const subtitle = document.getElementById('bots-subtitle');
try {
const data = await App.cachedFetch('/api/bots.php', 60000);
if (!data || !data.bots) throw new Error('no data');
const bots = data.bots;
subtitle.textContent = bots.length + ' بوتات بمستويات مختلفة';
const barColors = {
amina: 'var(--success)',
tarek: 'var(--success)',
nour: 'var(--warning)',
omar: 'var(--warning)',
layla: 'var(--error)',
ziad: 'var(--error)',
grandmaster: 'var(--purple)'
};
grid.innerHTML = bots.map((bot, i) => {
const barPct = Math.round(((i + 1) / bots.length) * 100);
const barColor = barColors[bot.id] || 'var(--cyan)';
const avgElo = Math.round((bot.elo_min + bot.elo_max) / 2);
const portraitUrl = 'https://stockfishapi.caprover.al-arcade.com' + bot.portrait_url;
const letter = bot.id === 'grandmaster' ? 'GM' : bot.name.charAt(0).toUpperCase();
return '<div class="card lobby-card card-hover bot-card" data-bot="' + bot.id + '" data-elo="' + avgElo + '" style="cursor:pointer;">' +
'<div class="card-body lobby-card-row">' +
'<img src="' + portraitUrl + '" class="avatar" style="object-fit:cover;" ' +
'onerror="this.style.display=\'none\';this.nextElementSibling.style.display=\'flex\';">' +
'<div class="avatar" style="display:none;background:linear-gradient(135deg,var(--bot-' + bot.id + '));color:var(--text-1);font-weight:700;font-size:18px;align-items:center;justify-content:center;">' + letter + '</div>' +
'<div style="flex:1;">' +
'<p style="font-size:16px;font-weight:600;">' + bot.name_ar + '</p>' +
'<p class="text-muted text-xs">' + bot.style_ar + ' - ELO ' + avgElo + '</p>' +
'<div class="stat-bar" style="margin-top:6px;"><div class="stat-bar-fill" style="width:' + barPct + '%;background:' + barColor + ';"></div></div>' +
'</div>' +
'<svg class="icon" style="color:var(--text-3);transform:scaleX(-1)"><use href="/public/icons/sprite.svg#icon-arrow-right"></use></svg>' +
'</div>' +
'</div>';
}).join('');
grid.querySelectorAll('.bot-card').forEach(card => {
card.addEventListener('click', () => {
const bot = card.dataset.bot;
const color = Math.random() < 0.5 ? 'w' : 'b';
window.location.href = '/game?bot=' + bot + '&color=' + color + '&time=600&inc=0&rated=true';
});
});
});
} catch (e) {
subtitle.textContent = 'تعذر تحميل البوتات';
grid.innerHTML = '<div class="empty-state"><p>حدث خطأ في تحميل البوتات. حاول مرة أخرى.</p></div>';
}
});
</script>
......
......@@ -185,16 +185,26 @@ document.addEventListener('DOMContentLoaded', () => {
const inc = parseInt(params.get('inc') || '0');
const rated = params.get('rated') !== 'false';
const botNames = {
amina: 'امينة', tarek: 'طارق', nour: 'نور',
omar: 'عمر', layla: 'ليلى', ziad: 'زياد', grandmaster: 'الاستاذ'
};
document.getElementById('opponent-name').textContent = botNames[botId] || botId;
const avatarEl = document.getElementById('opponent-avatar');
avatarEl.innerHTML = '<img src="https://stockfishapi.caprover.al-arcade.com/portraits/' + botId + '.jpg" alt="' + botId + '" style="width:100%;height:100%;border-radius:50%;object-fit:cover;">';
// Load bot name from API
(async () => {
try {
const data = await App.cachedFetch('/api/bots.php', 60000);
if (data && data.bots) {
const bot = data.bots.find(b => b.id === botId);
if (bot) {
document.getElementById('opponent-name').textContent = bot.name_ar.split(' ')[0];
} else {
document.getElementById('opponent-name').textContent = botId;
}
}
} catch (e) {
document.getElementById('opponent-name').textContent = botId;
}
})();
Game.start({ botId, color, time, increment: inc, rated });
});
</script>
......
......@@ -8,107 +8,94 @@
<p class="text-muted text-sm">اختر لعبتك المفضلة وابدأ</p>
</div>
<div class="games-grid">
<!-- Chess -->
<div class="game-card card-accent" data-game="chess">
<div class="game-card-cover game-card-cover--chess">
<svg class="game-card-icon"><use href="/public/icons/sprite.svg#icon-play"></use></svg>
</div>
<div class="game-card-info">
<div class="game-card-meta">
<span class="game-card-name">شطرنج</span>
<span class="online-badge" id="online-chess"><span class="online-dot"></span> --</span>
</div>
<a href="/play" class="btn btn-cyan btn-sm game-card-play">العب</a>
</div>
</div>
<!-- Ludo -->
<div class="game-card card-accent" data-game="ludo">
<div class="game-card-cover game-card-cover--ludo">
<svg class="game-card-icon"><use href="/public/icons/sprite.svg#icon-ludo"></use></svg>
</div>
<div class="game-card-info">
<div class="game-card-meta">
<span class="game-card-name">لودو</span>
<span class="online-badge" id="online-ludo"><span class="online-dot"></span> --</span>
</div>
<a href="/ludo" class="btn btn-cyan btn-sm game-card-play">العب</a>
</div>
</div>
<!-- Coming Soon: Dominoes -->
<div class="game-card game-card--soon">
<div class="game-card-cover game-card-cover--domino">
<svg class="game-card-icon"><use href="/public/icons/sprite.svg#icon-domino"></use></svg>
<span class="game-card-badge">قريبا</span>
</div>
<div class="game-card-info">
<div class="game-card-meta">
<span class="game-card-name">دومينو</span>
</div>
</div>
</div>
<!-- Coming Soon: Backgammon -->
<div class="game-card game-card--soon">
<div class="game-card-cover game-card-cover--backgammon">
<svg class="game-card-icon"><use href="/public/icons/sprite.svg#icon-backgammon"></use></svg>
<span class="game-card-badge">قريبا</span>
</div>
<div class="game-card-info">
<div class="game-card-meta">
<span class="game-card-name">طاولة</span>
</div>
</div>
</div>
<!-- Coming Soon: Trix -->
<div class="game-card game-card--soon">
<div class="game-card-cover game-card-cover--trix">
<svg class="game-card-icon"><use href="/public/icons/sprite.svg#icon-cards"></use></svg>
<span class="game-card-badge">قريبا</span>
</div>
<div class="game-card-info">
<div class="game-card-meta">
<span class="game-card-name">تركس</span>
</div>
</div>
</div>
<!-- Coming Soon: Baloot -->
<div class="game-card game-card--soon">
<div class="game-card-cover game-card-cover--baloot">
<svg class="game-card-icon"><use href="/public/icons/sprite.svg#icon-cards"></use></svg>
<span class="game-card-badge">قريبا</span>
</div>
<div class="game-card-info">
<div class="game-card-meta">
<span class="game-card-name">بلوت</span>
</div>
</div>
</div>
<div class="games-grid" id="games-grid">
<div class="skeleton skeleton-card" style="height:140px;border-radius:var(--radius-lg);"></div>
<div class="skeleton skeleton-card" style="height:140px;border-radius:var(--radius-lg);"></div>
<div class="skeleton skeleton-card" style="height:140px;border-radius:var(--radius-lg);"></div>
<div class="skeleton skeleton-card" style="height:140px;border-radius:var(--radius-lg);"></div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
document.addEventListener('DOMContentLoaded', async () => {
if (!App.isLoggedIn()) {
window.location.href = '/login';
return;
}
document.querySelectorAll('.game-card[data-game]').forEach(card => {
card.addEventListener('click', (e) => {
if (e.target.closest('.game-card-play')) return;
const game = card.dataset.game;
if (game === 'chess') window.location.href = '/play';
else if (game === 'ludo') window.location.href = '/ludo';
const grid = document.getElementById('games-grid');
const gameRoutes = {
chess: '/play',
ludo: '/ludo'
};
const gameIcons = {
chess: 'icon-play',
ludo: 'icon-ludo',
dominoes: 'icon-domino',
backgammon: 'icon-backgammon',
trix: 'icon-cards',
baloot: 'icon-cards',
trivia: 'icon-trophy'
};
const gameCoverClasses = {
chess: 'game-card-cover--chess',
ludo: 'game-card-cover--ludo',
dominoes: 'game-card-cover--domino',
backgammon: 'game-card-cover--backgammon',
trix: 'game-card-cover--trix',
baloot: 'game-card-cover--baloot',
trivia: 'game-card-cover--trix'
};
try {
const data = await App.cachedFetch('/api/games.php', 60000);
if (!data || !data.games) throw new Error('no data');
const games = data.games;
grid.innerHTML = games.map(game => {
const route = gameRoutes[game.game_key];
const isPlayable = route && game.is_enabled;
const icon = gameIcons[game.game_key] || 'icon-play';
const coverClass = gameCoverClasses[game.game_key] || 'game-card-cover--chess';
const cardClass = isPlayable ? 'game-card card-accent' : 'game-card game-card--soon';
let html = '<div class="' + cardClass + '" data-game="' + game.game_key + '">';
html += '<div class="game-card-cover ' + coverClass + '">';
html += '<svg class="game-card-icon"><use href="/public/icons/sprite.svg#' + icon + '"></use></svg>';
if (!isPlayable) {
html += '<span class="game-card-badge">قريبا</span>';
}
html += '</div>';
html += '<div class="game-card-info">';
html += '<div class="game-card-meta">';
html += '<span class="game-card-name">' + game.name_ar + '</span>';
if (isPlayable) {
html += '<span class="online-badge" id="online-' + game.game_key + '"><span class="online-dot"></span> --</span>';
}
html += '</div>';
if (isPlayable) {
html += '<a href="' + route + '" class="btn btn-cyan btn-sm game-card-play">العب</a>';
}
html += '</div></div>';
return html;
}).join('');
grid.querySelectorAll('.game-card[data-game]').forEach(card => {
card.addEventListener('click', (e) => {
if (e.target.closest('.game-card-play')) return;
const key = card.dataset.game;
if (gameRoutes[key]) window.location.href = gameRoutes[key];
});
});
});
} catch (e) {
grid.innerHTML = '<div class="empty-state" style="grid-column:1/-1;"><p>حدث خطأ في تحميل الالعاب</p></div>';
}
});
</script>
......
......@@ -62,7 +62,14 @@ document.addEventListener('DOMContentLoaded', async () => {
document.getElementById('home-subtitle').textContent = 'المستوى ' + (p.level || 1) + ' • ' + (p.elo_blitz || 1200) + ' بليتز';
const streak = p.daily_streak || 0;
document.getElementById('home-streak').textContent = 'اليوم ' + streak;
document.getElementById('home-streak-reward').textContent = '+' + (50 + streak * 10) + ' عملة';
// Load reward config from API
App.cachedFetch('/api/config.php?category=economy', 120000).then(cfg => {
const base = (cfg && cfg.config && cfg.config.daily_reward_base) || 50;
const bonus = (cfg && cfg.config && cfg.config.daily_reward_streak_bonus) || 10;
document.getElementById('home-streak-reward').textContent = '+' + (base + streak * bonus) + ' عملة';
}).catch(() => {
document.getElementById('home-streak-reward').textContent = '+' + (50 + streak * 10) + ' عملة';
});
// Update streak calendar strip
const claimedToday = p.last_daily_claim === new Date().toISOString().slice(0, 10);
......@@ -85,16 +92,24 @@ document.addEventListener('DOMContentLoaded', async () => {
}
}
const gamesData = await App.fetch('/api/game?action=recent');
const [gamesData, botsData] = await Promise.all([
App.fetch('/api/game?action=recent'),
App.cachedFetch('/api/bots.php', 60000)
]);
const botMap = {};
if (botsData && botsData.bots) {
botsData.bots.forEach(b => { botMap[b.id] = b.name_ar.split(' ')[0]; });
}
if (gamesData && gamesData.games && gamesData.games.length > 0) {
const container = document.getElementById('home-recent-games');
container.innerHTML = gamesData.games.slice(0, 5).map(g => {
const resultClass = g.result === 'win' ? 'color:var(--success)' : g.result === 'loss' ? 'color:var(--error)' : 'color:var(--text-3)';
const resultText = g.result === 'win' ? 'فوز' : g.result === 'loss' ? 'خسارة' : 'تعادل';
const resultIcon = g.result === 'win' ? 'check' : g.result === 'loss' ? 'x' : 'clock';
const botName = botMap[g.bot_id] || g.bot_id || '?';
return '<div style="display:flex;align-items:center;gap:12px;padding:10px 16px;border-bottom:1px solid var(--border);">' +
'<svg class="icon" style="' + resultClass + '"><use href="/public/icons/sprite.svg#icon-' + resultIcon + '"></use></svg>' +
'<div style="flex:1;"><p style="font-size:13px;font-weight:600;">ضد ' + (g.bot_id || '?') + '</p></div>' +
'<div style="flex:1;"><p style="font-size:13px;font-weight:600;">ضد ' + botName + '</p></div>' +
'<span class="badge" style="' + resultClass + '">' + resultText + '</span>' +
'</div>';
}).join('');
......
......@@ -54,6 +54,7 @@
<div>
<label class="input-label">الصعوبة</label>
<div class="tab-group" id="bot-diff-tabs">
<!-- Loaded from API, fallback below -->
<button class="tab active" data-diff="easy">سهل</button>
<button class="tab" data-diff="hard">صعب</button>
</div>
......@@ -126,6 +127,24 @@ document.addEventListener('DOMContentLoaded', function() {
});
});
});
// Load ludo config from games API
App.cachedFetch('/api/games.php', 60000).then(function(data) {
if (!data || !data.games) return;
var ludo = data.games.find(function(g) { return g.game_key === 'ludo'; });
if (!ludo || !ludo.config || !ludo.config.difficulties) return;
var diffTabs = document.getElementById('bot-diff-tabs');
if (!diffTabs) return;
diffTabs.innerHTML = ludo.config.difficulties.map(function(d, i) {
return '<button class="tab' + (i === 0 ? ' active' : '') + '" data-diff="' + d.id + '">' + d.label + '</button>';
}).join('');
diffTabs.querySelectorAll('.tab').forEach(function(tab) {
tab.addEventListener('click', function() {
diffTabs.querySelectorAll('.tab').forEach(function(t) { t.classList.remove('active'); });
tab.classList.add('active');
});
});
}).catch(function() {});
});
function startLocal() {
......
......@@ -100,13 +100,7 @@
<div>
<label class="input-label">الخصم</label>
<select class="input" id="bot-select" style="direction:ltr;">
<option value="amina">Amina (800)</option>
<option value="tarek">Tarek (1000)</option>
<option value="nour" selected>Nour (1200)</option>
<option value="omar">Omar (1400)</option>
<option value="layla">Layla (1600)</option>
<option value="ziad">Ziad (1800)</option>
<option value="grandmaster">Grandmaster (2200)</option>
<option value="nour">جاري التحميل...</option>
</select>
</div>
......@@ -130,7 +124,7 @@
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
document.addEventListener('DOMContentLoaded', async () => {
if (!App.isLoggedIn()) {
window.location.href = '/login';
return;
......@@ -144,6 +138,20 @@ document.addEventListener('DOMContentLoaded', () => {
});
});
});
// Load bots from API
try {
const data = await App.cachedFetch('/api/bots.php', 60000);
if (data && data.bots) {
const select = document.getElementById('bot-select');
select.innerHTML = data.bots.map(bot => {
const avgElo = Math.round((bot.elo_min + bot.elo_max) / 2);
const selected = bot.id === 'nour' ? ' selected' : '';
return '<option value="' + bot.id + '"' + selected + '>' + bot.name + ' (' + avgElo + ')</option>';
}).join('');
window._botsData = data.bots;
}
} catch (e) {}
});
function getSelectedTime() {
......@@ -167,7 +175,9 @@ function startMultiplayer() {
}
function startQuickMatch() {
const bots = ['amina','tarek','nour','omar','layla','ziad'];
const bots = window._botsData
? window._botsData.filter(b => b.id !== 'grandmaster').map(b => b.id)
: ['nour'];
const bot = bots[Math.floor(Math.random() * bots.length)];
const color = Math.random() < 0.5 ? 'w' : 'b';
window.location.href = '/game?bot=' + bot + '&color=' + color + '&time=300&inc=0&rated=true';
......
......@@ -186,14 +186,19 @@ const Analysis = {
renderUI() {
const game = this.gameData;
// Player names
const botNames = {
amina: 'امينة', tarek: 'طارق', nour: 'نور',
omar: 'عمر', layla: 'ليلى', ziad: 'زياد', grandmaster: 'الاستاذ'
};
// Player names - load from API cache if available
document.getElementById('analysis-player-name').textContent = 'انت';
document.getElementById('analysis-opponent-name').textContent = botNames[game.bot_id] || game.bot_id || 'الخصم';
const opponentEl = document.getElementById('analysis-opponent-name');
opponentEl.textContent = game.bot_id || 'الخصم';
if (game.bot_id && typeof App !== 'undefined') {
App.cachedFetch('/api/bots.php', 60000).then(data => {
if (data && data.bots) {
const bot = data.bots.find(b => b.id === game.bot_id);
if (bot) opponentEl.textContent = bot.name_ar.split(' ')[0];
}
}).catch(() => {});
}
// Opening detection
const opening = this.detectOpening();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment