PDA

Просмотр полной версии : [Include] random_switch.inc



Daniel_Cortez
03.12.2019, 09:13
Что это такое?
Этот инклуд позволяет использовать псевдооператор switchrand() - аналог switch со случайным выполнением одного из кейсов (case).

Где это может понадобиться?
В любых системах, где нужно случайно выбрать один из кейсов в switch, и при этом нумерация в кейсах прерывается (например, "1, 2, 3, 6, 7").
Допустим, решили вы сделать пикап, который выдаёт случайный приз:

switch(random(6))
{
case 0:
{
new money = random(4000) + 1000;
GivePlayerMoney(playerid, money);
new string[14 + 4 + 1];
format(string, sizeof(string), "Вы получили $%d!", money);
SendClientMessage(playerid, -1, string);
}
case 1:
{
new money = random(2000) + 1000;
GivePlayerMoney(playerid, -money);
new string[14 + 4 + 1];
format(string, sizeof(string), "Вы потеряли $%d!", money);
SendClientMessage(playerid, -1, string);
}
case 2:
{
#if !defined FLOAT_INFINITY
const Float:FLOAT_INFINITY = Float:0x7F800000;
#endif
SetPlayerHealth(playerid, FLOAT_INFINITY);
SendClientMessage(playerid, -1, "Вы получили бессмертие (до респавна)!");
}
case 3:
{
GivePlayerWeapon(playerid, WEAPON_DEAGLE, 300);
SendClientMessage(playerid, -1, "Вы получили пистолет!");
}
case 4, 5:
{
new Float:x, Float:y, Float:z, Float:a;
GetPlayerPos(playerid, x, y, z);
GetPlayerFacingAngle(playerid, a);

new vehicleid = CreateVehicle(562, x, y, z, a, -1, -1, 1 * 60);
SetVehicleParamsEx(vehicleid,
VEHICLE_PARAMS_ON, VEHICLE_PARAMS_OFF, VEHICLE_PARAMS_OFF,
VEHICLE_PARAMS_OFF, VEHICLE_PARAMS_OFF, VEHICLE_PARAMS_OFF,
VEHICLE_PARAMS_OFF);
PutPlayerInVehicle(playerid, vehicleid, 0);
SendClientMessage(playerid, -1, "Вы получили а-а-а-втомобиль!");
}
}

И всё, вроде бы, хорошо, система работает, а игроки довольны. Но тут в одном из обновлений вам нужно убрать case 1, ибо игроки расстраиваются, когда у них отбирают деньги. Если вы просто удалите его - система не будет работать в 1 случае из 6, ибо random() всё ещё будет иногда выдавать единицу. Чтобы это исправить, придётся сделать что-то одно:
Объединить 1-й кейс с каким-то другим (например, case 0, 1:), но это фактически увеличит шанс выпадения другого приза.
Добавить какой-то новый приз вместо удалённого (придётся думать и писать новый код, что, естественно, большущий минус :mamba:).
Сместить номера всех кейсов, которые шли после единицы, + исправить значение в random().
Придумать костыль по типу добавления goto, который бы отправлял обработчик в позицию перед switch, дабы random() сгенерировало новое число.

И если на подобном switch с малым количеством кейсов все перечисленные пункты могут показаться незначительными, то представьте, что switch у вас состоит из 50, 100 и т.д. кейсов. В этом случае switchrand довольно сильно упростит поддержку кода, сняв с вас и нужду думать насчёт того, чтоб все кейсы были по порядку, и нужду в изменении значения, указанного в random() при изменении количества кейсов.

Ещё один пример ситуации - RP-мод с системой фракций (банд/мафий/организаций), в котором при выполнении какого-то задания нужно отправить игрока в одну из трёх армий. При этом так получилось, что ID армий в списке фракций - 2, 15, 22.
В случае со switchrand решение будет выглядеть примерно так:

switchrand()
{
case 2:
{
// ...
}
case 15:
{
// ...
}
case 22:
{
// ...
}
}

Без switchrand есть 2 варианта решения:
Поместить все нужные данные в массивы, определять рандомно индекс и уже отображать игроку нужную информацию из массивов согласно получившемуся индексу (но если у нас для каждой отдельной фракции нужно выполнить уникальный код, могут возникнуть проблемы. Да и в целом код получится довольно сложным и менее гибким относительно варианта switchrand).
Сделать нечто подобное:

new army_frac[] = {2, 15, 22};
new fid = army_frac[random(sizeof(army_frac))];
switch(fid)
{
case 2:
{
// ...
}
case 15:
{
// ...
}
case 22:
{
// ...
}
}
Но тут придётся следить за тем, чтоб значения в массиве и номера кейсов совпадали, что может быть проблематично (особенно в моменты когда кейсы наполнятся кодом и/или количество кейсов возрастёт).

Иными словами, главная задача switchrand - упростить работу со связкой "random + switch", избавив пользователя от рутинной задачи по слежению за номерами кейсов и числом в random().

Плюсы реализации:
Не требует наличия YSI, amx_assembly или каких-либо других сторонних инклудов.
Совместим с JIT.

Недостатки:
Из-за использования оператора __emit (необходимо для совместимости с JIT) инклуд совместим только с новыми версиями компилятора (3.10.6 и новее).

Использование
Просто подключите random_switch.inc и используйте макрос switchrand(), как если бы использовали оператор switch:

