Однажды, солнечным весенним утром, почитывая городской форум, я наткнулся на ссылку с простенькой игрой от известной торговой сети. Игра (акция), посвящённая чемпионату мира по футболу, представляла собой незамысловатое поле три на три, заполненное футбольными мячами. Кликая по мячу, мы открывали картинку с тем или иным товаром. При открытии трёх одинаковых картинок участнику гарантировалось бесплатное получение данного товара в одном из магазинов сети. Также под одним из мячей имелось изображение красной карточки, открытие которой означало конец игры.
Интерес к игре быстро угасал по причине крайне редких случаев выигрыша. Играя вчетвером с 6 номеров телефона за несколько дней (около 15 партий) выиграть не удалось никому. “А какова вообще вероятность выиграть в данной игре?” — спросил я себя и достал лист бумаги, на ходу вспоминая курс высшей математики. Расписывая формулы, выяснилось, что каждая игра может содержать от 1 до 9 ходов, а каждый ход приводит к одному из трёх состояний — победа, поражение или продолжение игры (за исключением первых двух ходов, которые могут привести только к двум состояниям). Быстро поняв, что формула для 9 полей слишком сложна, я начал с простого — 4 поля (три одинаковых продукта и красная карточка). Быстрый набросок формул на салфетке, и выяснилось, что вероятность выигрыша — 1/4. Для 5 полей пришлось повозиться, но расчётная вероятность получилась также 25%. В этот момент, я задумался и трижды перепроверил расчёт. Всё верно. Не сказать, что я сильно удивился, ещё со времён учёбы в ВУЗе я привык, что в теории вероятности возможны самые неожиданные результаты. Расчёт для 9 полей занял бы несколько листов бумаги и не один час времени, поэтому было принято более простое решение. Смоделировать игру скриптом. Несколько десятков минут, кружка кофе, и скрипт готов. Использовался PowerShell, как инструмент, который всегда под рукой у системного администратора.
$fail = 0
$win = 0
for ($m=1; $m -lt 1001; $m++)
{
$mas = 1, 2, 3, 4, 5, 6, 7, 8, 9
$sum = 0
$result = ""
for ($i=0; $i -lt 8; $i++)
{
$j = Get-Random -Minimum $i -Maximum 9
if ($mas[$j] -eq 9)
{
$result = "FAIL"
break
}
if ($mas[$j] -eq 1 -or $mas[$j] -eq 2 -or $mas[$j] -eq 3) { $sum++ }
if ($sum -eq 3)
{
$result = "WIN"
break
}
$mas[$j] = 0
$mas = $mas | Sort-Object
}
$result
if ($result -eq "WIN") { $win++ }
if ($result -eq "FAIL") { $fail++ }
}
$fail
$win
Выигрышные номера я принял за 1, 2 и 3, а красную карточку за 9. Забегу немного вперёд, как выяснилось позже, программисты, которые писали эту игру, мыслили примерно в том же ключе.
Запустив скрипт, я получил неожиданный результат — 25% выигрышей. Поиграв с количеством выигрышных элементов и общим количеством полей, я выяснил, что вероятность выигрыша в подобной игре не зависит от количества полей и равна единице, поделенной на количество выигрышных элементов, увеличенных на единицу.
В этот момент в мою голову закрались большие сомнения в честности игры. Ведь я должен был выигрывать каждый четвёртый раз. Но к тому времени я проиграл уже раз 10. Вероятность такого развития была крайне низка, и я начал исследовать скрипты игры.
А параллельно открыл правила.
Правила участия в маркетинговом мероприятииОтлично! Значит, доказав наличие этих алгоритмов и процедур, мы поймаем организатора за руку.
1.3 Организатор гарантирует, что при определении возможности получения поощрений не используется алгоритмов или процедур, которые могут определить результаты Акции до начала ее проведения.
F12 в Chrome, и начинаем исследование. Играем до конца, одни глазом поглядывая в мониторинг сети. Достаточно стандартная работа приложения, загрузка страницы, скриптов, спрайтов и нескольких наборов данных в формате JSON. Но странное дело, от момента нажатия кнопки “Начать игру” до её завершения нет никакого обмена данными с сервером. Вторая странность — выигрышный спрайт даже не грузится, грузится только спрайт “Вы проиграли”. Очевидно, что скрипт ещё до начала игры “знает” её итог. Осталось поймать его за руку.
Основной JS с игрой очень большой, 1.5 Мб, без форматирования, всё «в кашу». Беглый поиск в Google выдал нам сервис JS Beautifier, и вот мы уже читаем отформатированный код. Но объём его очень велик, более 40 000 строк. Беглый просмотр JSON не дал результатов, слишком много данных, было решено идти другим путём — от обратного. Поиском по именам файлов спрайтов был найден JSON с нужными данными.
Спрайты 0, 1 и 2 — выигрышные, спрайт 3 — красная карта, остальные не имеют значения.
Поиск по имени массива спрайтов приводит нас в нужный блок скрипта игры.
Меня заинтересовал массив Outcome в скрипте, я ещё раз глянул в JSON, и о, чудо! Вы не поверите!
Да это же порядок выпадения спрайтов! И он заканчивается крайной картой! Абсолютно не важно, в какие поля вы кликаете мышкой, спрайты отображаются в заданном порядке, сгенерированном сервером. Сам сервер управляет процессом и регулирует вероятность. Никакой случайности не прослеживается.
Снова обратимся к правилам игры:
8. Призовой фонд:Очевидно, что при честной игре призы закончатся ориентировочно через 664000 игр, что, видимо, не устроило руководство торговой сети с учётом времени проведения акции и потенциального количества участников.
8.1. Доступные призы: названия и количества
…
Итого 166000
Выводы (немного очевидные).
Играйте в честные оффлайн игры (например, шахматы). Не верьте организаторам онлайн-розыгрышей. Все врут. (с)