Как определить игрока быстрее всех нажавшего кнопку (web)?

Ссылка скопирована
1 ответ

Добрый день!
Для одного пет-проекта возникла потребность реализовать справедливый/честный алгоритм определения самого быстрого нажатия на кнопку. В чем суть: есть несколько игроков, которые присоединены к одному серверу и в какой-то момент времени они все могут нажать кнопку и нужно определить, кто был первым.
Если бы был какой-то сигнал, то можно было бы вычислять разницу между подачей сигнала на браузере и нажатием кнопки в браузере и прикреплять эту информацию к сообщению, которое отправляется на сервер. На сервере же просто замерять разницу между временем отправки сигнала игрокам и времени ответа от каждого игрока. Получили бы "серверное" время и "клиентское" время и как-то от этого плясать.
Но если нажатие игрока может происходить в любой момент времени, то непонятно от чего измерять время на клиенте. Лучшее что я придумал, это посылать от сервера на клиент "временные метки" раз в 5-10 секунд, а в ответ на нажатие присылать разницу во времени между нажатием и получением последней временной метки.

Но может есть какие-то другие способы решения?

Дополнительно:

В общем случае все что происходит на клиенте - недостоверно. Я могу спокойно открыть код клиентской странички, внести любые правки и при поступлении сигнала на нажатие отправить мгновенный ответ, или задать любой интервал для отсылки ответа. Если это кому-то выгодно, это будет взломано за пару часов максимум.

  • ThunderCat, это понятно, поэтому есть и обычная скорость, замеренная на сервере. Вообще, нечестных игроков оставляем за скобками, т.к. следить за ними задачи не стоит.
  • Какой сигнал пришёл на сервер первым, тот первый и был, чего засекать то?
    Если у клиента медленный интернет и страница с кнопкой грузится 5 мин, то он не может по определению отреаагировать быстрее стабильного клиента. А если не стоит следить за нечестными игроками реализация вообще не важна.
  • Я вижу решение, близкое к желаемому.

    Сначала, при старте клиентского интерфейса, выполняется несколько (навскидку - может, и 10 достаточно, а, может, и 100 потребуется, следует определить экспериментально) передач с клиента на сервер, причём используется тот же протокол, который будет впоследствии использован для передачи информации о нажатии, и в каждой передаче в том числе передаётся точное время клиента (с максимально доступной точностью). Приходящие пакеты маркируются точным штампом времени получения (серверное время), с максимально доступной точностью. Полученный массив обрабатывается статистически, и определяется разница точного времени часов клиента и сервера и погрешность этого определения. Если статистическая ошибка велика, процесс повторяется.

    Далее, уже в момент нажатия, по той же технологии передаётся пакет с точным клиентским временем нажатия, и по подсчитанной ранее разности определяется точное время нажатия по времени сервера. Для всех клиентов это время сравнивается, и победителем объявляется тот, у кого время нажатия по серверу минимальное.

    Такой алгоритм (практически) не зависит от колебания времени доставки пакета от клиента серверу.

    Калибровка разности времени при необходимости может повторяться. Частота рекалибровки зависит достижимой точности определения локального и серверного времени и от требуемой точности вычисления разности. По моим очень приблизительным прикидкам, если требуемая точность составляет 100 мс, то рекалибровку следует проводить ежечасно.

    Если после 2-3 повторений калибровки установить точное значение разности не удалось - клиенту следует отказать в участии.

    • На клиенте я могу самостоятельно нарисовать любой отклик, так что все танцы вокруг замеров в итоге бесполезны. В общем случае задача не решаема именно в силу того что клиентская часть попадает в руки пользователя.
    • ThunderCat,

      На клиенте я могу самостоятельно нарисовать любой отклик

      Ну да, осталось уложиться в доли секунды, и при этом не выдать не только проигрышный, но и супервыигрышный результат (когда расчётно нажатие было выполнено до сигнала), что вообще череповато баном. Нет, это возможно, конечно... но непросто.

      В общем случае задача не решаема именно в силу того что клиентская часть попадает в руки пользователя.

      Согласен.

    • Akina, да, я думал про некий аналог калибровки, чтобы вычислить задержку/пинг/разницу между сервером и клиентом. Думаю, что если добавить к этому еще одну временную метку, которая будет означать начало раунда можно будет выводить точное время отклика с начала раунда. Пока буду реализовывать в эту сторону..
    • Shavadrius, ну я так и описываю - сервер работает исключительно по своим меткам времени, а клиентские пересчитывает в свои на основании рассчитанных разниц для каждого клиента индивидуально.

      С другой стороны - я как-то перечитал вопрос и вдруг подумал, что тебе по описанию вроде как надо время реакции не с момента "отмашки" на сервере, а с момента отображения этой отмашки на клиенте... не? А то сервер прокукарекал, у одного клиента на экране этот отобразится через секунду, у другого через две - и получается у первого секундный гандикап, кагбэ тоже не сильно справедливо.

    • Akina, да нет никакого сигнала. ведущий задает вопрос и кто первый догадался - тот и нажимает. Можно нажимать по ходу вопроса и тогда все могут начать нажимать после ключевого слова, а там уже и пинги/задержки будут влиять.
      Хочется уменьшить ситуации: "ну я же жал первее!" и "во всем пинг виноват!"...
    • Shavadrius,

      ведущий задает вопрос и кто первый догадался - тот и нажимает

      Значит, отсчёт идёт от момента начала "задавания". По нему и смотреть.

      а там уже и пинги/задержки будут влиять

      В описанном мной случае - не влияют. Правда, влияет время с момента рассылки сервером вопроса до момента отображения его на клиентах. Но если оно сильно разное, то это скорее претензии к локальной скорости отображения, а не к передаче. Особенно если вопрос стримится в реалтайме, а не рассылается текстом.

      Хочется уменьшить ситуации: "ну я же жал первее!" и "во всем пинг виноват!"...

      В правилах явно прописать безусловный отказ от претензий по данному поводу. Чай, не детский сад.

    Ответы:

    Вам, похоже, не нужно какое-то абсолютное время. Достаточно отправить игроку сигнал и замерить локальное время реакции на этот сигнал по времени браузера игрока. Браузер игрока знает локальное время, знает когда пришел сигнал с сервера и знает когда отреагировал пользователь. Осталось отправить на сервер локальное время регистрации пришедшего сигнала от сервера и время реакции игрока.

    Минус у схемы только один, через отладочную панель можно жульничать.
    Чтобы жульничать было труднее, можно заставить клиенты подключиться к веб-сокету сервера и сигнал с реакцией прокидывать через него. Это тоже не убережет от жульничества с автоматизацией реакции.
    Наверно самое безопасное в плане жульничества решение потребовало бы стриминга видеопотока с физической реакцией игрока, а все подозрительные случаи выборочно потом проверять и модерировать людьми покадрово на предмет подделки видеосигнала.

    Только на сервере. Если есть возможность реагировать со стороны сервера не сразу, то реализовать несколько серверов. Игроки выбирают ближайший.

    Нужно решить такую задачу?

    Опишите проблему, и специалист поможет с настройкой, исправлением ошибки или доработкой сайта. Подберём понятный план работ без лишней переписки.

    Заказать помощь
    Лучший ответ
    1
    Игорь Волков Ответ

    Для определения игрока, который быстрее всех нажал кнопку на веб-странице, можно использовать JavaScript. Ниже приведен пример кода на JavaScript, который позволит определить время нажатия кнопки для каждого игрока:

    let players = []; // создаем массив игроков
     
    document.addEventListener('keydown', function(event) {
      if(event.keyCode === 32) { // проверяем нажатие клавиши пробел
        let currentTime = new Date().getTime(); // получаем текущее время в миллисекундах
        let player = {
          time: currentTime,
          name: 'Player ' + (players.length + 1) // генерируем имя игрока
        };
        players.push(player); // добавляем игрока в массив
        console.log(player.name + ' pressed the button at ' + currentTime); // выводим информацию о нажатии кнопки
      }
    });
     
    // После игры можно определить победителя, найдя игрока с наименьшим временем нажатия кнопки
    let winner = players.reduce((prev, current) => prev.time < current.time ? prev : current);
    console.log('Winner is: ' + winner.name + ' with time: ' + winner.time);

    let players = []; // создаем массив игроков document.addEventListener('keydown', function(event) { if(event.keyCode === 32) { // проверяем нажатие клавиши пробел let currentTime = new Date().getTime(); // получаем текущее время в миллисекундах let player = { time: currentTime, name: 'Player ' + (players.length + 1) // генерируем имя игрока }; players.push(player); // добавляем игрока в массив console.log(player.name + ' pressed the button at ' + currentTime); // выводим информацию о нажатии кнопки } }); // После игры можно определить победителя, найдя игрока с наименьшим временем нажатия кнопки let winner = players.reduce((prev, current) => prev.time < current.time ? prev : current); console.log('Winner is: ' + winner.name + ' with time: ' + winner.time);

    Этот код создает массив игроков и при нажатии клавиши пробел записывает время нажатия для каждого игрока. После окончания игры можно определить победителя, найдя игрока с наименьшим временем нажатия кнопки.

    Таким образом, вы сможете определить игрока, который быстрее всех нажал кнопку на веб-странице.

    Другие ответы (0)

    Пока нет других ответов. Будьте первым, кто поможет автору.

    Ответить на вопрос

    комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *

    Вам также может быть интересно