Commit 1f8152ac authored by Mahmoud Aglan's avatar Mahmoud Aglan

fix(tournaments): bot testing can no longer touch a real tournament

Populate used to attach a new Swiss-service tournament to whatever it was
pointed at, which switched a game-run tournament to the other engine, and
registered bots with no profile that real players were then paired against.
Population is refused once a real player has registered or the tournament
has left draft, and simulation while any real player is entered, unless a
superadmin ticks an explicit test override. Game-run test tournaments are
simulated natively: start through the game, then results drawn from the
bots' ratings are recorded through the game's arbiter endpoint and the
engine pairs the next round itself. bot_metadata is stored as jsonb, not a
JSON string.
Co-Authored-By: 's avatarClaude Opus 5.5 <noreply@anthropic.com>
parent aa025be9
......@@ -10,6 +10,15 @@ $bots = Database::getInstance()->select('tournament_registrations', [
'select' => 'id',
]);
$botCount = count($bots);
$realCount = Database::getInstance()->count('tournament_registrations', [
'tournament_id' => "eq.{$tournament['id']}",
'is_bot' => 'eq.false',
'status' => 'eq.registered',
]);
$canOverride = Auth::role() === 'superadmin';
$populateBlocked = $realCount > 0 || $tournament['status'] !== 'draft';
$simulateBlocked = $realCount > 0;
$botNative = empty($tournament['swiss_api_tournament_id']);
$totalRounds = $tournament['rounds_total'] ?? $tournament['swiss_rounds'] ?? 7;
$currentRound = $tournament['current_round'] ?? 0;
$hasActiveRound = false;
......@@ -35,11 +44,28 @@ if (!empty($rounds)) {
</div>
<div class="p-4">
<p class="text-sm text-muted mb-3">للبطولات التجريبية فقط. البوتات لا حساب لها ولا تلعب مباريات حقيقية؛ النتائج تُولّد من تقييماتها<?= $botNative ? ' وتُسجل عبر أدوات الحكم في التطبيق.' : '.' ?></p>
<?php if ($populateBlocked || $simulateBlocked): ?>
<p class="text-sm text-warning mb-3">
<?= $realCount > 0
? "في البطولة {$realCount} لاعب حقيقي مسجل، لذلك إضافة البوتات والمحاكاة مقفلة."
: 'إضافة البوتات متاحة لبطولة في حالة المسودة فقط.' ?>
<?= $canOverride ? 'يمكن لمدير النظام تجاوز الحماية لبطولة اختبار بتفعيل الخيار أدناه.' : '' ?>
</p>
<?php endif; ?>
<?php if ($canOverride): ?>
<label class="flex items-center gap-2 text-sm mb-4">
<input type="checkbox" id="botTestOverride">
<span>تجاوز الحماية: أؤكد أن هذه بطولة اختبار</span>
</label>
<?php endif; ?>
<!-- Section 1: Populate -->
<div class="mb-5">
<h4 class="text-sm font-medium mb-3">ملء البطولة بلاعبين وهميين</h4>
<form method="POST" action="/tournaments/<?= $tournament['id'] ?>/bots/populate">
<form method="POST" action="/tournaments/<?= $tournament['id'] ?>/bots/populate" onsubmit="this.test_override.value = (document.getElementById('botTestOverride') || {}).checked ? '1' : ''">
<input type="hidden" name="_csrf" value="<?= Auth::csrfToken() ?>">
<input type="hidden" name="test_override" value="">
<div class="flex gap-3 items-end flex-wrap">
<div class="form-group" style="margin-bottom:0;width:120px;">
<label class="form-label">العدد</label>
......@@ -79,11 +105,11 @@ if (!empty($rounds)) {
<?php endif; ?>
</div>
<?php if ($tournament['status'] === 'in_progress'): ?>
<?php if ($tournament['status'] === 'in_progress' || ($botNative && $botCount > 0 && in_array($tournament['status'], ['draft', 'registration'], true))): ?>
<hr style="border-color:var(--border-color);margin:16px 0;">
<!-- Section 2: Auto-Play Round -->
<?php if ($hasActiveRound): ?>
<?php if ($hasActiveRound && $tournament['status'] === 'in_progress'): ?>
<div class="mb-5">
<h4 class="text-sm font-medium mb-3">تشغيل الجولة الحالية تلقائياً</h4>
<div class="flex gap-3 items-end flex-wrap">
......@@ -107,10 +133,12 @@ if (!empty($rounds)) {
<label class="form-label">نسبة التعادل %</label>
<input type="number" id="simDrawRate" class="form-input" value="15" min="0" max="50" step="5">
</div>
<?php if (!$botNative): ?>
<label class="checkbox-label" style="display:flex;align-items:center;gap:8px;padding-top:20px;">
<input type="checkbox" id="playActualCheckbox">
<span style="font-size:13px;">لعب مباريات حقيقية بالمحرك (أبطأ ≈ ٥ دقائق/جولة)</span>
<span style="font-size:13px;">لعب مباريات حقيقية بالمحرك (أبطأ، نحو ٥ دقائق للجولة)</span>
</label>
<?php endif; ?>
<button type="button" class="btn btn-primary" id="simulateBtn" onclick="simulateFullTournament()">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="5 3 19 12 5 21 5 3"/></svg>
محاكاة البطولة بالكامل
......@@ -143,7 +171,7 @@ function autoPlayCurrentRound() {
fetch('/tournaments/<?= $tournament['id'] ?>/rounds/' + roundId + '/auto-play', {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: '_csrf=<?= Auth::csrfToken() ?>&draw_rate=' + drawRate
body: '_csrf=<?= Auth::csrfToken() ?>&draw_rate=' + drawRate + botOverrideParam()
})
.then(r => r.json())
.then(data => {
......@@ -157,9 +185,15 @@ function autoPlayCurrentRound() {
.catch(e => alert('خطأ في الاتصال'));
}
function botOverrideParam() {
const box = document.getElementById('botTestOverride');
return box && box.checked ? '&test_override=1' : '';
}
function simulateFullTournament() {
const drawRate = document.getElementById('simDrawRate').value;
const playActual = document.getElementById('playActualCheckbox').checked;
const actualBox = document.getElementById('playActualCheckbox');
const playActual = actualBox ? actualBox.checked : false;
const totalRounds = <?= $totalRounds ?>;
const msg = playActual
......@@ -174,7 +208,7 @@ function simulateFullTournament() {
}
function runNextRound(drawRate, totalRounds, playActual) {
let body = '_csrf=<?= Auth::csrfToken() ?>&draw_rate=' + drawRate;
let body = '_csrf=<?= Auth::csrfToken() ?>&draw_rate=' + drawRate + botOverrideParam();
if (playActual) body += '&play_actual=1';
fetch('/tournaments/<?= $tournament['id'] ?>/simulate-round', {
......
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