Programando blocos Tetris (literalmente)

33

No jogo Tetris , existem 7 tipos de tijolos ou tetr i Minoes , os quais são matematicamente conhecidos como tetr o Minoes porque eles são todos feitos com 4 segmentos quadrados:

Tijolos de Tetris

Eles têm os nomes I, J, L, O, S, T e Z, que correspondem às suas formas aproximadas. Contando rotações de 90 °, existem 19 formas únicas no 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

Desafio

Escreva um bloco retangular de código que atue como o segmento base de onde são feitas essas 19 formas. Quando esse código é organizado em uma das formas, deve ser formado um programa que produza a única letra maiúscula associada a essa forma. Isso deve funcionar para todas as 19 formas.

As principais áreas vazias presentes em algumas das 19 formas são preenchidas inteiramente com espaços ( ). As áreas vazias à direita não são preenchidas com nada (portanto, os programas nem sempre são exatamente retangulares).

Exemplo

Suponha que este fosse seu bloco de código:

ABC
123

Então, qualquer disposição do bloco na peça S Tetris seria um programa que imprime S:

   ABCABC
   123123
ABCABC
123123

ABC
123
ABCABC
123123
   ABC
   123

(Observe que todo o espaço vazio inicial é preenchido com caracteres de espaço e que nenhuma linha possui espaços à direita.)

A mesma idéia se aplica a todas as outras 6 peças e suas respectivas rotações.

Notas

  • Todos os 19 programas finais devem ser executados na mesma linguagem de programação.
  • Se desejar, você pode adicionar uma nova linha final à todos os programas (não apenas alguns, todos ou nenhum).
  • Seu bloco de código pode conter caracteres (incluindo espaços) que não são terminadores de linha .
  • Envie a carta para stdout (ou a alternativa mais próxima do seu idioma) com uma nova linha à direita opcional.

Pontuação

A submissão cujo bloco de código possui a menor área (largura x altura) vence. Isso significa essencialmente que o código mais curto vence, e é por isso que esse é marcado como . O desempatador vai para a resposta mais votada .

O ABC\n123exemplo tem a área 3 × 2 = 6.

Snippet

Dado um bloco de código, esse trecho irá gerar todos os 19 programas:

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

Passatempos de Calvin
fonte
Então a relação comprimento-largura é de 2 a 3? Ou pode ter outro tamanho? Além disso, o que o programa precisa fazer, no mínimo? Supondo que programas vazios não contam, mas programas que não produzem nada contam.
ASCIIThenANSI
@ASCIIThenANSI Qualquer largura e altura são boas. Eu imagino que algo maior que 2 * 3 será necessário. Existem 19 programas, um para cada arranjo do bloco em uma das 19 formas distintas de tetromino. Quando um desses programas é executado, ele gera a letra correspondente à peça do tetris.
Calvin's Hobbies
Uau! Que desafio incrível! Importa qual idioma usamos?
theonlygusti
@theonlygusti Quase todas as perguntas neste site permitem qualquer idioma. Isso não é exceção.
Hobbies de Calvin
@ Calvin'sHobbies Sim, eu sei; Acabei de interpretar mal o trecho como um controlador para executar respostas em JavaScript. Aparentemente, ele apenas organiza os blocos de código.
theonlygusti

Respostas:

16

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

Eu estava planejando buscar uma solução mais elegante, mas de alguma forma acabei com isso, que é bastante 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; 

É bem simples, verifica o código em um quadrado de 3x3 em busca de texto e usa os resultados para ver qual tetrimino corresponde à forma do código. Ainda não me esforcei muito para jogar golfe.

Experimente o código aqui (depois de usar o snippet para modelá-lo como um tetrimino)

Exemplo de código na forma Z (v1) aqui

Thijs ter Haar
fonte
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;}//

Fui informado recentemente dos atributos de função do GNU, e o mais interessante, o constructoratributo, que permite uma implementação mais concisa do que eu estava fazendo de uma maneira mais indireta na minha abordagem anterior a esse problema.

O impulso da idéia é o mesmo de antes: Crie uma string e procure-a em uma lista para identificar em qual bloco de tetris o código está definido. Isso é feito chamando funções, cada uma adicionando um caractere à string. A complicação foi e continua sendo que o número de funções varia.

Definir uma função com attribute((constructor(x)))faz com que a função seja executada antes de main()ser inserida, com o opcional xsendo a prioridade (menor significa que ela é executada anteriormente). Isso elimina a necessidade de ponteiros de função, o que nos permite eliminar uma macro, algumas declarações e a cadeia de chamada.

O uso __LINE__da prioridade é duvidoso, pois os níveis de prioridade de 0 a 100 são reservados. No entanto, isso não resulta em erros, apenas avisos, e esses são muitos quando se joga, então o que é mais?

Ajudaria a raspar outra coluna para não usar prioridades, mas a ordem de execução não parece estar definida. (Eles são revertidos neste caso, mas outros testes são inconclusivos.)