#include <a_samp>
#include <random_switch>

main()
{
switchrand()
{
case 0: print("case 0");
case 42: print("case 42");
case 84: print("case 84");
}
}


Скачать: https://www.dropbox.com/s/ew9px62sec6peu5/random_switch.zip?dl=1

Благодарности:
DeimoS - идея, примеры использования (http://pro-pawn.ru/showthread.php?16974&p=95520&viewfull=1#post95520).
VVWVV - изначальная реализация (http://pro-pawn.ru/showthread.php?16974&p=95511&viewfull=1#post95511).

История версий:
(03.12.2019) Первый релиз.
(05.12.2019) Добавлен обход проблемы с падением компилятора (баг специфичен для версий компилятора 3.10.7 - 3.10.9).
(31.12.2019) Оптимизирован код.

vvw
03.12.2019, 15:44
Может быть сделать отдельную тему, которая будет включать инклюд и описание DeimoS? Ну а эту тему удалить, ибо она некрасивая.

SteveStage
03.12.2019, 20:08
Ну и 1 баг - зависает компилятор (крашит) при подключении данного инклуда, даже без его использования
Компилятор от Zeex'а, версия 3.10.9

Daniel_Cortez
04.12.2019, 13:03
Ну и 1 баг - зависает компилятор (крашит) при подключении данного инклуда, даже без его использования
Компилятор от Zeex'а, версия 3.10.9
Перед релизом я проверял инклуд на стандартном new.pwn (с подключением random_switch.inc и добавлением конструкции switchrand в OnGameModeInit()) с компиляторами версий 3.10.6-3.10.9, во всех случаях мод компилировался и работал нормально, причём как с JIT, так и без (в т.ч. с CrashDetect).
Если у вас крашит компилятор, советую присмотреться к своему моду или другим инклудам, которые в нём используются. Либо выкладывайте сюда мод со всеми инклудами, вместе будем разбираться.

SteveStage
05.12.2019, 00:37
Если у вас крашит компилятор, советую присмотреться к своему моду или другим инклудам, которые в нём используются. Либо выкладывайте сюда мод со всеми инклудами, вместе будем разбираться.

Присмотрелся, и даже поочередно подключал каждый инклуд в моде - идеально компилирует.
Кидаю все свои инклуды: https://www.dropbox.com/s/6e454xtjucwhht9/include.rar?dl=0
Вот их подключение:

//#include <jit>
#include <crashdetect>
#include <a_samp>
#include <a_mysql>
#include <foreach>
#include <mxdate>
#include <Pawn.CMD>
#include <sscanf2>
#include <streamer>
#include <zmessage>
//#include <random_switch>
//если я раскомментирую комментарий выше - будет краш компилятора

Daniel_Cortez
05.12.2019, 19:35
Может быть сделать отдельную тему, которая будет включать инклюд и описание DeimoS? Ну а эту тему удалить, ибо она некрасивая.
Сделал, прежнюю тему (http://pro-pawn.ru/showthread.php?16974) переместил в архив.


...
Да, действительно, краш происходит, но только когда файл random_switch.inc подключен и switchrand при этом не используется. Связано это с __emit sysreq.c random, причём краш происходит только если функция random() ранее не использовлась. Я уже исправил (https://github.com/pawn-lang/compiler/commit/3c1ac1e91166aae6f14871d1202712a0a6f245bb) этот баг ещё год назад, фикс приняли в июне этого года, поэтому в следующем релизе компилятора краша быть не должно.
Тем не менее, я и в random_switch.inc добавил меры для обхода этой проблемы (причём "обход" вышел довольно бескровным, т.к. даже никакого лишнего кода из-за него не генерируется).

random_switch.inc v1.1
Скачать: https://www.dropbox.com/s/ew9px62sec6peu5/random_switch.zip?dl=1
Добавлен обход проблемы с падением компилятора (баг специфичен для версий компилятора 3.10.7 - 3.10.9).

SteveStage
05.12.2019, 20:22
Тем не менее, я и в random_switch.inc добавил меры для обхода этой проблемы (причём "обход" вышел довольно бескровным, т.к. даже никакого лишнего кода из-за него не генерируется).

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

Daniel_Cortez
31.12.2019, 14:37
random_switch.inc v1.2
Скачать: https://www.dropbox.com/s/ew9px62sec6peu5/random_switch.zip?dl=1
Немного оптимизирован код.
Исправлено случайное использование emit вместо __emit.
Оператор emit будет удалён в следующей версии компилятора, в 3.10.10 останется только вариант __emit.

Daniel_Cortez
14.01.2021, 19:12
random_switch.inc v1.3
Скачать: https://www.dropbox.com/s/ew9px62sec6peu5/random_switch.zip?dl=1
Добавлена функция GetSwitchRandValue(), возвращающая значение случайного кейса, выбранного при последнем использовании switchrand.
Пример:

switchrand()
{
case 2, 15, 22: printf("%d", GetSwitchRandValue()); // Выведет "2", "15" или "22"
}

Данная функция может пригодиться, когда нужно знать выбранное случайное значение после конструкции switchrand, либо внутри case из нескольких значений (как в примере выше).
Отдельное спасибо DeimoS (https://pro-pawn.ru/member.php?2548-DeimoS)'у за идею.