PDA

Просмотр полной версии : [Урок] Работаем с битами



georJik
21.07.2014, 01:21
Йо. Этот тутор предназначен для более-менее соображающих в программировании. В нашем случае мы будем учиться работать с битами. На первый взгляд это выглядит страшно и непонятно, но если вникнуть - это значительно облегчит вам работу..
И так, для начала ознакомьтесь с побитовыми операторами.

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

Создаем нашу переменную в которой записаны биты.

new lics = 0b000;
Как мы знаем 0 - false, то есть ложь; 1 - true, то есть истина. В нашем случае будет также - 0 есть лицензия, 1 - нету.

Внимание! Биты как и массив начинаются с нулевого!

Давайте попробуем присвоить значение лицензии на авто - 1
Как мы это сделаем:

lics ^= (1<<0);
^ - это оператор "Исключающее или", почему мы используем именно его? Потому-что если вы захотите инвертировать сразу несколько битов - он нам в этом поможет.
И так, мы инвертируем первый бит справа налево, т.е

Как было:

0b000
Как стало:

0b001
Окей, с этим разобрались.

А если нам нужно присвоить сразу несколько значений, как быть?
Решение:

lics ^= ((1<<0)|(1<<1)|(1<<2));
(1<<0) мы присваиваем нулевому биту слева значение 1, инверсия.
(1<<1) если вы ещё не поняли почему первая цифра 1 - мы сдвигаемся на один бит влево; 1 - инвертируем первый бит читая справа налево.
(1<<2) тоже самое что и все выражения выше, только уже операция производится со вторым битом.

Тем самым мы произвели инверсию битов. Т.е если у нас было 000, мы вывели 111. ( также и наоборот )

Дальше идет проверка на лицензии.

Например, узнаем есть ли у нас лицензия на автомобиль

if((lics & (1<<0)) == 0) return SendClientMessage(playerid,0xFF0000FF,"У вас нет лицензии на автомобиль");
Поясняю, мы проверяем почти также, как и устанавливаем. Т.е на сдвигаем на один бит влево ( в проверке ) и проверяем нулевой бит справа налево. Если он не равен нулю, значит лицензия есть. == 1 ставить не рекомендую.


if((lics & (1<<1)) == 0) return SendClientMessage(playerid,0xFF0000FF,"У вас нет лицензии на воздушный транспорт");
То же что и выше, только проверяем уже второй бит

Как примерно это будет выглядеть в моде?:


new lics[MAX_PLAYERS];

callback PlayerReg(playerid) { // callback - фаш коллбек присвоения данных при коннекте ( обнуление )
lics[playerid] = 0b000;
}

CMD:givelicense(playerid,params[]) {
if(sscanf(params,"u",params[0])) return SendClientMessage(playerid,-1,"/givelicense [id]");
lics[params[0]] ^= (1<<0);
SendClientMessage(playerid,-1, ((lics[params[0]] & (1<<0)) != 0) ? ("Лицензия на авто получена!") : ("Лицензия на авто изъята!"));
return true;
}


p.s: Если хотите, можете юзать >> слева направо


Также D_C предложил следующий варинт:

В самом начале сделать перечисление, чтобы не париться с номерами лицензий:

enum
{
LICENSE_DRIVING = 0,
LICENSE_BOATING,
LICENSE_PILOT
};
затем при сдвигах использовать элементы этого перечисления:


lics[targetid] |= (1 << LICENSE_DRIVING);




Вариант Londlem'a:

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


enum(<<= 1)
{
LICENSE_DRIVING = 1, // 1
LICENSE_BOATING, // 2
LICENSE_PILOT // 4
};


Далее просто используем битовый оператор или ( |= ) и название константы:

lics[targetid] |= LICENSE_DRIVING;

И проверять на наличие одной из лицензии почти также:


if((lics[targetid] & LICENSE_DRIVING) != 0)
// code


Разницы почти никакой нет, разве что, не нужно каждый раз смещать, удобнее.



Делаем сохранение \ загрузку ( MySQL ):
Для этого уже мы будем присваивать не 0b000, а 0 ( тоже самое что и 000 )
Мы будем загружать целое число также как и сохранять, а менять уже в нем биты.

Я предлагаю вам создать в вашем массиве игроков переменную Licenses, дабы было удобнее.

Загружаем:


Player[playerid][Licenses] = cache_get_field_content_int(0, "licenses", connectionHandle);
Player - ваш массив игрока
Licenses - наши лицензии
"licenses" - поле в БД где хранится наша переменная
connectionHandle - переменная вашего соединения

Сохраняем:

new query[81+1]; // 81 на запрос; Одну ячейку отдаем машине
mysql_format(connectionHandle,query,80,"UPDATE `accounts` SET `licenses` = '%i' WHERE `Name` = '%s'",Player[playerid][Licenses],name);
mysql_tquery(connectionHandle,query,"","");
accounts - ваша таблица с аккаунтами
licenses - ваше поле в бд для сохранении нашей переменной
name - переменная определяющая имя игрока. Я предлагаю использовать удобный вариант - в массив игрока добавляем массив Name, который будет равен макс.значению имени игрока (24), далее в OnPlayerConnect присваиваем переменной значение нашего имени, думаю вы поняли как это сделать. Ну и использовать можно как Player[playerid][Name], либо создать макрос #define Name(%0) Player[%0][Name]. Это удобно и к тому же не нужно лишний раз создавать эти ненужные переменные. И так, тут думаю все ясно, посылаем запрос в таблицу accounts с нашими параметрами.