Exemplo de L v2 aqui

Abordagem mais antiga e mais portátil

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

Um dos meus problemas favoritos que resolvi neste site.

Comecei imaginando que cada bloco iria adivinhar suas próprias coordenadas de alguma forma. As linhas são fáceis de usar __LINE__e o número de blocos adjacentes horizontalmente pode ser encontrado usando o comprimento de uma cadeia de caracteres literal, assim:

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

Pegue o comprimento da string resultante e divida por um número apropriado e você terá a largura. Infelizmente, qualquer espaço vazio antes do bloco é invisível por esse método. Eu ainda suspeitos cordas seria a solução, uma vez que os espaços em branco só tem sentido fora das cordas muito raramente, em coisas como a+++bvs. a+ ++b. Eu considerei brevemente algo assim, mas não consegui encontrar nada útil. Outra possibilidade seria permitir que os identificadores fossem "colados" juntos onde os blocos se encontrassem:

A  BA  B

Eu não ficaria surpreso se isso ainda pudesse constituir uma solução interessante.

Apesar de sua simplicidade, levei algum tempo para encontrar a solução de string, baseada neste fragmento de bloco:

s=//
"  \
";//

Se o fragmento não tiver vizinhos horizontais, a nova linha na segunda linha será escapada pela barra invertida, criando uma sequência de comprimento 2. Se, no entanto, tiver um vizinho, a barra invertida escapará da marca de aspas no início da linha 2 do próximo bloco:

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

Isso criará a string "\" "de comprimento 5.

Mais crucialmente, isso também permite a detecção de espaço vazio antes do bloco:

    s=//
    "  \
    ";//

Novamente, a nova linha é escapada e o espaço em branco do bloco vazio à esquerda é incluído na sequência "" resultante de comprimento 6.

No total, existem sete configurações diferentes de blocos em uma linha com as quais precisamos nos preocupar, e todas elas formam cadeias de comprimentos únicos:

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

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

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

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

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

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

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

Obviamente, os blocos finais não terão tamanho tão curto, mas o princípio é o mesmo, independentemente do tamanho do bloco. Isso também tem o bônus de que um mecanismo separado para detectar a largura é desnecessário. Ao adicionar um caractere correspondente ao comprimento dessa string a uma string de resultados, cada uma das 19 configurações produz uma string única, que precisa ser comparada apenas a uma lista adequada depois que todos os blocos forem executados.

Depois que isso foi resolvido, o próximo grande problema foi como "visitar" cada linha de blocos. Em C, estamos muito limitados ao que pode ser feito fora das funções. Também precisamos main()aparecer, mas apenas uma vez. O último é facilmente alcançado por alguns #defines, mas se queremos que o código dos blocos subseqüentes esteja dentro main(), o problema de como saber quando colocar o colchete final de fechamento. Afinal, não sabemos quantas linhas de blocos serão realmente usadas. Então, precisamos ter main()estática e, de alguma forma, o resto para ser dinâmico.

Se as outras linhas de bloco devem ser independentes, elas precisam ser funções, mas precisamos garantir que cada função tenha um nome exclusivo, além de ser previsível o suficiente para ser possível chamar main(). Também precisamos de um mecanismo para saber quais funções realmente existem para serem chamadas. A geração de nomes exclusivos é resolvida por macros auxiliares:

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

A chamada Fcriará um identificador cujo nome começa com um f e termina com o número da linha. Afaz o mesmo, mas com um prefixo as, que é usado para a segunda parte da solução, que é ponteiros de função. Declaramos quatro desses indicadores:

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

Como essas são declaradas como variáveis ​​globais, elas são convenientemente definidas como NULL. Posteriormente, cada linha de bloco terá o seguinte trecho de código:

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

Isso primeiro declarará uma função, definirá o ponteiro de função apropriado para apontar para essa função (só podemos definir globais uma vez, mas a declaração anterior não contou como definição, mesmo se inicializou como NULL) e, em seguida, defina o real função. Isso permite main()chamar qualquer ponteiro de função que não seja NULL (a17 nunca será NULL):

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

Fazer isso criará a string r, que é procurada na tabela de strings e, se encontrada, a letra apropriada será impressa.

O único truque restante é que a lista de strings a serem comparadas era reduzida sempre que a ambiguidade pudesse ser evitada, ou strings sobrepostos podiam ser confundidos.

Exemplo de L v2 aqui

gastropner
fonte
6

x86 opcode (.com), 86 82 bytes

Testador:

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  

Fonte:

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:

Executar em win7dos onde o init AX = 0, SI = 100, BX = 0 Referências

l4m2
fonte
Se você se sente à vontade para reduzir um pouco o número de ambientes suportados, pode assumir SI = 100h e usar esse registro em vez de BX para indexação, para economizar 3 bytes soltando mov bx, 100hno início.
Gastropner 01/12/19
@gastropner Feito e fixa um ponto onde eu não notei
l4m2