Programmazione di blocchi Tetris (letteralmente)


33

Nel gioco Tetris , ci sono 7 tipi di mattoni o Tetr i minoes , che sono matematicamente conosciuti come tetr o minoes perché sono tutti realizzati con 4 segmenti quadrati:

Mattoni di Tetris

Hanno i nomi I, J, L, O, S, T e Z, che corrispondono alle loro forme approssimative. Contando le rotazioni di 90 °, ci sono 19 forme uniche in totale:

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

Sfida

Scrivi un blocco rettangolare di codice che funge da segmento di base da cui sono ricavate queste 19 forme. Quando questo codice è organizzato in una delle forme, dovrebbe essere formato un programma che genera la singola lettera maiuscola associata a quella forma. Questo deve funzionare per tutte e 19 le forme.

Le principali aree vuote presenti in alcune delle 19 forme sono riempite interamente con spazi ( ). Le aree vuote finali non sono piene di nulla (quindi i programmi non sono sempre esattamente rettangolari).

Esempio

Supponiamo che questo fosse il tuo blocco di codice:

ABC
123

Quindi una delle disposizioni del blocco nel pezzo S Tetris sarebbe un programma che stampa S:

   ABCABC
   123123
ABCABC
123123

ABC
123
ABCABC
123123
   ABC
   123

(Si noti che tutto lo spazio vuoto iniziale è pieno di caratteri spazio e che nessuna riga ha spazi finali).

La stessa idea si applica a tutti gli altri 6 pezzi e alle rispettive rotazioni.

