Programmation de blocs Tetris (littéralement)

33

Dans le jeu Tetris , il existe 7 types de briques ou Tetr i minoes , qui sont mathématiquement connus comme tetr o minoes car ils sont tous constitués de 4 segments carrés:

Briques de tetris

Ils ont les noms I, J, L, O, S, T et Z, qui correspondent à leurs formes approximatives. En comptant les rotations à 90 °, il existe 19 formes uniques au total:

I
I
I
I

IIII

 J
 J
JJ

JJJ
  J

JJ
J
J

J
JJJ

L
L
LL

  L
LLL

LL
 L
 L

LLL
L

OO
OO

 SS
SS

S
SS
 S

TTT
 T

T
TT
T

 T
TTT

 T
TT
 T

ZZ
 ZZ

 Z
ZZ
Z

Défi

Ecrivez un bloc de code rectangulaire qui sert de segment de base à partir duquel ces 19 formes sont créées. Lorsque ce code est organisé dans l'une des formes, un programme doit être formé pour générer la seule lettre majuscule associée à cette forme. Cela doit fonctionner pour les 19 formes.

Les principales zones vides présentes dans certaines des 19 formes sont entièrement remplies d’espaces ( ). Les dernières zones vides ne sont remplies de rien (les programmes ne sont donc pas toujours exactement rectangulaires).

Exemple

Supposons que ceci soit votre bloc de code:

ABC
123

Ensuite, l’un ou l’autre arrangement du bloc dans la pièce S Tetris serait un programme qui imprime S:

   ABCABC
   123123
ABCABC
123123

ABC
123
ABCABC
123123
   ABC
   123

(Notez que tous les espaces vides en tête sont remplis par des espaces et qu'aucune ligne ne comporte d'espaces de fin.)

La même idée s'applique aux 6 autres pièces et à leurs rotations respectives.

