إلى الطاولة من جديد بلمسة واحدة
شاشة النتيجة تستطيع الآن بدء المباراة نفسها من جديد، و«مباراة سريعة» تضعك في غرفة تمتلئ بالفعل بدل أن تفتح غرفة فارغة أخرى.

بكلمات بسيطة
أفضل لحظة لبدء مباراة هي الثانية التي تنتهي فيها المباراة السابقة. كان الدليل بين أيدينا وكنا نهدره: من بين اللاعبين الذين عادوا لمباراة ثانية، بدأ أكثر من نصفهم خلال دقيقتين من انتهاء الأولى — وكل ما قدّمناه لهم في تلك اللحظة كان رابطًا إلى الردهة، حيث عليهم اختيار نمط وعدد مقاعد وإنشاء غرفة ثم الانتظار.
الآن في شاشة النتيجة زر «العب مرة أخرى». لمسة واحدة وأنت في مباراة جديدة: النمط نفسه، وعدد المقاعد نفسه، والمقاعد الفارغة ممتلئة سلفًا، والمباراة قد بدأت فعلًا. لا ردهة، ولا شيء لتضبطه.

«مباراة سريعة» هي النصف الآخر من المشكلة نفسها، في الردهة. كان زرًا لا يفعل شيئًا. الآن يضعك في أفضل غرفة مفتوحة من النمط الذي اخترته: غرفة يستضيفها شخص حقيقي أولًا، ثم الأقرب إلى الانطلاق، ثم التي انتظرت أطول مدة. ولا يفتح لك غرفة إلا إذا لم يكن هناك فعلًا ما تنضم إليه.
للمهتمين بالتفاصيل التقنية
إعادة المباراة لا تضيف أي نقطة اتصال جديدة على الخادم. إنها تفعل بالضبط ما يفعله اللاعب بيده — إنشاء الغرفة، وإجلاس البوتات واحدًا واحدًا (فالنداء يختار المقعد الحر التالي، ولو جرى بالتوازي لتنازعت النداءات المقعد نفسه)، ثم بدء المباراة — وكل خطوة تُنفَّذ بصلاحيات اللاعب، فتنطبق هنا كل القواعد التي تنطبق على غرفة تنشئها بنفسك.
وهذا مهم بسبب من يضغط الزر. تسعة من كل عشرة انصرافات من المباراة الأولى هي استسلام لا إفلاس، فالحالة المعتادة لاعب يترك مباراة لا تزال جارية لبقية اللاعبين. وهي تعمل لأن من خرج من مباراة لم يُعد محسوبًا داخلها — قاعدة شُحنت لسبب آخر قبل عام، وهي الآن ما يجعل زر إعادة المباراة مشروعًا.
تُبقي «مباراة سريعة» قرارها داخل دالة خالصة، فيمكن اختبارها بلا ردهة ولا قاعدة بيانات. والنمط مرشِّح لا تفضيل: أن تُلقى في مباراة كلاسيكية من نصف ساعة بعد اختيارك بليتز أسوأ من أن تنتظر لحظة.
// lib/lobby/quickMatch.ts — which open room to try, in order
.sort((a, b) => {
// 1. a human host: pool the people who are actually online
if (a.creator_is_bot !== b.creator_is_bot) return a.creator_is_bot ? 1 : -1;
// 2. fewest free seats: a nearly-full room starts sooner
const freeA = a.max_players - a.player_count;
const freeB = b.max_players - b.player_count;
if (freeA !== freeB) return freeA - freeB;
// 3. oldest: whoever has waited longest gets company first
return Date.parse(a.created_at) - Date.parse(b.created_at);
});تُعاد القائمة المرتبة كاملة بدل اختيار واحد، لأن أعداد المقاعد مجرد لقطة لحظية: يسير المنادي في القائمة حتى ينجح انضمام فعلي، وهذا ما يمنع شخصين يتنازعان المقعد الأخير من أن ينتهي بهما الأمر وحيدين.
