Боты, которые добивают лобби за 10 секунд
У маленькой игры есть проблема курицы и яйца: онлайн интересен, когда есть игроки, а игроки приходят, когда онлайн интересен. Пока аудитория небольшая, живого соперника можно ждать минуту, а минуту ожидания в мобильной игре почти никто не терпит.
Решение банальное и честное: если за короткое время реальные соперники не набрались, добиваем стол ботами. Весь фокус в том, чтобы это ощущалось нормальной игрой, а не отпиской.
Правило десяти секунд
Игрок нажимает «играть» и попадает в лобби. Дальше сервер ждёт: если за 10 секунд собралось нужное число живых — стартуем сразу. Если нет — досаживаем ботов до полного состава и всё равно стартуем. Игрок почти никогда не ждёт дольше десяти секунд.
// упрощённо, на стороне сервера
setTimeout(() => {
const need = seats - lobby.humans.length;
for (let i = 0; i < need; i++) lobby.add(makeBot());
startMatch(lobby);
}, 10_000);
// а если живых и так набралось — стартуем раньше
function onJoin(p) {
lobby.humans.push(p);
if (lobby.humans.length === seats) startMatch(lobby);
}
Чтобы бот не выдавал себя
Технически посадить бота легко. Сложнее — сделать так, чтобы он не читался мгновенно. Что помогло мне:
- Задержка перед ходом. Живой человек думает. Бот, отвечающий за 5 мс, выглядит жутко. Я добавляю случайную паузу в 0,6–1,8 секунды, и притом чуть дольше — на «сложных» ситуациях.
- Обычные имена и аватары. Из того же набора, что у живых игроков. Никаких «Bot_04».
- Неидеальная игра. Бот, который не ошибается, узнаётся так же быстро, как бот, который ходит мгновенно. У меня у ботов есть параметр «вероятность ошибиться», привязанный к уровню сложности.
Цель бота — не выиграть, а сделать матч живым, пока в игре мало людей. Хороший бот проигрывает достаточно часто.
Тихая подмена на живого
Приятный бонус: если посреди матча с ботами подключается живой игрок, а кто-то из ботов ещё не «занял ключевую роль», сервер может отдать место человеку. Для остальных за столом ничего не меняется. Так лобби с одним живым игроком постепенно превращается в лобби с несколькими, и никто не замечает шва.
Этот механизм я вынес в тот самый маленький игровой сервер на Go, о котором упоминал на главной. Он ничего не знает про правила конкретной игры — только про лобби, места и таймеры, — поэтому переезжает из проекта в проект почти без правок.