Такие пироги, легко и просто, не правда ли?






Автор: georJik


Тема будет дополняться...

Daniel_Cortez
21.07.2014, 02:20
Что означают "SCM" и "callback" ?
Можно было в самом начале сделать перечисление, чтобы не париться с номерами лицензий:

enum
{
LICENSE_DRIVING = 0,
LICENSE_BOATING,
LICENSE_PILOT
};
затем при сдвигах использовать элементы этого перечисления:

lics[targetid] |= (1 << LICENSE_DRIVING);

Было бы куда более наглядно.

georJik
21.07.2014, 02:28
Что означают "SCM" и "callback" ?
Можно было в самом начале сделать перечисление, чтобы не париться с номерами лицензий:

enum
{
LICENSE_DRIVING = 0,
LICENSE_BOATING,
LICENSE_PILOT
};
затем при сдвигах использовать элементы этого перечисления:

lics[targetid] |= (1 << LICENSE_DRIVING);

Было бы куда более наглядно.

Кому как удобнее, можно даже задефайнить как номер ячейки

Daniel_Cortez
21.07.2014, 02:47
Кому как удобнее
Это не удобство, а обычная практика при хранении данных в упакованном виде. Не держать же в голове кучу ID сдвигов для каждого случая.
Потому, собсна, и предложил дополнить 1-й пост. Но, если не хотите - не дополняйте, дело ваше.

georJik
21.07.2014, 02:52
Дополнил, подправил

L0ndl3m
21.07.2014, 22:47
Также, чтобы не смещать каждый раз, лучше сделать смещение прямо в энумераторе.


enum(<<= 1)
{
LICENSE_DRIVING = 1, // 1
LICENSE_BOATING, // 2
LICENSE_PILOT // 4
};


Далее просто используем битовый оператор или ( |= ) и название константы:

lics[targetid] |= LICENSE_DRIVING;

И проверять на наличие одной из лицензии почти также:


if((lics[targetid] & LICENSE_DRIVING) != 0)
// code


Разницы почти никакой нет, разве что, не нужно каждый раз смещать, удобнее.

Salvacore
22.07.2014, 22:48
Буду битмейкером.

Maranzalla
18.03.2015, 21:06
А теперь попробуем начать использовать их, на примере давайте заменим банальные лицензии. Например у нас их три - на лодку воздух и авто.

Создаем нашу переменную в которой записаны биты.

new lics = 0b000;
Как мы знаем 0 - false, то есть ложь; 1 - true, то есть истина. В нашем случае будет также - 0 есть лицензия, 1 - нету.

Вот этот момент поподробнее можно?
что такое первое 0
второе b
и с третьего по пятые 0

NewGreen
19.03.2015, 21:16
Вот этот момент поподробнее можно?
что такое первое 0
второе b
и с третьего по пятые 0

Это инициализация переменой двоичным (бинарным) числом, b - binary, 0b - сообщает компилятору, что дальнейшее число будет представлено в двоичном виде, 000 - число в двоичном представлении.

Maranzalla
20.03.2015, 00:06
Какое максимальное количество бит можно использовать для переменной
new param[MAX_PLAYERS char];?

L0ndl3m
20.03.2015, 00:34
Восьмёрочка ( максимально 1 << 7 ).

Maranzalla
20.03.2015, 21:10
если мы используем
new lics = 0b000;
как вывести lics в format? %i или %b ?или как то по другому?

NewGreen
20.03.2015, 21:28
если мы используем
new lics = 0b000;
как вывести lics в format? %i или %b ?или как то по другому?

Смотря что нужно, если использовать %d то число автоматически преобразуется из двоичного в десятичное, если же %b то будет выведено двоичное число.

format(str, sizeof(str), "Dec = %d, Bin = %b",lics,lics);
Правда вы инициализировали переменную нулем, поэтому будет выведен ноль, присвойте другое бинарное число и вы увидите результат, к примеру 1010100101

Maranzalla
21.03.2015, 00:13
Так получается при загрузке на том примере
Player[playerid][Licenses] уже будет равно значению integer а не binary ?

Camelot
18.08.2015, 02:27
Всё вроде объяснил, а главное забыл:
В чем соблазн работы с битами?

wAx
18.08.2015, 09:00
Всё вроде объяснил, а главное забыл:
В чем соблазн работы с битами?

в одной переменной можно хранить несколько true/false значений
меньше переменных - меньше выделяемой под них памяти
больше свободной памяти - this is good

Unreal
25.08.2015, 23:46
А можно объяснит

lics[params[0]] ^= (1<<0);
Тут дает 1 или 0? И можно ли использовать char?

Daniel_Cortez
03.09.2015, 11:18
А можно объяснит

lics[params[0]] ^= (1<<0);
Тут дает 1 или 0? И можно ли использовать char?
Что значит "даёт"?

Unreal
03.09.2015, 17:59
Что значит "даёт"?

я хотел сказать 'присвоить значение'
а ответ на вопрос я так и не дождался