Gli appunti

  • Tutti i 19 programmi finali devono essere eseguiti nello stesso linguaggio di programmazione.
  • Se lo si desidera, è possibile aggiungere una sola nuova riga finale a tutti i programmi (non solo alcuni, tutti o nessuno).
  • Il blocco di codice può contenere caratteri (inclusi spazi) che non sono terminatori di riga .
  • Invia la lettera a stdout (o l'alternativa più vicina alla tua lingua) con una nuova riga finale opzionale.

punteggio

Vince l'invio il cui blocco di codice ha l'area più piccola (larghezza per altezza). Ciò significa essenzialmente che vince il codice più corto, motivo per cui questo è contrassegnato come . Tiebreaker va alla risposta più votata .

L' ABC\n123esempio ha l'area 3 × 2 = 6.

Frammento

Dato un blocco di codice, questo frammento genererà tutti i 19 programmi:

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


Quindi il rapporto lunghezza-larghezza è da 2 a 3? O può essere di qualsiasi altra dimensione? Inoltre, cosa deve fare il programma, come minimo? Supponendo che i programmi vuoti non contino, ma i programmi che non generano nulla non lo fanno.
ASCIIThenANSI,

@ASCIIThenANSI Qualsiasi larghezza e altezza vanno bene. Immagino che sarà necessario qualcosa di più grande di 2 * 3. Esistono 19 programmi, uno per ciascuna disposizione del blocco in una delle 19 distinte forme di tetromino. Quando viene eseguito uno di questi programmi, viene emessa la corrispondente lettera di tetris.
Calvin's Hobbies,

Wow! Che sfida fantastica! Importa quale lingua utilizziamo?
theonlygusti,

@theonlygusti Quasi tutte le domande su questo sito consentono qualsiasi lingua. Questa non fa eccezione.
Calvin's Hobbies,

@ Calvin'sHobbies Sì, lo so; Ho appena interpretato erroneamente lo snippet come controller per l'esecuzione di risposte JavaScript. Apparentemente organizza solo blocchi di codice.
theonlygusti,

Risposte:


16

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

Avevo intenzione di cercare una soluzione più elegante, ma in qualche modo ho finito con questo, che è piuttosto forza bruta:

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; 

È piuttosto semplice, controlla il codice in un quadrato 3x3 per il testo e usa i risultati per vedere quale tetrimino corrisponde alla forma del codice. Non ho ancora fatto molti sforzi per giocare a golf.

Prova qui il codice (dopo aver utilizzato lo snippet per modellarlo come un tetrimino)

Esempio di codice in forma Z (v1) qui


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

Sono stato recentemente informato degli attributi della funzione GNU e, cosa più interessante, l' constructorattributo, che consente un'implementazione più concisa di ciò che stavo facendo in modo più indiretto nel mio approccio precedente a questo problema.

La spinta dell'idea è la stessa di prima: costruire una stringa e cercarla in un elenco per identificare in quale blocco tetris è disposto il codice. Questo viene fatto chiamando le funzioni, ognuna aggiungendo un carattere alla stringa. La complicazione era e rimane che il numero di funzioni varia.

La definizione di una funzione con attribute((constructor(x)))fa sì che la funzione venga eseguita prima di main()essere immessa, con l'opzione facoltativa xcome priorità (inferiore significa che viene eseguita prima). Ciò elimina la necessità di puntatori a funzione, che ci consente di eliminare una macro, alcune dichiarazioni e la catena di chiamata.

L'uso __LINE__per la priorità è iffy, poiché i livelli di priorità 0-100 sono riservati. Tuttavia, non si traducono in errori, solo in avvertimenti e quelli sono abbondanti quando si gioca a golf, quindi cosa c'è di più?

Avrebbe aiutato a radere via un'altra colonna per non usare affatto le priorità, ma l'ordine di esecuzione non sembra essere definito. (In questo caso sono invertiti, ma altri test non sono conclusivi.)

Esempio di L v2 qui

Approccio più vecchio, più portatile

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

Uno dei miei problemi preferiti che ho risolto su questo sito.

Ho iniziato immaginando che ogni blocco divinasse in qualche modo le proprie coordinate. Le righe sono facili con __LINE__, e il numero di blocchi orizzontalmente adiacenti può essere trovato usando la lunghezza di una stringa letterale, in questo modo:

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

Prendi la lunghezza della stringa risultante e dividi per un numero appropriato e avrai la larghezza. Sfortunatamente, qualsiasi spazio vuoto prima del blocco è invisibile con questo metodo. Ho ancora le stringhe sospetti sarebbe la soluzione, dal momento che gli spazi bianchi solo ha senso al di fuori delle corde molto raramente, in cose come a+++bvs. a+ ++b. Ho considerato brevemente qualcosa del genere, ma non sono riuscito a trovare nulla di utile. Un'altra possibilità sarebbe stata quella di "incollare" gli identificatori dove i blocchi si incontravano:

A  BA  B

Non sarei sorpreso se questo potesse ancora costituire una soluzione interessante.

Nonostante la sua semplicità, mi ci è voluto un po 'di tempo per trovare la soluzione di stringa, che si basa su questo frammento di blocco:

s=//
"  \
";//

Se il frammento non ha vicini orizzontali, la nuova riga sulla seconda riga viene salvata dalla barra rovesciata, creando una stringa di lunghezza 2. Se, tuttavia, ha un vicino, la barra inversa sfuggirà invece al segno di quotion all'inizio della riga 2 del blocco successivo:

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

Questo creerà la stringa "\" "di lunghezza 5.

Ancora più importante, ciò consente anche il rilevamento di spazio vuoto prima del blocco:

    s=//
    "  \
    ";//

Ancora una volta, la nuova riga viene salvata e lo spazio bianco del blocco vuoto a sinistra viene incluso nella stringa risultante "" di lunghezza 6.

In totale ci sono sette diverse configurazioni di blocchi su una riga di cui dobbiamo preoccuparci e fanno tutti stringhe di lunghezze uniche:

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

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

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

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

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

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

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

Naturalmente i blocchi finali non avranno una lunghezza così breve, ma il principio è lo stesso indipendentemente dalle dimensioni del blocco. Questo ha anche il vantaggio che non è necessario un meccanismo separato per rilevare la larghezza. Aggiungendo un carattere corrispondente alla lunghezza di questa stringa a una stringa di risultati, ognuna delle 19 configurazioni produce una stringa univoca, che deve essere confrontata con un elenco adatto solo dopo che tutti i blocchi sono stati eseguiti.

Una volta che questo è stato risolto, il prossimo grosso problema era come "visitare" ogni fila di blocchi. In C, siamo molto limitati a ciò che può essere fatto al di fuori delle funzioni. Dobbiamo anche main()apparire, ma solo una volta. Quest'ultimo è facilmente raggiungibile da alcuni #defines, ma se vogliamo che il codice dei blocchi successivi sia all'interno di main(), il problema di come sapere quando mettere la parentesi graffa di chiusura finale. Dopotutto, non sappiamo quante file di blocchi verranno effettivamente utilizzate. Quindi dobbiamo avere main()statico e in qualche modo il resto per essere dinamico.

Se le altre righe di blocco devono essere autosufficienti, devono essere funzioni, ma dobbiamo assicurarci che ogni funzione abbia un nome univoco, pur essendo abbastanza prevedibile da poter essere richiamata main(). Abbiamo anche bisogno di un meccanismo per sapere quali funzioni sono effettivamente lì per essere chiamate. La generazione di nomi univoci è risolta dalle macro helper:

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

La chiamata Fcreerà un identificatore il cui nome inizia con una f e termina con il numero di riga. Afa lo stesso ma con un prefisso as, che viene utilizzato per la seconda parte della soluzione, ovvero i puntatori a funzione. Dichiariamo quattro di questi puntatori:

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

Poiché sono dichiarate come variabili globali, sono comodamente impostate su NULL. Successivamente, ogni riga di blocco avrà il seguente codice:

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

Questo prima dichiarerà una funzione, definirà il puntatore della funzione appropriata per puntare a quella funzione (possiamo definire i globali solo una volta, ma la dichiarazione precedente non contava come una definizione, anche se inizializzava su NULL), quindi definire l'effettivo funzione. Ciò consente main()di chiamare qualsiasi puntatore a funzione diverso da NULL (a17 non sarà mai NULL):

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

In questo modo verrà creata la stringa r, che viene quindi cercata nella tabella delle stringhe e, se trovata, viene emessa la lettera appropriata.

L'unico trucco rimanente è che l'elenco delle stringhe con cui confrontarsi veniva ridotto ogni volta che si poteva evitare l'ambiguità o si potevano confondere stringhe sovrapposte.

Esempio di L v2 qui


6

x86 opcode (.com), 86 82 byte

Tester:

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:

Esegui in win7dos dove init AX = 0, SI = 100, BX = 0 Riferimenti


Se ti senti a tuo agio a ridurre un po 'il numero di ambienti supportati, puoi assumere SI = 100h e usare quel registro invece di BX per l'indicizzazione, per salvare 3 byte facendo cadere mov bx, 100hall'inizio.
Gastropner

@gastropner Fatto e fissato un punto in cui non me ne sono accorto
l4m2
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.