Lancement de 619 mille tetris sur GLSL, leur rendu et un simple bot

J'ai eu une «idée» de créer le nombre maximum de Tetris simultanément pour un shader (une texture framebuffer).


Voici une brève description du fonctionnement du code résultant.


Qu'est-ce que c'est:


Chaque tetris fonctionne en trois pixels, pour une résolution de 1920x1080 , vous pouvez exécuter 619200 copies à la fois. A également fait un bot simple pour la lecture automatique.
À la fin de la publication, des liens pour exécuter et source.
La vidéo mise à jour, montre le nombre de champs restants, jusqu'à zéro.



Stockage de données


Table Tetris de taille [10, 22] (10 largeur, 22 hauteur).
Chaque cellule peut être vide ou non vide.
Un total de 22 * 10 = 220 bits est requis pour stocker la table entière.
Un «pixel» correspond à quatre flottants 24 bits, 96 bits par pixel.


Visuellement (une partie du cadre de débogage), trois pixels sont surlignés en rouge, c'est un champ enregistré:


image


2 * 96 + 24 + 4
Deux pixels, un flotteur du troisième pixel, 4 bits du deuxième flotteur du troisième pixel
Il y a deux flotteurs inutilisés dans le troisième pixel pixel3.zw , ils stockent l' état de la logique , plus précisément


  • z stocke trois nombres à huit bits [a,b,c]
    - une position du bloc courant, comme l'ID de la position dans le tableau (un tableau de taille 220 bits, la position maximale est 220 qui est inférieure à 0xff)
    - b temps jusqu'à la descente automatique (temporisateur) chaque trame -1 à ce nombre, car il est devenu 0 puis il tombe sur le bloc vers le bas
    - c ID du bloc courant
  • w aussi [a,b,c] , mais aussi le signe (positif ou négatif) de l'ensemble du flotteur est le drapeau de fin de partie dans la table courante (afin de ne pas gaspiller de ressources si le champ est débordé )
    - une action, aucune action (0), gauche (1), droite (2), et ainsi de suite, le code complet en commun , les actions ont deux états, vérifiez à gauche et vérifiez s'il est possible de se déplacer à gauche, l'action est définie à gauche_ déplacer .
    - [b,c] 0xffff (16 bits) points de la table actuelle, le nombre de lignes qui ont brûlé

Il reste 20 inutilisés dans le deuxième flottant du troisième pixel.


cadre de débogage montrant que la logique de sauvegarde fonctionne correctement
à gauche, il y a un champ blanc de trois pixels, défini spécifiquement pour montrer que les espaces sont traités correctement (si la résolution n'est pas un multiple de trois, la bande ira à un angle)
condition sur la ligne 75 Tampon A


image


Pourquoi ai-je besoin d'ID d'action:


  • Les données sont stockées dans trois pixels, il est impossible de vérifier simultanément la logique et de modifier les données dans une seule image (sans exécuter toute la logique et charger la carte entière dans chaque pixel, la charge augmentera des dizaines de fois).
  • Par conséquent, la logique de stockage de données fonctionne dans chaque pixel et exécute les commandes reçues left_ move , les commandes de vérification left_ check ne sont exécutées que dans un pixel (troisième).

Endroit lent


  • Chaque troisième pixel (pixel logique) décompresse la carte entière (lecture des trois pixels).
  • Les deux pixels restants ne se décompressent que "eux-mêmes" (un pixel) pour effectuer l'action enregistrée.
  • Pendant l'action, la ligne a brûlé, un autre pixel est chargé, car la table tombe et les parties inférieures de la table doivent savoir ce qui est en haut.

Performances de l'algorithme de stockage


Pour le test, définissez également #define debug sur Common et AI 0 .
J'ai obtenu un tel résultat - 10FPS lors du rendu et du traitement de tous les champs 619200,
sur 120 mille champs (25fps)


image


Logique du bot


Logique Très mauvais , le bot s'éteint en une minute, et obtient jusqu'à 60 points.


Je n'ai pas pu démarrer une bonne logique avec de nombreux cycles de vérification des trous et rebords et champs combustibles, considérant la meilleure position en fonction de toutes les chutes possibles ...
Une bonne logique a fonctionné pour moi jusqu'à 100 copies et a donné un fort décalage lors du contournement de tous les cycles.


Ma logique de bot fonctionne comme ça
Toute la logique est dans la fonction AI_pos_gen dans le tampon A, il y en a dix.


Pseudocode:
vérifier la hauteur pour l'installation du bloc est égal au maximum pour le champ dans la colonne actuelle (vérifier une ligne pour la hauteur)


 (4   ){ (  (10)){ (     ){  (    ,  )   best ID()  best POS } } }  (     )   (  )      0     1 

Il s'avère que trois cycles sont courants - ils mettent le bloc de sorte que la hauteur soit minimale.


La fonction AI_pos_gen est appelée lorsqu'un nouveau bloc apparaît et renvoie la position de chute par le haut , en prenant l'ID de bloc et en le faisant tourner, la fonction fonctionne dans le troisième pixel (logique), c'est-à-dire qu'elle a une carte entièrement chargée (tableau de cartes).
Vous pouvez facilement essayer d'écrire votre bot si vous le souhaitez.


Endroit le plus lent
En ajoutant une seule boucle pour tester les trous , mon pilote de carte vidéo s'est écrasé lorsque le nombre de bots était supérieur à 10 000 ... le bot que j'ai écrit est la version la plus "minimaliste" du bot que j'ai pu faire, et c'est malheureusement très mauvais.


Interface utilisateur / rendu


Tout le rendu dans l' image , la logique de l'interface utilisateur dans le tampon B.


Rendu:
Fractionner l'écran en tuiles et dessiner une table dans chaque tuile, charge minimale.


Logique de chargement d'une carte - chaque pixel n'est pas décompressé, chaque pixel est décompressé, seul le «bit requis» est décompressé (littéralement), le code de fonction est:


 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; } 

