- PVSM.RU - https://www.pvsm.ru -
У меня была "идея" сделать максимальное число одновременно запущенных "Тетрисов" для одного шейдера (одной текстуры фреймбуфера).
Далее небольшое описание как работает полученный код.
Каждый тетрис работает в трех пикселях, для 1920x1080
разрешения можно запустить 619200
копий одновременно. Также сделан простой бот для авто-игры.
В конце поста ссылки для запуска и исходники.
Таблица "тетриса" размера [10, 22]
(10 ширина, 22 высота).
Каждая ячейка может быть либо пустой либо не пустой.
Итого 22 * 10 = 220
бит требуется для хранения всей таблицы.
Один "пиксель" это четыре 24-битных float, 96 бит в пикселе.
Визуально (кусок debug фрейма), красным выделены три пикселя это и есть одно сохраненное поле:
2 * 96 + 24 + 4
Два пикселя, один float третьего пикселя, 4 бита второго float третьего пикселя
Осталось два неиспользованных float в третьем пикселе pixel3.zw, они хранят состояние логики, точнее
[a,b,c]
-1
к этому числу, как стало 0 то падает на блок вниз[a,b,c]
, но также знак (положительное или отрицательное) всего float является флагом конца игры в текущей таблице (чтоб не тратить ресурсы если поле завалено)[b,c]
0xffff(16 бит) очки текущей таблицы, количество линий которые сгорелиОсталось 20 бит
неиспользованных во втором float третьего пикселя.
debug фрейм показывающий что логика сохранения работает верно
слева идут белое поле размером три пикселя, установлено специально чтобы показать что разрывы обработаны верно (при разрешении не кратном трем полоса будет идти под углом)
условие на строке 75 Buffer A
Для теста установите #define debug в Common и AI 0 там-же.
Я получил такой результат — 10ФПС при рендеринге и обработке всех 619200 полей,
на 120 тысяч полей (25фпс)
Логика Очень плоха, бот сгорает в течении минуты, и получает до 60 очков.
Я не смог запустить хорошую логику с множеством циклов проверяя дыры и выступы и сгораемые поля, считая лучшее положение на основе всех возможных падений…
Хорошая логика у меня работала до 100 копий и давала сильный лаг при обходе всех циклов.
Моя логика бота работает так
Вся логика находится в функции AI_pos_gen в Buffer A, ее там десяток строк.
Псевдокод:
высота проверки для установки блока равна максимальной для поля в текущей колонке (проверка по одной строке по высоте)
цикл(4 поворота текущего блока){
цикл(по ширине таблице(10)){
ЕСЛИ(блок можно поставить в текущее положение){
если (текущая высота блока и позиция, меньше прошлой)
то запоминаем best ID(блока) и best POS
}
}
}
функция (блок можно поставить в текущее положение)
проверяет циклом (по размеру блока) чтоб все элементы поля были 0 там где элементы блока 1
Получается три цикла которые банально — ставят блок так чтобы высота была минимальна.
Функция AI_pos_gen вызывается при появлении нового блока, и возвращает позицию для падения сверху, принимая ID блока и делая его вращение, функция работает в третьем пикселе (логике) то есть имеет полную загруженную карту (массив map).
Легко можно попробовать написать свой бот, если если желание.
Самое медленное место
Добавив всего один цикл для проверки дыр у меня драйвер видеокарты падал при количестве ботов больше 10тыщ… тот бот что я написал это максимально "минималистичный" вариант бота который я смог сделать, и он очень плох к сожалению.
Весь рендеринг в Image, логика UI в Buffer B.
Рендеринг:
Разбитие экрана на тайлы и рисую по таблице в каждом тайле, минимальная нагрузка.
Логика загрузки карты — не распаковывается всю карта каждый пиксель, распаковывается только "нужный бит" (буквально), код функции:
int maptmp(int id, int midg) {
int nBits = 8;
ivec4 pixeldata = loadat(id, midg);
int itt = (id / 24) / 4; //data pixel id 0-2
int jtt = (id - itt * 24 * 4) / 24; //component in data pizel id 0-3
int ott = (id - itt * 24 * 4 - jtt * 24) / 8; //component in unpacked value 0-2
int ttt = (id - itt * 24 * 4 - jtt * 24 - ott * 8); //bit after int2bit 0-7
ivec3 val = decodeval16(pixeldata[jtt]);
int n = val[ott];
for (int i = 0; i < nBits; ++i, n /= 2) {
if (i == ttt) {
if ((n % 2) == 0)return 0;
else return 1;
//switch + return does not work on windows(Angle)
/*switch (n % 2) {
case 0:return 0;break;
case 1:return 1;break;
}*/
}
}
return 0;
}
Чтоб избежать пикселизации при прокрутке, начиная с 43000 идет потеря дробной части float, и никак не выйдет добавить 619 тысяч к UV для прокрутки (будут пиксели вместо таблиц).
Вся прокрутка делится на один большой тайл и крутится по кругу прибавляя максимум 32 к UV. (строка 207 в Image).
Тоже самое сделано для определения ID поля. (строка 215 в Image)
Числа:
Желтое — количество полей тетриса.
Слева большое — номер текущего поля.
Справа меньшее — очки текущего поля.
Bufer A логика, Bufer B это управление UI, Image отрисовка
Исходник по ссылке https://www.shadertoy.com/view/3dlSzs [1] (время компиляции через Angle 16 сек)
Там отключен бот(можно включить), и все поля играбельные с клавиатуры.
Управление стрелками влево/право/вверх/вниз.
UI красный прямоугольник сброс, перемещайте (тащите мышь нажав ЛКМ) и клик мышкой по полям для прокрутки или выбора поля для показа.
Запуск из веб-браузера:
Второй вариант, запустить шейдер в любом "лаунчере шейдеров", вот ссылка на архив (скачать) [2] в котором *.exe файл с этим шейдером.
OpenGL время компиляции около 10 сек.
Автор: Danil
Источник [3]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/tetris/311010
Ссылки в тексте:
[1] https://www.shadertoy.com/view/3dlSzs: https://www.shadertoy.com/view/3dlSzs
[2] ссылка на архив (скачать): https://danilw.github.io/GLSL-howto/Auto_tetris/AutoTetris.zip
[3] Источник: https://habr.com/ru/post/443042/?utm_source=habrahabr&utm_medium=rss&utm_campaign=443042
Нажмите здесь для печати.