Remarques

  • Les 19 programmes finaux doivent être exécutés dans le même langage de programmation.
  • Si vous le souhaitez, vous pouvez ajouter une nouvelle ligne de fin à tous les programmes (pas seulement certains, tous ou aucun).
  • Votre bloc de code peut contenir des caractères (espaces compris) qui ne sont pas des fins de ligne .
  • Générez la lettre sur stdout (ou l'alternative la plus proche de votre langue) avec un retour à la ligne facultatif.

Notation

La soumission dont le bloc de code a la plus petite surface (largeur fois la hauteur) gagne. Cela signifie essentiellement que le code le plus court gagne, c'est pourquoi il s'agit du tagged . Tiebreaker va à la réponse la plus votée .

L' ABC\n123exemple a l'aire 3 × 2 = 6.

Fragment

Étant donné un bloc de code, cet extrait générera les 19 programmes:

<script>function drawShape(X,n,v){for(var t="",e=0;e<v.length;e++)for(var l=0;l<n.length;l++){for(var r=0;r<v[e].length;r++)t+="X"===v[e][r]?n[l]:X[l];t+="\n"}return t}function go(){var X=document.getElementById("input").value;if(0!=X.length){var n=X.replace(/./g," ").split("\n");X=X.split("\n");for(var v="I (v1):|I (v2):|J (v1):|J (v2):|J (v3):|J (v4):|L (v1):|L (v2):|L (v3):|L (v4):|O:|S (v1):|S (v2):|T (v1):|T (v2):|T (v3):|T (v4):|Z (v1):|Z (v2):".split("|"),t="X\nX\nX\nX|XXXX| X\n X\nXX|XXX\n  X|XX\nX\nX|X\nXXX|X\nX\nXX|  X\nXXX|XX\n X\n X|XXX\nX|XX\nXX| XX\nXX|X\nXX\n X|XXX\n X|X\nXX\nX| X\nXXX| X\nXX\n X|XX\n XX| X\nXX\nX".split("|"),e="",l=0;l<v.length;l++)e+=v[l]+"\n\n"+drawShape(n,X,t[l].split("\n"))+"\n";e=e.substring(0,e.length-2),document.getElementById("output").value=e}}</script><style>html *{font-family: monospace;}</style>Code Block:<br><textarea id='input' rows='8' cols='64'>ABC&#010;123</textarea><br><button type='button' onclick='go()'>Go</button><br><br>All 19 Programs:<br><textarea id='output' rows='24' cols='64'></textarea>

Les passe-temps de Calvin
la source
Le rapport longueur / largeur est donc de 2 à 3? Ou peut-il s'agir d'une autre taille? Aussi, que doit faire le programme au minimum? En supposant que les programmes vides ne comptent pas, les programmes ne produisant rien ne le font.
ASCIIThenANSI
@ASCIIThenANSI Toutes les largeurs et hauteurs sont bonnes. J'imagine que quelque chose de plus grand que 2 * 3 sera nécessaire. Il existe 19 programmes, un pour chaque disposition du bloc dans l’une des 19 formes distinctes de tétrominos. Lorsque l'un de ces programmes est exécuté, il génère la lettre de morceau de tetris correspondante.
Les passe-temps de Calvin
Hou la la! Quel défi formidable! La langue que nous utilisons est-elle importante?
theonlygusti
@theonlygusti Presque toutes les questions de ce site permettent toutes les langues. Ce n'est pas une exception.
Les passe-temps de Calvin
@ Calvin'sHobbies Ouais, je sais; Je viens de mal interpréter l'extrait de code en tant que contrôleur pour l'exécution de réponses JavaScript. Apparemment, il organise juste des blocs de code.
theonlygusti

Réponses:

16

<> <(Poisson) - 12 * 32 = 384

Je prévoyais une solution plus élégante, mais je me suis retrouvé avec ceci, ce qui est plutôt brutal:

c  0  g84*%\
c2*0  g84*%\
0  84*g84*%\
c  84*g84*%\
c2*84*g84*%\
0  88*g84*%\
c  88*g84*%\
?v         \
;>?v~~?vv   
"L" o;  >   
"S" o; >~?v 
"T" o;    > 
;  >~?v"L"o 
;     >"J"o 
?v         \
 >~?v~~?vv  
"I"  o;  >  
"J"  o; >   
    \~~?vv  
"T"  o;  >  
"Z"  o; >   
?v         \
 >?v"J"o;   
   >?v"Z"o; 
"L"o;>?!v   
"J"o;   >?v 
"T"o;     > 
?v?v"I"o;  >
   >"L"o;   
 >?v"T"o;   
   >?v"O"o; 
     >"S"o; 

C'est assez simple, il vérifie le code dans un carré 3x3 et utilise les résultats pour voir quel tétrimino correspond à la forme du code. Je n'ai pas encore pris beaucoup d'efforts pour jouer au golf.

Essayez le code ici (après avoir utilisé l'extrait pour le façonner comme un tétrimino)

Exemple de code en forme Z (v1) ici

Thijs ter Haar
la source
14

C (gcc) , 26x20 = 520 25x19 = 475 23x17 = 391

#ifndef M            //
#define M(a,b)a##b   //
#define W(z,x)M(z,x) //
char*s,*S[]={"!!!!c",//
"8M !7! M8 878","77",//
"7!!MO887","788OM!!7"//
,"N7!78","7N87!"},r[5//
],*p=r;i=7;main(){for//
(;i--;)strstr(S[i],r)//
&&putchar("ITOJLSZ"[i//
]);}                 //
#endif               //
__attribute__((      //
constructor(__LINE__)//
))W(f,__LINE__)(){s= //
"                     \
";*p++=strlen(s)+12;}//

J'ai récemment été informé des attributs de fonction de GNU et, plus intéressant encore, du constructor attribut, ce qui permet une mise en œuvre plus concise de ce que je faisais de manière plus détournée dans mon approche précédente de ce problème.

L’idée de base est la même que précédemment: créez une chaîne et recherchez-la dans une liste pour identifier le bloc de tétris dans lequel le code est présenté. Ceci est fait en appelant des fonctions, chacune ajoutant un caractère à la chaîne. La complication était et reste que le nombre de fonctions varie.

Définir une fonction avec attribute((constructor(x)))permet d’exécuter la fonction avant de la main()saisir, avec le paramètre optionnelx étant la priorité (plus faible signifie qu'elle est exécutée plus tôt). Cela supprime le besoin de pointeurs de fonction, ce qui nous permet de supprimer une macro, certaines déclarations et la chaîne d'appel.

En utilisant __LINE__ de la priorité est incertaine, car les niveaux de priorité 0 à 100 sont réservés. Cependant, cela n'entraîne pas d'erreurs, mais uniquement des avertissements, et ceux-ci sont nombreux lorsque vous jouez au golf.

Il aurait été utile de ne pas utiliser de priorités du tout dans une autre colonne, mais l'ordre d'exécution ne semble pas être défini. (Ils sont inversés dans ce cas, mais les autres tests ne sont pas concluants.)

Exemple de L v2 ici

Une approche plus ancienne et plus portable

#ifndef M              //
#define M(a,b) a##b    //
#define W(z,x)M(z,x)   //
#define F W(f,__LINE__)//
#define A W(a,__LINE__)//
char r[5],*S[]={"####k"//
,";<<US##;",";##SU<<;",//
";;",";T<;#","<S #;# S"//
"< <;<","T;#;<"},*s,*p=//
r;i;typedef(*T)();T a17//
,a36,a55,a74;main(){for//
(a17(),a36&&a36(),a55&&//
a55(),a74&&a74();i--;) //
strstr(S[i],r)&&putchar//
("ILJOZTS"[i]);}i=7;   //
#endif                 //
F();T A=F;F(){s=       //
"                       \
";*p++=strlen(s)+12;}  //

Un de mes problèmes préférés que j'ai résolus sur ce site.

J'ai commencé par imaginer que chaque bloc devinerait ses propres coordonnées. Les rangées sont faciles avec __LINE__, et le nombre de blocs adjacents horizontalement pourrait être trouvé en utilisant la longueur d'un littéral de chaîne, comme ceci:

char*s=//char*s=//
"       ""       "
;        ;        

Prenez la longueur de la chaîne résultante et divisez-la par un nombre approprié et vous aurez la largeur. Malheureusement, tout espace vide avant le bloc est invisible grâce à cette méthode. Je cordes encore suspectées serait la solution, puisque les espaces n'a de sens en dehors des cordes très rarement, dans des choses comme a+++bcontre a+ ++b. J'ai brièvement envisagé quelque chose comme ça, mais je n'ai rien trouvé d'utile. Une autre possibilité aurait été de laisser les identifiants être "collés" lorsque les blocs se rencontreraient:

A  BA  B

Je ne serais pas surpris si cela pouvait encore constituer une solution intéressante.

Malgré sa simplicité, il m'a fallu un certain temps pour trouver la solution de chaîne basée sur ce fragment de bloc:

s=//
"  \
";//

Si le fragment n'a pas de voisins horizontaux, le retour à la ligne de la deuxième ligne est échappé par la barre oblique inverse, créant ainsi une chaîne de longueur 2. Si, toutefois, il a un voisin, la barre oblique inverse échappera à la marque de mouvement au début de la ligne. 2 du bloc suivant:

s=//s=//
"  \"  \
";//";//

Cela créera la chaîne "\" "de longueur 5.

Plus important encore, cela permet également de détecter l’espace vide avant le bloc:

    s=//
    "  \
    ";//

Encore une fois, la nouvelle ligne est échappée et les espaces du bloc vide à gauche sont inclus dans la chaîne résultante "" de longueur 6.

Au total, il y a sept configurations différentes de blocs sur une ligne dont nous devons nous préoccuper, et toutes créent des chaînes de longueurs uniques:

2 "  "
---
s=//
"  \
";//

5 "  \"  "
---
s=//s=//
"  \"  \
";//";//

6 "      "
---
    s=//
    "  \
    ";//

9 "  \"      "
----
    s=//s=//
    "  \"  \
    ";//";//

10 "          "
---
        s=//
        "  \
        ";//

8 "  \"  \"  "
---
s=//s=//s=//
"  \"  \"  \
";//";//";//

11 "  \"  \"  \"  "
----
s=//s=//s=//s=//
"  \"  \"  \"  \
";//";//";//";//

Les derniers blocs n'auront bien sûr pas une longueur aussi courte, mais le principe est le même quelle que soit la taille du bloc. Cela a également l'avantage qu'un mécanisme séparé pour détecter la largeur n'est pas nécessaire. En ajoutant un caractère correspondant à la longueur de cette chaîne à une chaîne de résultats, chacune des 19 configurations génère une chaîne unique, qu'il suffit de comparer à une liste appropriée une fois que tous les blocs ont été exécutés.

Une fois cela réglé, le problème suivant était de savoir comment "visiter" chaque rangée de blocs. En C, nous sommes très limités à ce qui peut être fait en dehors des fonctions. Nous devons également main()comparaître, mais une seule fois. Ce dernier est facilement réalisé par certains #defines, mais si nous voulons que le code des blocs suivants soit à l’intérieur de main(), le problème est de savoir quand mettre l’accolade de fermeture finale. Après tout, nous ne savons pas combien de rangées de blocs seront réellement utilisées. Nous avons donc besoin d'avoirmain() statique et le reste pour être dynamique.

Si les autres lignes de bloc doivent être autonomes, elles doivent être des fonctions, mais nous devons nous assurer que chaque fonction a un nom unique, tout en étant suffisamment prévisible pour pouvoir être appelé main(). Nous avons également besoin d'un mécanisme pour savoir quelles fonctions sont réellement appelées. La génération de noms uniques est résolue par les macros helper:

#define M(a,b) a##b     //
#define W(z,x)M(z,x)    //
#define F W(f,__LINE__) //
#define A W(a,__LINE__) //

L'appel Fcréera un identifiant dont le nom commence par un f et se termine par le numéro de ligne. Afait de même, mais avec un préfixe as, utilisé pour la deuxième partie de la solution, à savoir les pointeurs de fonction. Nous déclarons quatre de ces pointeurs:

typedef(*T)();T a17,a36,a55,a74;

Comme elles sont déclarées en tant que variables globales, elles ont la valeur NULL. Plus tard, chaque bloc-ligne aura le code suivant:

F();T A=F;F()

Cela va d'abord déclarer une fonction, définir le pointeur de fonction approprié pour pointer sur cette fonction (nous ne pouvons définir qu'une seule fois les globales, mais la déclaration précédente ne comptait pas comme une définition, même si elle était initialisée à NULL), puis définissait la valeur réelle. une fonction. Cela permet main()d’appeler n’importe quel pointeur de fonction non NULL (a17 ne sera jamais NULL):

a17(),a36&&a36(),a55&&a55(),a74&&a74()

Cela va construire la chaîne r , qui sera ensuite recherchée dans la table des chaînes et, si elle est trouvée, la lettre appropriée est générée.

La seule astuce restante consiste à raccourcir la liste des chaînes à comparer, chaque fois que l’ambiguïté peut être évitée, ou à fusionner les chaînes qui se chevauchent.

Exemple de L v2 ici

gastropner
la source
6

Opcode x86 (.com), 86 82 octets

Testeur:

org 100h
macro e {
db $F6,$04,$DF,$78,$13,$75,$08,$00,$C0,$40,$83,$C6,$52,$EB,$F1,$88
db $C2,$00,$D0,$00,$D0,$46,$EB,$E8,$05,$02,$40,$73,$ED,$E8,$26,$00
db $50,$08,$43,$4D,$2C,$0C,$1C,$15,$A5,$14,$10,$13,$3F,$27,$20,$0F
db $51,$1D,$29,$49,$49,$4A,$4A,$4A,$4A,$4C,$4C,$4C,$4C,$4F,$53,$53
db $54,$54,$54,$54,$5A,$5A,$5F,$AE,$75,$FD,$8A,$55,$12,$B4,$02,$CD
db $21,$C3
}

macro n { db 82 dup $20 }

macro s { db 10 }

n
e
s
n
e
s
e
e  

La source:

BOF:    ;mov bx, 100h
p:      test [si], byte $DF
        js _a ; exist
        jnz _b ; newline
_z:     add al, al
        inc ax
q:      add si, EOF-BOF
        jmp p
_b:     mov dl, al
        add al, dl
        add al, dl
        inc si
        jmp p
_a:     add ax, 4002h
        jnc q
        call y
        db 80,8,67,77,44,12,28,21,165,20,16,19,63,39,32,15,81,29,41
        db 'IIJJJJLLLLOSSTTTTZZ'
y:      pop di
        scasb
        jnz y+1
        mov dl,[di+18]
        mov ah,2
        int $21
        ret
EOF:

Exécuter dans win7dos où init AX = 0, SI = 100, BX = 0 Références

l4m2
la source
Si vous êtes prêt à réduire quelque peu le nombre d'environnements pris en charge, vous pouvez supposer que SI = 100h et utiliser ce registre à la place de BX pour l'indexation, pour économiser 3 octets en supprimant mov bx, 100hle début.
gastropner
@gastropner Fait et fixe un point où je n'ai pas remarqué
l4m2