Pour éviter la pixellisation pendant le défilement, à partir de 43 000, la partie fractionnaire du flotteur est perdue, et cela ne fonctionne pas d'ajouter 619 000 aux UV pour le défilement (il y aura des pixels au lieu des tableaux).
Tout le défilement est divisé en une grande tuile et tourne dans un cercle ajoutant un maximum de 32 aux UV. (ligne 207 dans l' image ).


La même opération est effectuée pour déterminer l'ID de champ. (ligne 215 dans l' image )


UI


Numéros:
Le jaune est le nombre de champs tetris.
Grand gauche - le numéro du champ actuel.
En bas à droite - le champ actuel pointe.


Source et lancement


Bufer A logique, Bufer B est le contrôle de l'interface utilisateur, rendu d' image
Source sur https://www.shadertoy.com/view/3dlSzs (temps de compilation via Angle 16 sec)
Le bot y est désactivé (vous pouvez l'activer), et tous les champs sont jouables depuis le clavier.


Contrôlez les flèches gauche / droite / haut / bas.


Réinitialisation du rectangle rouge de l'interface utilisateur, déplacez (faites glisser la souris en cliquant sur LMB) et cliquez sur les champs pour faire défiler ou sélectionnez le champ à afficher.


Lancer à partir d'un navigateur Web:


  • exécuter chrome avec chrome.exe --use-angle = gl
  • suivez le lien vers shadertoy
  • dans l'éditeur du site, sélectionnez Commun et supprimez #define no_AI
  • (également en commun) définissez #define AI 199 sur 0, c'est-à-dire #define AI 0
  • cliquez sur le bouton de compilation (sous la fenêtre de l'éditeur sur le shader) et cliquez sur plein écran

La deuxième option est d'exécuter le shader dans n'importe quel "lanceur de shader", voici le lien vers l'archive ( téléchargement ) dans laquelle le fichier * .exe avec ce shader.


Temps de compilation OpenGL environ 10 sec.


Mise à jour : ajout d'un shader avec vérification des trous https://www.shadertoy.com/view/wsXXzH
au lieu de la condition pour une meilleure position à la même hauteur. La fonction check_block_at_wh (ligne 380 BufA) compte les trous avec vérification de la validité de la position, aucun nouveau cycle n'a été ajouté et la ligne de condition 442 à 459 BufA.
Il brûle également rapidement en une minute dans les 30 à 60 points. (Évidemment, vous devez vérifier une grande zone pour les trous, mais cela donne des freins puissants)


Et deux photos expliquant un peu le travail:
sélection de position https://i.imgur.com/e0uENgV.png
la position de blocage pour la condition est https://i.imgur.com/ORECXUW.png

Source: https://habr.com/ru/post/fr443042/


All Articles