Пожалуйста, отнеситесь к моей статье серьёзно. Здесь не будет мнимых тактик по «выигрышу» в казино. Только математика и алгоритмы. По началу я рыл интернет в поиске информации, но не нашёл ничего дельного. Именно это и сподвигло меня написать статью. Забегая наперёд скажу, что здесь будет написано о предсказывании псевдослучайной генерации.
Как работает рандом в SA:MP?
У компьютеров нет случайности. Здесь рандом - это алгоритм, основанный на математических операциях. Такой алгоритм называется генератором псевдослучайных чисел. Настоящая случайность существует только в природе, и то это довольно философская тема. То, что сейчас для нас случайность, может стать абсолютно прогнозируемой вещью в будущем. Наверняка вы когда-либо интересовались тем, как же всё-таки работает рандом в попытках обыграть в казино. Может быть, пытались зайти закономерность в последовательнастях выпадающих чисел? К сожалению, я не волшебник и предсказать рандом в казино пока что можно только в теории. Но я считаю, что достаточно близок к этому и в своей статье постараюсь это доказать. Однако, эту статью я пишу не просто так. Мне не хочется больше этим заниматься и я хочу привлечь внимание других людей к этому. Может быть, именно вы создадите первый в мире чит на казино?
Ядро Pawn
Pawn – си-подобный скриптовый язык программирования и бла-бла-бла.
Именно на нём пишутся сервера для нашей любимой игры. Для того, чтобы решить нашу задачу, нужно понять, как это работает изнутри. Обратимся к исходному коду, который, к счастью, был любезно оставлен авторами:
тык
Обратите внимание на строку №446: именно здесь начинается нужная нам функция. Это линейный конгруэнтный генератор — один из самый популярных и простых в создании генераторов псевдослучайных чисел. Мы любезно позаимствуем эту фунцию в свой код. На основе этой функции я написал программу, позволяющую удобно генерировать числа. Код ниже:
int main()
{
int mod;
cout << "Enter module of number (0 if not): ";
cin >> mod;
while (true)
{
cin.ignore();
cout << "Your number is " << noRandom(mod);
}
return 0;
}
*Это лишь пример, поэтому если вы бездумно скопируете, скорее всего у вас ничего не получится. В своей статье я буду вставлять только основную часть, а не весь код. Если вам понадобится, вы сможете самостоятельно написать нужную вам программу, потому что я описываю достаточно простые для понимания вещи.
Теория
Каждый линейный генератор псевдослучайных чисел имеет периодичность. Это значит, что через n генераций последовательность начнёт повторяться. В нашем случае это число 2^32. Проверить это можно написав следующий код:
void main()
{
for(unsigned long long i = 0; i < RANDOM_PERIOD; i++)
{
noRandom();
if (((i + 1) % 4294967UL) == 0UL)
{
cout << "\rProgress: " << int((double)i / (double)RANDOM_PERIOD * 100) << "%" << flush;
}
}
cout << "\rProgress: " << "100% - copmleted" << endl;
while(true)
{
cin.ignore();
cout << noRandom() << endl;
}
}
Здесь RANDOM_PERIOD – 2^32.
Вывод:
Посмотреть вложение 180875
Мы видим, что числа совпадают. Это значит, что если знать некоторую последовательность чисел, то возможно определить следующее генерируемое число. Но есть один нюанс.
Практика: Решение в лоб
Я написал функцию, ищущую примерную последовательность среди псевдослучайно сгенерированых чисел. Что значит примерную? Это значит, что между числами в последовательности может быть до n неизвестных чисел. Почему примерную? Помните я говорил о нюансе? Так вот, если искать точную последовательность, то у вас скорее всего ничего не выйдет, потому что помимо вас на сервере множество игроков, вызывающих функцию random своими действиями. Я пытался проверить работу этой программы на реальном сервере с малым количеством игроков, заплатил свои кровные 150 рублей криптону для того, чтобы он соорудил мне плагин для SAMPFUNCS, ибо сам я под самп ничего писать не умею, но, увы, ничего не вышло. То ли я дурак, то ли лыжи не едут. Проще говоря, я потратил на тесты довольно много времени, а выхлопа как не было, так и нет. Зато всё прекрасно работает на сервере с одним игроком. Это я показал в видео -
клац. А вот и код:
int optimizedSearch(vector<int> pat, int mod, int k)
{
int M = pat.size();
int result = -1;
if (k > RANDOM_PERIOD - M)
return result; // k is out of range
int j, c;
for (unsigned long i = 0; i < RANDOM_PERIOD - M - c; ++i) {
c = 0;
for (j = 0; j < M + c; ++j)
{
if (noRandom(mod) != pat[j - c])
{
if (j == 0)
break;
++c;
}
if (c > k)
{
c = k;
break;
}
}
if (j == M + c)
{
return 1;
}
}
return result;
}
Алгоритм вернёт 1, если последовательность будет найдена и -1, если нет. Если вам вернётся 1, то нужно будет просто вызывать функцию рандома, чтобы получать следующие числа.
Почему в лоб? Есть ли ещё какое-то решение?
Да, скорее всего есть более быстрый алгоритм поиска, основанный на математический вычислениях. Я не стал этим заниматься,потому что, как я писал выше, мне надоело. Возможно, вложившись в его написание у вас получится отыграть все деньги, проигранные в баре? ;)
Что будет дальше?
Надеюсь, люди воспримут меня всерьёз и умельцы смогут сделать настоящий чит для казино. Но, к сожалению, сейчас это возможно только на бумаге.
Огромная благодарность за то, что прочитали мою статью. Надеюсь, она вдохновит вас на подвиги ;) Удачи.