Code Golf: Jugar al Tetris

83

Los basicos:

Considere los siguientes tetrominós y el campo de juego vacío:

                                            0123456789
    IOZTLSJ []
                                           []
    # ## ## ### # ## # []
    # ## ## # # ## # []
    # ## ## []
    # []
                                           [==========]

Las dimensiones del campo de juego son fijas. Los números en la parte superior están aquí para indicar el número de columna (ver también la entrada).

Entrada:

1 . Se le da un campo de juego específico (basado en lo anterior) que ya se puede llenar parcialmente con tetrominós (esto puede estar en un archivo separado o proporcionado a través de stdin).

Entrada de muestra:

[]
[]
[]
[]
[# # #]
[## ######]
[==========]

2 . Se le da una cadena que describe (separada por espacios) qué tetromino insertar (y desplegar) en qué columna. No es necesario rotar los tetrominós. La entrada se puede leer desde stdin.

Entrada de muestra:

T2 Z6 I0 T7

Puede asumir que la entrada está 'bien formada' (o producir un comportamiento indefinido cuando no lo está).

Salida

Procese el campo resultante (las líneas 'completas' deben desaparecer) e imprima el recuento de puntuación (cada línea eliminada representa 10 puntos).

Salida de muestra basada en la entrada de muestra anterior:

[]
[]
[]
[# ###]
[# ###]
[##### ####]
[==========]
10

Ganador:

Solución más corta (por número de caracteres del código). Los ejemplos de uso son buenos. ¡Diviértete jugando al golf!

Editar : agregó una recompensa de +500reputación para llamar más la atención sobre los buenos esfuerzos que los que respondieron ya hicieron (y posiblemente algunas nuevas soluciones a esta pregunta) ...

ChristopheD
fuente
5
@omouse: ver meta.stackoverflow.com - el código de golf generalmente está permitido (en forma de wiki de la comunidad)
ChristopheD
18
@omouse: Para eso es votar para cerrar. Arrastrar a los moderadores aquí marcando la pregunta probablemente no lo hará tan popular, dado que una y otra vez la comunidad ha permitido (a regañadientes) que exista el código de golf (vea la etiqueta de código de golf y las meta discusiones; no es nada nuevo) .
Mark Peters
8
@omouse: ¡Fuera de tema! = spam. Incluso si no puede votar para cerrar, esa marca de spam no fue necesaria.
BoltClock
3
¡Estoy esperando un atleta de APL! Apuesto a que puede hacerlo en 3.5 símbolos
n8wrl
3
Se supone que las dimensiones son fijas, pero la entrada de muestra y el campo en blanco tienen alturas diferentes. ¿Cuál se supone que es la altura?
Nabb

Respuestas:

27

GolfScript - 181 caracteres

Las nuevas líneas no son necesarias. La salida está en la salida estándar, aunque hay algunos errores en stderr.
\10debe reemplazarse por el carácter ASCII correspondiente para que el programa tenga 181 caracteres.

{):X!-{2B{" #"=}%X" ":f*+-1%}%:P;:>.{\!:F;>P{\(@{3&\(@.2$&F|:F;|}%\+}%\+F![f]P+:P
;}do;{"= "&},.,7^.R+:R;[>0="#"/f*]*\+}0"R@1(XBc_""~\10"{base}:B/3/~4*"nIOZTLSJR "
";:"*~;n%)n*~ 10R*+n*

E / S de muestra:

$ cat inp
[          ]
[          ]
[          ]
[          ]
[ #    #  #]
[ ## ######]
[==========]
T2 Z6 I0 T7
$ cat inp|golfscript tetris.gs 2>/dev/null
[          ]
[          ]
[          ]
[#      ###]
[#     ### ]
[##### ####]
[==========]
10

Compresión Tetromino: las
piezas se almacenan como tres dígitos base 8. Esta es una representación binaria simple, por ejemplo T=[7,2,0], S=[6,3,0], J=[2,2,3]. [1]se utiliza para la Ipieza en compresión, pero se establece explícitamente en [1,1,1,1]later (es decir, 4*en el código). Todas estas matrices se concatenan en una sola matriz, que se convierte en un número entero y luego en una cadena (base 126 para minimizar los caracteres no imprimibles, la longitud y no encontrar utf8). Esta cadena es muy corta: "R@1(XBc_".

Entonces, la descompresión es sencilla. Primero hacemos una conversión base 126 seguida de una conversión base 8 ( "~\10"{base}/es decir, iteramos "~\10"y hacemos una conversión base para cada elemento). La matriz resultante se divide en grupos de 3, la matriz para Ies fija ( 3/~4*). Luego convertimos cada elemento a base 2 y (después de eliminar los ceros) reemplazamos cada dígito binario con el carácter de ese índice en la cadena " #"( 2base{" #"=}%...-1%- tenga en cuenta que necesitamos revertir la matriz, de lo contrario 2se convertiría en "# "lugar de " #").

Formato tablero / pieza, dejando caer piezas
El tablero es simplemente una matriz de cuerdas, una para cada línea. No se trabaja inicialmente en esto, por lo que podemos generarlo con n/(la entrada. Las piezas también son matrices de cadenas, rellenas con espacios a la izquierda para su posición X, pero sin espacios finales. Las piezas se colocan anteponiéndolas a la matriz y probando continuamente si hay una colisión.

La prueba de colisión se realiza iterando a través de todos los personajes de la pieza y comparándola con el personaje de la misma posición en el tablero. Queremos considerar #+ =y #+ #como colisiones, por lo que probamos si ((piecechar & 3) & boardchar) es distinto de cero. Mientras hacemos esta iteración, también actualizamos (una copia de) el tablero con ((piecechar & 3) | boardchar), que establece correctamente el valor para los pares #+ , + #, + [. Usamos este tablero actualizado si hay una colisión después de mover la pieza hacia abajo en otra fila.

Eliminar filas llenas es bastante simple. Eliminamos todas las filas para las que "= "&devuelve falso. Una fila llena no tendrá ni =o , por lo que la conjunción será una cadena en blanco, lo que equivale a falso. Luego contamos el número de filas que se han eliminado, sumamos el recuento a la puntuación y anteponemos esa cantidad de "[ ... ]"s. Generamos esto de forma compacta tomando la primera fila de la cuadrícula y reemplazándola #con .

Bonificación
Dado que calculamos cómo se vería el tablero en cada posición de la pieza a medida que cae, ¡podemos mantenerlos en la pila en lugar de eliminarlos! Para un total de tres caracteres más, podemos generar todas estas posiciones (o dos caracteres si tenemos los estados del tablero a espacio simple).

{):X!-{2B{" #"=}%X" ":f*+-1%}%:P;:>.{>[f]P+:P(!:F;{\(@{3&\(@.2$&F|:F;|}%\+}%\+F!}
do;{"= "&},.,7^.R+:R;[>0="#"/f*]*\+}0"R@1(XBc_""~\10"{base}:B/3/~4*"nIOZTLSJR "
";:"*~;n%)n*~ ]{n*n.}/10R*
Nabb
fuente
Aquí hay algo de golf de código extremo (no pensé que esto se pudiera hacer en menos de 200 caracteres). ¡Buen trabajo!
ChristopheD
8
Asombroso. Ojalá entendiera GolfScript. Espera ... no, no lo hago.
P Daddy
26

Perl, 586523483472427407404386387356353 caracteres

(Necesita Perl 5.10 para el //operador definido-or ).

Toma toda la entrada de stdin. Todavía necesita algo de golf en serio.
Tenga en cuenta que ^ Q representa ASCII 17 (DC1 / XON), ^ C representa ASCII 3 y ^ @ representa ASCII 0 (NUL).

while(<>){push@A,[split//]if/]/;while(/\w/g){for$i(0..6){for($f=0,$j=4;$j--;){$c=0;map{if($_){$i--,$f=$j=3,redo if$A[$k=$i+$j][$C=$c+$'+1]ne$";$A[$k][$C]="#"if$f}$c++}split//,unpack"b*",chr vec"3^@'^@c^@^Q^C6^@\"^C^Q^Q",index(OTZLSJI,$&)*4+$j,4;$s+=10,@A[0..$k]=@A[$k,0..$k-1],map{s/#/ /}@{$A[0]},$i++if 9<grep/#/,@{$A[$k]}}last if$f}}}print+(map@$_,@A),$s//0,$/

Versión comentada:

while(<>){
    # store the playfield as an AoA of chars
    push@A,[split//]if/]/;
    # while we're getting pieces
    while(/\w/g){
            # for each line of playfield
            for$i(0..6){
                    # for each line of current piece
                    for($f=0,$j=4;$j--;){
                            # for each column of current piece
                            $c=0;
                            map{
                                    if($_){
                                            # if there's a collision, restart loop over piece lines
                                            # with a mark set and playfield line decremented
                                            $i--,$f=$j=3,redo if$A[$k=$i+$j][$C=$c+$'+1]ne$";
                                            # if we already found a collision, draw piece
                                            $A[$k][$C]="#"if$f
                                    }
                                    $c++
                            # pieces are stored as a bit vector, 16 bits (4x4) per piece,
                            # expand into array of 1's and 0's
                            }split//,unpack"b*",chr vec"3^@'^@c^@^Q^C6^@\"^C^Q^Q",index(OTZLSJI,$&)*4+$j,4;
                            # if this playfield line is full, remove it. Done by array slicing
                            # and substituting all "#"'s in line 0 with " "'s
                            $s+=10,@A[0..$k]=@A[$k,0..$k-1],map{s/#/ /}@{$A[0]},$i++if 9<grep/#/,@{$A[$k]}
                    }
                    # if we found a collision, stop iterating over the playfield and get next piece from input
                    last if$f
            }
    }
}
# print everything
print+(map@$_,@A),$s//0,$/

Edición 1: algo de golf serio, corrección de errores de salida.
Edición 2: algo de inserción, fusionó dos bucles en uno para un ahorro neto de (redoble de batería ...) 3 caracteres, golf misceláneo.
Edición 3: algunas eliminaciones de subexpresiones comunes, un poco de fusión constante y una expresión regular ajustada.
Edición 4: cambio de representación de tetrominós en un vector de bits empaquetado, golf misceláneo.
Edición 5: traducción más directa de la letra tetromino al índice de la matriz, use caracteres no imprimibles, golf misceláneo.
Edición 6: corrección de errores en la línea superior de limpieza, introducida en r3 (edición 2), detectada por Nakilon. Utilice más caracteres no imprimibles.
Edición 7: utilícelo vecpara obtener datos de tetromino. Aprovecha que el campo de juego tiene dimensiones fijas. ifdeclaración =>ifmodificador, la fusión de bucles de la edición 2 comienza a dar sus frutos. Úselo //para el caso de puntuación 0.
Edición 8: se corrigió otro error, introducido en r6 (edición 5), detectado por Nakilon.
Edición 9: no cree nuevas referencias al borrar líneas, simplemente mueva las referencias a través del corte de matriz. Fusiona dos mapen uno. Regex más inteligente. "Más inteligente" for. Campos de golf varios.
Edición 10: matriz de tetromino en línea, versión comentada agregada.

ninjalj
fuente
Funciona muy bien (y ya con un buen recuento de caracteres para este problema no trivial). Una pequeña peculiaridad es que mi perl (perl, v5.10.0 construido para darwin-thread-multi-2level) parece imprimir el resultado dos veces (entrada canalizada).
ChristopheD
@ChristopheD: arregló la salida duplicada, estaba imprimiendo dentro de mi bucle principal, pero solo para líneas sin campo de juego. Probablemente
tuviste
4 caracteres más para vencer a Python !!
Vivin Paliath
1
¡Todavía no me he rendido a Perl! xD (Aunque me gustaría ver algunas otras soluciones a estas alturas ...)
pinche el
@Nakilon: ¡Buen partido! Tienes un buen caso de prueba ahí.
ninjalj
24

Ruby - 427 408 398 369 359

t=[*$<]
o=0
u=->f{f.transpose}
a=u[t.reverse.join.scan /#{'( |#)'*10}/]
t.pop.split.map{|w|m=(g='I4O22Z0121T01201L31S1201J13'[/#{w[0]}\d+/].scan(/0?\d/).zip a.drop w[1].to_i).map{|r,b|(b.rindex ?#or-1)-r.size+1}.max
g.map{|r,b|b.fill ?#,m+r.size,r.to_i}
v=u[a]
v.reject!{|i|i-[?#]==[]&&(o+=10;v)<<[' ']*10}
a=u[v]}
puts u[a].reverse.map{|i|?[+i*''+?]},t[-1],o
Nakilon
fuente
¡Muy buena solución! Tendré que echar un vistazo a cómo codificó exactamente las formas terominós (se ve muy compacto de esta manera).
ChristopheD
3
Realmente me encantaría ver una explicación ampliada de este código. Se ve tan enfermo ... no puedo entenderlo.
Nils Riedemann
1
@Nils Riedemann, escribo una explicación en este momento, pero estoy pensando en publicarla ahora, o después de que el ganador anuncie) De todos modos, una vez, publicaré y responderé todas las preguntas, porque es una wiki de la comunidad con la idea principal para ser útil)
Nakilon
En Ruby 1.9.2dev de Debian (2010-07-30) esto falla en su caso de prueba en paste.org.ru/?6ep1on . Además , ¿siempre extiende el campo de juego a diez líneas?
ninjalj
@ninjalj, ruby ​​1.9.2p0 (2010-08-18) [i386-mingw32] paste.org.ru/?1qnjhj Se ve bien. El ancho de 10 es un estándar de Tetris, supongo.
Nakilon
17

Script de shell bash ( 301 304 caracteres)


ACTUALIZACIÓN: se corrigió un error que involucraba piezas que se extendían hasta la fila superior. Además, la salida ahora se envía a la salida estándar y, como beneficio adicional, es posible ejecutar el script nuevamente para continuar jugando un juego (en cuyo caso debe sumar la puntuación total usted mismo).

Esto incluye caracteres no imprimibles, por lo que he proporcionado un volcado hexadecimal. Guárdelo como tetris.txt:

0000000: 7461 696c 202d 3120 245f 7c7a 6361 743e  tail -1 $_|zcat>
0000010: 753b 2e20 750a 1f8b 0800 35b0 b34c 0203  u;. u.....5..L..
0000020: 5590 516b 8330 10c7 dff3 296e 4c88 ae64  U.Qk.0....)nL..d
0000030: a863 0c4a f57d 63b0 07f7 b452 88d1 b4da  .c.J.}c....R....
0000040: 1a5d 5369 91a6 df7d 899a d05d 5e72 bfbb  .]Si...}...]^r..
0000050: fbff 2fe1 45d5 0196 7cff 6cce f272 7c10  ../.E...|.l..r|.
0000060: 387d 477c c4b1 e695 855f 77d0 b29f 99bd  8}G|....._w.....
0000070: 98c6 c8d2 ef99 8eaa b1a5 9f33 6d8c 40ec  ...........3m.@.
0000080: 6433 8bc7 eeca b57f a06d 27a1 4765 07e6  d3.......m'.Ge..
0000090: 3240 dd02 3df1 2344 f04a 0d1d c748 0bde  2@..=.#D.J...H..
00000a0: 75b8 ed0f 9eef 7bd7 7e19 dd16 5110 34aa  u.....{.~...Q.4.
00000b0: c87b 2060 48a8 993a d7c0 d210 ed24 ff85  .{ `H..:.....$..
00000c0: c405 8834 548a 499e 1fd0 1a68 2f81 1425  ...4T.I....h/..%
00000d0: e047 bc62 ea52 e884 42f2 0f0b 8b37 764c  .G.b.R..B....7vL
00000e0: 17f9 544a 5bbd 54cb 9171 6e53 3679 91b3  ..TJ[.T..qnS6y..
00000f0: 2eba c07a 0981 f4a6 d922 89c2 279f 1ab5  ...z....."..'...
0000100: 0656 c028 7177 4183 2040 033f 015e 838b  .V.(qwA. @.?.^..
0000110: 0d56 15cf 4b20 6ff3 d384 eaf3 bad1 b9b6  .V..K o.........
0000120: 72be 6cfa 4b2f fb03 45fc cd51 d601 0000  r.l.K/..E..Q....

Luego, en el símbolo del sistema de bash, preferiblemente con en elvislugar de viminstalado como vi:

$ xxd -r tetris.txt tetris.sh
$ chmod +x tetris.sh
$ cat << EOF > b
> [          ]
> [          ]
> [          ]
> [          ]
> [ #    #  #]
> [ ## ######]
> [==========]
> EOF
$ ./tetris.sh T2 Z6 I0 T7 2>/dev/null
-- removed stuff that is not in standard out --
[          ]
[          ]
[          ]
[#      ###]
[#     ### ]
[##### ####]
[==========]
10

Cómo funciona

El código se autoextrae de manera similar a como lo hacen los programas ejecutables comprimidos usando el gzexescript. Las piezas de Tetromino se representan como secuencias de comandos del editor vi. El recuento de caracteres se utiliza para detectar colisiones y el recuento de líneas se utiliza para calcular la puntuación.

El código descomprimido:

echo 'rej.j.j.:wq!m'>I
echo '2rejh.:wq!m'>O
echo '2rej.:wq!m'>Z
echo '3rejh1.:wq!m'>T
echo 'rej.j2.:wq!m'>L
echo 'l2rej2h.:wq!m'>S
echo 'lrej.jh2.:wq!m'>J
for t
do for y in `seq 1 5`
do echo -n ${y}jk$((${t:1}+1))l|cat - ${t:0:1}|vi b>0
grep ========== m>0||break
[ `tr -cd '#'<b|wc -c` = `tr -cd '#'<m|wc -c` ]||break
tr e '#'<m>n
done
cat n>b
grep -v '##########' b>m
$((S+=10*(`wc -l < b`-`wc -l < m`)))
yes '[          ]'|head -7|cat - m|tail -7>b
done
cat b
echo $S

El código original antes de jugar al golf:

#!/bin/bash

mkpieces() {
    pieces=('[email protected].' '2r@jh.' '2r@j.' '3r@jh1.' '[email protected].' 'l2r@j2h.' '[email protected].')
    letters=(I O Z T L S J)

    for j in `seq 0 9`; do
        for i in `seq 0 6`; do
            echo "jk$(($j+1))l${pieces[$i]}:wq! temp" > ${letters[$i]}$j
        done
    done
}

counthashes() {
    tr -cd '#' < $1 | wc -c
}

droppiece() {
    for y in `seq 1 5`; do
        echo -n $y | cat - $1 | vi board > /dev/null
        egrep '={10}' temp > /dev/null || break
        [ `counthashes board` -eq `counthashes temp` ] || break
        tr @ "#" < temp > newboard
    done
    cp newboard board
}

removelines() {
    egrep -v '#{10}' board > temp
    SCORE=$(($SCORE + 10 * (`wc -l < board` - `wc -l < temp`)))
    yes '[          ]' | head -7 | cat - temp | tail -7 > board
}

SCORE=0
mkpieces
for piece; do
    droppiece $piece
    removelines
done
cat board
echo $SCORE
favor
fuente
1
Un archivo bash, descomprimiendo y ejecutando vi .. no estoy seguro de la legalidad de tal abominación .. pero es más impresionante, +1. felicitaciones a usted señor.
Michael Anderson
Lleva un tiempo ridículamente largo para completarse y luego genera la salida incorrecta para el caso de prueba "T2 Z6 I0 T7 T2 Z6 T2 I5 I1 I0 T4 O8 T1 T6 T3 Z0 I9 I6 O7 T3 I2 O0 J8 L6 O7 O4 I3 J8 S6 O1 I0 O4 "(misma placa que la entrada de ejemplo). Además, miles de líneas de basura se apagarán cuando se canalicen, y el resultado de la placa probablemente debería ir allí.
Nabb
Sería mucho más rápido si se instalara Elvis en lugar de Vim como vi.
PleaseStand
2
@Nabb: Acabo de solucionar todos esos problemas a un costo de solo tres caracteres.
favor
Guau. Ese es un abuso de bash bastante impresionante.
P Daddy
13

Python: 504 519 caracteres

(Solución Python 3) Actualmente requiere establecer la entrada en el formato que se muestra en la parte superior (el código de entrada no se cuenta). Lo expandiré para leer desde el archivo o stdin más tarde. Ahora funciona con un mensaje, simplemente pegue la entrada (8 líneas en total).

R=range
f,p=[input()[1:11]for i in R(7)],p
for(a,b)in input().split():
 t=[' '*int(b)+r+' '*9for r in{'I':'#,#,#,#','O':'##,##','Z':'##, ##','T':'###, # ','L':'#,#,##','S':' ##,##','J':' #, #,##'}[a].split(',')]
 for r in R(6-len(t),0,-1):
  for i in R(len(t)):
   if any(a==b=='#'for(a,b)in zip(t[i],f[r+i])):break
  else:
   for i in R(0,len(t)):
    f[r+i]=''.join(a if b!='#'else b for(a,b)in zip(t[i],f[r+i]))
    if f[r+i]=='#'*10:del f[r+i];f[0:0]=[' '*10];p+=10
   break
print('\n'.join('['+r+']'for r in f[:7]),p,sep='\n')

No estoy seguro de poder ahorrar mucho más allí. Se pierden muchos caracteres de la transformación a campos de bits, pero eso ahorra muchos más caracteres que trabajar con las cadenas. Además, no estoy seguro de poder eliminar más espacios en blanco allí, pero lo intentaré más tarde.
No podrá reducirlo mucho más; después de tener la solución basada en bitfield, volví a las cadenas, ya que encontré una manera de comprimirlo más (¡guardé 8 caracteres en el bitfield!). Pero dado que olvidé incluir el Ly tuve un error con los puntos adentro, mi conteo de caracteres solo sube suspiro ... Quizás encuentre algo más tarde para comprimirlo un poco más, pero creo que estoy cerca del final. Para el código original y comentado, consulte a continuación:

Versión original:

field = [ input()[1:11] for i in range(7) ] + [ 0, input() ]
# harcoded tetrominoes
tetrominoes = {'I':('#','#','#','#'),'O':('##','##'),'Z':('##',' ##'),'T':('###',' # '),'L':('#','#','##'),'S':(' ##','##'),'J':(' #',' #','##')}
for ( f, c ) in field[8].split():
    # shift tetromino to the correct column
    tetromino = [ ' ' * int(c) + r + ' ' * 9 for r in tetrominoes[f] ]

    # find the correct row to insert
    for r in range( 6 - len( tetromino ), 0, -1 ):
        for i in range( len( tetromino ) ):
            if any( a == b == '#' for (a,b) in zip( tetromino[i], field[r+i] ) ):
                # skip the row if some pieces overlap
                break
        else:
            # didn't break, insert the tetromino
            for i in range( 0, len( tetromino ) ):
                # merge the tetromino with the field
                field[r+i] = ''.join( a if b != '#' else b for (a,b) in zip( tetromino[i], field[r+i] ) )

                # check for completely filled rows
                if field[r+i] == '#' * 10:
                    # remove current row
                    del field[r+i]
                    # add new row
                    field[0:0] = [' '*10]
                    field[7] += 10
            # we found the row, so abort here
            break
# print it in the requested format
print( '\n'.join( '[' + r + ']' for r in field[:7] ) )
# and add the points = 10 * the number of redundant lines at the end
print( str( field[7] ) )
golpe
fuente
No creo que esto sea correcto. No existe una regla que diga que solo la línea de fondo puede desaparecer, pero a juzgar por sus comentarios, solo revisa esa línea.
Michael Madsen
Por favor, haga su entrada como en una tarea. Quiero decir, entrada desde archivo o STDIN.
Nakilon
6
¿No te encanta cómo incluso el código Python minimizado sigue siendo bastante legible?
EMP
@Evgeny, solo si se compara con Perl o Malbolge)
Nakilon
Bueno, me refiero a "legible" en relación con otras respuestas de golf de código.
EMP
13

Rubí 1.9, 357 355 353 339 330 310 309 caracteres

d=0
e=[*$<]
e.pop.split.map{|f|f="L\003\003\007J\005\005\007O\007\007Z\007\013S\013\007I\003\003\003\003T\017\005"[/#{f[j=0]}(\W*)/,1].bytes.map{|z|?\0+?\0*f[1].hex+z.to_s(2).tr("01"," #")[1,9]}
k,f,i=i,[p]+f,e.zip(f).map{|l,m|l.bytes.zip(m.to_s.bytes).map{|n,o|j|=n&3&q=o||0;(n|q).chr}*""}until j>0
e=[]
e+=k.reject{|r|r.sum==544&&e<<r.tr(?#,?\s)&&d+=10}}
puts e,d

Tenga en cuenta que los \000escapes (incluidos los bytes nulos en la tercera línea) deben reemplazarse con su equivalente no imprimible real.

Entrada de muestra:

[          ]
[          ]
[          ]
[          ]
[ #    #  #]
[ ## ######]
[==========]
T2 Z6 I0 T7

Uso:

ruby1.9 tetris.rb < input

o

ruby1.9 tetris.rb input
14 revoluciones
fuente
Otra forma de dejar caer tetrominos y mantener todo el vidrio ordenado, incluso con bordes ... agradable. Ahora serás el líder de Ruby / Perl. PD: No sabía nada ?\s.
Nakilon
12

C, 727 [...] 596 581 556 517 496 471 461 457 caracteres

Este es mi primer código de golf, creo que el recuento de caracteres puede ser mucho menor, sería bueno si los golfistas experimentados me pudieran dar algunas pistas.

La versión actual también puede manejar campos de juego con diferentes dimensiones. La entrada puede tener saltos de línea en formato DOS / Windows y Unix.

El código era bastante sencillo antes de la optimización, los tetrominós se almacenan en 4 enteros que se interpretan como una matriz de (7 * 3) x4 bits, el campo de juego se almacena como está, los mosaicos se eliminan y las líneas completas se eliminan al inicio y después de cada caída de baldosas.

No estaba seguro de cómo contar los caracteres, así que utilicé el código con todos los saltos de línea innecesarios eliminados.

EDITAR 596 => 581: Gracias a KitsuneYMG, todo excepto la %lssugerencia funcionó perfectamente, además, noté que en putchlugar de putcharse puede usar (de getchalguna manera no funciona) y eliminé todos los paréntesis en #define G.

EDITAR 581 => 556: No estaba satisfecho con los bucles restantes fory anidados F, por lo que hubo algunas combinaciones, cambios y eliminación de bucles, bastante confuso pero definitivamente valió la pena.

EDITAR 556 => 517: Finalmente encontré una manera de hacer auna matriz int. Algunos se N;fusionaron con c, breakya no .

EDITAR 496 => 471: Ancho y alto del campo de juego arreglados ahora.

EDITAR 471 => 461: Modificaciones menores, putcharusado de nuevo como putchno es una función estándar.

EDITAR: Corrección de errores, las líneas completas se eliminaron antes de la caída del mosaico en lugar de después , por lo que las líneas completas podrían dejarse al final. Fix no cambia el recuento de caracteres.

#define N (c=getchar())
#define G T[j%4]&1<<t*3+j/4
#define X j%4*w+x+j/4
#define F(x,m) for(x=0;x<m;x++)
#define W while
T[]={916561,992849,217,1},C[99],c,i,j,s,t,x,A,a[99],w=13;
main(){F(j,7)C["IJLSTZO"[j]]=j;
F(j,91)a[j]=N;
W(N>w){t=C[c];x=N-86;
W(c){F(j,12)if(G&&X>1?a[X]-32:0)c=0;
F(j,12)if(G&&X>w&&!c)a[X-w]=35;x+=w;}N;
F(i,6){A=0;t=i*w;F(x,w)A|=(a[t+x]==32);
if(!A){s++;F(j,t)a[t+w-j]=a[t-j];
x=1;W(a[x]-93)a[x++]=32;}}}
F(i,91)putchar(a[i]);printf("%i0",s);}
18 revoluciones
fuente
1
¿No puedes definir forcomo #define F(x,m) for(x=0;x++<m;)? Funciona en C # ...: P
BrunoLM
@BrunoLM: Gracias, pero esto no funcionará, fe F(x,3){printf("%i",x}imprime en 12lugar de 012con este cambio. Podría cambiar a for(x=-1;x++<m;), pero esto no guarda nada :)
schnaader
1
Si ha escrito el código correctamente, si compila como C, no es necesario que incluya stdio.h (a menos que me haya perdido algo). Guarde algunos caracteres :)
1
Puede reemplazar su definición de N con (c=getchar())y eliminar todas las líneas c = N ahorrando 6 caracteres. A menos que me equivoque sobre esto, debería bajar a 585
KitsuneYMG
1
type por defecto es int también para variables, al menos para C89.
ninjalj
8

Python 2.6+ - 334 322 316 caracteres

397 368 366 caracteres sin comprimir

#coding:l1
exec'xÚEPMO!½ï¯ i,P*Ýlš%ì­‰=‰Ö–*†­þz©‰:‡—Lò¾fÜ”bžAù,MVi™.ÐlǃwÁ„eQL&•uÏÔ‹¿1O6ǘ.€LSLÓ’¼›î”3òšL¸tŠv[ѵl»h;ÁºŽñÝ0Àë»Ç‡ÛûH.ª€¼âBNjr}¹„V5¾3Dë@¼¡•gO. ¾ô6 çÊsÃЮürÃ1&›ßVˆ­ùZ`Ü€ÿžcx±ˆ‹sCàŽ êüRô{U¯ZÕDüE+³ŽFA÷{CjùYö„÷¦¯Î[0þøõ…(Îd®_›â»E#–Y%’›”ëýÒ·X‹d¼.ß9‡kD'.decode('zip')

Se requiere la nueva línea única y la he contado como un carácter.

La palabrería de la página de códigos del navegador puede evitar que se pueda copiar y pegar correctamente este código, por lo que puede generar el archivo a partir de este código:

s = """
23 63 6F 64 69 6E 67 3A 6C 31 0A 65 78 65 63 27 78 DA 45 50 4D 4F 03 21
10 BD EF AF 20 69 2C 50 2A 02 DD 6C 9A 25 EC AD 07 8D 89 07 3D 89 1C D6
96 2A 86 05 02 1B AD FE 7A A9 89 3A 87 97 4C F2 BE 66 DC 94 62 9E 41 F9
2C 4D 56 15 69 99 0F 2E D0 6C C7 83 77 C1 16 84 65 51 4C 26 95 75 CF 8D
1C 15 D4 8B BF 31 4F 01 36 C7 98 81 07 2E 80 4C 53 4C 08 D3 92 BC 9B 11
EE 1B 10 94 0B 33 F2 9A 1B 4C B8 74 8A 9D 76 5B D1 B5 6C BB 13 9D 68 3B
C1 BA 8E F1 DD 30 C0 EB BB C7 87 DB FB 1B 48 8F 2E 1C AA 80 19 BC E2 42
4E 6A 72 01 7D B9 84 56 35 BE 33 44 8F 06 EB 40 BC A1 95 67 4F 08 2E 20
BE F4 36 A0 E7 CA 73 C3 D0 AE FC 72 C3 31 26 9B DF 56 88 AD F9 5A 60 DC
80 FF 9E 63 78 B1 88 8B 73 43 E0 8E A0 EA FC 52 F4 7B 55 8D AF 5A 19 D5
44 FC 45 2B B3 8E 46 9D 41 F7 7B 43 6A 12 F9 59 F6 84 F7 A6 01 1F AF CE
5B 30 FE F8 F5 85 28 CE 64 AE 5F 9B E2 BB 45 23 96 59 25 92 9B 94 EB FD
10 D2 B7 58 8B 64 BC 2E DF 39 87 6B 44 27 2E 64 65 63 6F 64 65 28 27 7A
69 70 27 29
"""

with open('golftris.py', 'wb') as f:
    f.write(''.join(chr(int(i, 16)) for i in s.split()))

Pruebas

intetris

[]
[]
[]
[]
[# # #]
[## ######]
[==========]
T2 Z6 I0 T7

Las líneas nuevas deben ser de estilo Unix (solo salto de línea). Un salto de línea final en la última línea es opcional.

Probar:

> python golftris.py <intetris
[]
[]
[]
[# ###]
[# ###]
[##### ####]
[==========]
10

Este código descomprime el código original y lo ejecuta con exec. Este código descomprimido pesa 366 caracteres y se ve así:

import sys
r=sys.stdin.readlines();s=0;p=r[:1];a='[##########]\n'
for l in r.pop().split():
 n=int(l[1])+1;i=0xE826408E26246206601E>>'IOZTLSJ'.find(l[0])*12;m=min(zip(*r[:6]+[a])[n+l].index('#')-len(bin(i>>4*l&31))+3for l in(0,1,2))
 for l in range(12):
  if i>>l&2:c=n+l/4;o=m+l%4;r[o]=r[o][:c]+'#'+r[o][c+1:]
 while a in r:s+=10;r.remove(a);r=p+r
print''.join(r),s

Se requieren nuevas líneas y son de un carácter cada una.

No intente leer este código. Los nombres de las variables se eligen literalmente al azar en busca de la compresión más alta (con diferentes nombres de variables, vi hasta 342 caracteres después de la compresión). A continuación se muestra una versión más comprensible:

import sys

board = sys.stdin.readlines()
score = 0
blank = board[:1] # notice that I rely on the first line being blank
full  = '[##########]\n'

for piece in board.pop().split():
    column = int(piece[1]) + 1 # "+ 1" to skip the '[' at the start of the line

    # explanation of these three lines after the code
    bits = 0xE826408E26246206601E >> 'IOZTLSJ'.find(piece[0]) * 12
    drop = min(zip(*board[:6]+[full])[column + x].index('#') -
               len(bin(bits >> 4 * x & 31)) + 3 for x in (0, 1, 2))

    for i in range(12):
        if bits >> i & 2: # if the current cell should be a '#'
            x = column + i / 4
            y = drop + i % 4
            board[y] = board[y][:x] + '#' + board[y][x + 1:]

    while full in board:      # if there is a full line,
        score += 10           # score it,
        board.remove(full)    # remove it,
        board = blank + board # and replace it with a blank line at top
        
print ''.join(board), score

El quid está en las tres líneas crípticas que dije que explicaría.

La forma de los tetrominós está codificada en el número hexadecimal allí. Se considera que cada tetrónimo ocupa una cuadrícula de celdas de 3x4, donde cada celda está en blanco (un espacio) o completa (un signo de número). Luego, cada pieza se codifica con 3 dígitos hexadecimales, cada uno de los cuales describe una columna de 4 celdas. Los dígitos menos significativos describen las columnas más a la izquierda y el bit menos significativo de cada dígito describe la celda más alta de cada columna. Si un bit es 0, entonces esa celda está en blanco, de lo contrario es un '#'. Por ejemplo, el I tetronimo se codifica como 00F, con los cuatro bits del dígito menos significativo activados para codificar los cuatro signos numéricos en la columna más a la izquierda, y la T es131, con el bit superior establecido a la izquierda y a la derecha, y los dos bits superiores colocados en el medio.

El número hexadecimal completo se desplaza un bit hacia la izquierda (multiplicado por dos). Esto nos permitirá ignorar el bit más bajo. Explicaré por qué en un minuto.

Entonces, dada la pieza actual de la entrada, encontramos el índice en este número hexadecimal donde comienzan los 12 bits que describen su forma, luego lo desplazamos hacia abajo para que los bits 1–12 (saltando el bit 0) de la bitsvariable describan la pieza actual.

La asignación dropdetermina cuántas filas desde la parte superior de la cuadrícula caerá la pieza antes de aterrizar en otros fragmentos de pieza. La primera línea encuentra cuántas celdas vacías hay en la parte superior de cada columna del campo de juego, mientras que la segunda encuentra la celda ocupada más baja en cada columna de la pieza. La zipfunción devuelve una lista de tuplas, donde cada tupla consiste en el n ° de células de cada elemento en la lista de entrada. Entonces, usando la placa de entrada de muestra, zip(board[:6] + [full])devolverá:

[
 ('[', '[', '[', '[', '[', '[', '['),
 (' ', ' ', ' ', ' ', ' ', ' ', '#'),
 (' ', ' ', ' ', ' ', '#', '#', '#'),
 (' ', ' ', ' ', ' ', ' ', '#', '#'),
 (' ', ' ', ' ', ' ', ' ', ' ', '#'),
 (' ', ' ', ' ', ' ', ' ', '#', '#'),
 (' ', ' ', ' ', ' ', ' ', '#', '#'),
 (' ', ' ', ' ', ' ', '#', '#', '#'),
 (' ', ' ', ' ', ' ', ' ', '#', '#'),
 (' ', ' ', ' ', ' ', ' ', '#', '#'),
 (' ', ' ', ' ', ' ', '#', '#', '#'),
 (']', ']', ']', ']', ']', ']', ']')
]

Seleccionamos la tupla de esta lista correspondiente a la columna apropiada, y buscamos el índice de la primera '#'en la columna. Es por eso que agregamos una fila "completa" antes de llamar zip, para que indextenga un retorno sensato (en lugar de lanzar una excepción) cuando la columna está en blanco.

Luego, para encontrar el más bajo '#'en cada columna de la pieza, cambiamos y enmascaramos los cuatro bits que describen esa columna, luego usamos la binfunción para convertir eso en una cadena de unos y ceros. La binfunción solo devuelve bits significativos, por lo que solo necesitamos calcular la longitud de esta cadena para encontrar la celda ocupada más baja (bit de conjunto más significativo). La binfunción también antepone '0b', así que tenemos que restar eso. También ignoramos el bit menos significativo. Es por eso que el número hexadecimal se desplaza un bit hacia la izquierda. Esto es para tener en cuenta las columnas vacías, cuyas representaciones de cadena tendrían la misma longitud que una columna con solo la celda superior llena (como la pieza T ).

Por ejemplo, las columnas de la I tetromino, como se mencionó anteriormente, son F, 0, y 0. bin(0xF)es '0b1111'. Después de ignorar '0b', tenemos una longitud de 4, que es correcta. Pero bin(0x0)es 0b0. Después de ignorar '0b', todavía tenemos una longitud de '1, que es incorrecta. Para dar cuenta de esto, hemos agregado un bit adicional al final, para que podamos ignorar este bit insignificante. Por lo tanto, +3en el código está ahí para tener en cuenta la longitud extra que ocupa al '0b'principio y el bit insignificante al final.

Todo esto ocurre dentro de una expresión generadora para tres columnas ( (0,1,2)), y tomamos el minresultado para encontrar el número máximo de filas que la pieza puede caer antes de tocar en cualquiera de las tres columnas.

El resto debería ser bastante fácil de entender leyendo el código, pero el forciclo que sigue a estas asignaciones agrega la pieza al tablero. Después de esto, el whileciclo elimina filas completas, reemplazándolas con filas en blanco en la parte superior y suma la puntuación. Al final, la pizarra y la puntuación se imprimen en la salida.

P Papi
fuente
6

Python, 298 caracteres

Supera todas las soluciones de lenguaje no esotérico hasta ahora (Perl, Ruby, C, bash ...)


... y ni siquiera usa artimañas para comprimir códigos.

import os
r=os.read
b='[%11c\n'%']'*99+r(0,91)
for k,v in r(0,99).split():
    t=map(ord,' -:G!.:; -:; !-.!"-. !". !./')['IJLOSTZ'.find(k)*4:][:4];v=int(v)-31
    while'!'>max(b[v+j+13]for j in t):v+=13
    for j in t:b=b[:v+j]+'#'+b[v+j+1:]
    b=b.replace('[##########]\n','')
print b[-91:],1060-10*len(b)/13

En el ejemplo de prueba

[          ]
[          ]
[          ]
[          ]
[ #    #  #]
[ ## ######]
[==========]
T2 Z6 I0 T7

sale

[          ]
[          ]
[          ]
[#      ###]
[#     ### ]
[##### ####]
[==========]
10

PD. Se corrigió un error señalado por Nakilon a un costo de +5

Nas Banov
fuente
Ese es un código bastante impresionante. Se necesitarían 14 caracteres más para arreglarlo ( ideone.com/zeuYB ), a menos que haya una mejor manera, pero aún así, supera a todo menos a GolfScript y Bash. Sin duda, es una solución inteligente.
P Daddy
¡Sí, absolutamente una excelente solución!
ChristopheD
@Nakilon: gracias, obviamente me perdí ese. fijo @ costo 293-> 298
Nas Banov
@P Daddy, gracias. Encontré una manera de solucionar el problema en bash & toolchain para mantenerme honesto al decir "todo no esotérico" :)
Nas Banov
@Nabb: para mantener el código más corto, fue escrito con algunas limitaciones en mente. algo así como 33 tetrominos max y 99 line drop max. Se puede ampliar fácilmente por el precio de +3. O por un precio bajo :), las limitaciones se pueden eliminar por completo. Por cierto, este es un gran ejemplo de cómo tener un conjunto de prueba habría aclarado la especificación (lo que estaba molestando a ChristopherD en los comentarios)
Nas Banov
5

Golfscript 260 caracteres

Estoy seguro de que esto podría mejorarse, soy un poco nuevo en Golfscript.

[39 26.2/0:$14{.(}:?~1?15?1?14 2??27?13.!14?2?27?14 1]4/:t;n/)\n*:|;' '/-1%.,:c;~{)18+:&;'XIOZTLSJX'\%~;,1-t\={{.&+.90>{;.}*|\=32=!{&13-:&;}*}%}6*{&+}/|{\.@<'#'+\)|>+}4*{'['\10*']'++}:
;n/0\~n+:|;0\{.'#'
={;)}{n+|+:|;}if\.}do;' '
n+\.@*|+\$+:$;.,1-<:|;}c*|n?$*

Los finales de las líneas son relevantes (no debería haber uno al final). De todos modos, estos son algunos de los casos de prueba que utilicé:

> cat init.txt 
[]
[]
[]
[]
[# # #]
[## ######]
[==========]
T2 Z6 I0 T7> cat init.txt | ruby golfscript.rb tetris.gsc
[]
[]
[]
[# ###]
[# ###]
[##### ####]
[==========]
10

> cat init.txt
[]
[]
[]
[]
[# # #]
[## #####]
[==========]
I0 O7 Z1 S4> cat init.txt | ruby golfscript.rb tetris.gsc
[]
[]
[]
[#]
[### ####]
[### #####]
[==========]
10

> cat init.txt
[]
[]
[]
[## ###]
[# #]
[## ######]
[==========]
T7 I0 I3> cat init.txt | ruby golfscript.rb tetris.gsc
[]
[]
[]
[]
[# #]
[## # # #]
[==========]
20

Tenga en cuenta que no hay final de línea en el archivo de entrada, un final de línea rompería el script tal como está.

coderaj
fuente
2
/ me considera que GolfScript no es un verdadero lenguaje de concurso ... Es solo una biblioteca, diseñada directamente para tareas de golf ... El tamaño de esta biblioteca puede agregarse al tamaño del código golfscript ...
Nakilon
4
@Nakilon: ¿No puedes decir algo similar sobre algo que no esté escrito en lenguaje de máquina sin procesar? :) El intérprete de Python es solo una biblioteca, agregue su tamaño a su entrada. </sarcasmo>
bta
2
@Nakilon: ese es solo el intérprete. Podría estar escrito en cualquier otro idioma; ¿Seguiría diciendo que Golfscript no es un idioma real?
Michael Foukarakis
1
@Nabb: Gracias, pensé que había algunos trucos que me perdí ... No te sientas mal, tampoco me molesté en entender mi código :).
coderaj
1
@Michael Foukarakis, puedo en 1 minuto escribir mi propio intérprete para resolver esta tarea en un solo carácter, ¿y qué?
Nakilon
4

O'Caml 809 782 Caracteres

open String let w=length let c s=let x=ref 0in iter(fun k->if k='#'then incr x)s;!x open List let(@),g,s,p,q=nth,ref[],ref 0,(0,1),(0,2)let l=length let u=Printf.printf let rec o x i j=let a=map(fun s->copy s)!g in if snd(fold_left(fun(r,k)(p,l)->let z=c(a@r)in blit(make l '#')0(a@r)(i+p)l;if c(a@r)=z+l then r+1,k else r,false)(j-l x+1,true)x)then g:=a else o x i(j-1)and f x=let s=read_line()in if s.[1]='='then g:=rev x else f(sub s 1 10::x)let z=f [];read_line();;for i=0to w z/3 do o(assoc z.[i*3]['I',[p;p;p;p];'O',[q;q];'Z',[q;1,2];'T',[0,3;1,1];'L',[p;p;q];'S',[1,2;q];'J',[1,1;1,1;q]])(Char.code z.[i*3+1]-48)(l!g-1);let h=l!g in g:=filter(fun s->c s<>w s)!g;for i=1to h-(l!g)do incr s;g:=make 10' '::!g done;done;iter(fun r->u"[%s]\n"r)!g;u"[==========]\n";u"%d\n"(!s*10)
jessicah
fuente
4

Common Lisp 667657 645 caracteres

Mi primer intento en el golf de código, por lo que probablemente hay muchos trucos que aún no conozco. Dejé algunas líneas nuevas allí para mantener algo de "legibilidad" residual (conté las líneas nuevas como 2 bytes, por lo que al eliminar 6 líneas nuevas innecesarias se obtienen 12 caracteres más).

En la entrada, primero ponga las formas y luego el campo.

(let(b(s 0)m(e'(0 1 2 3 4 5 6 7 8 9)))
(labels((o(p i)(mapcar(lambda(j)(+ i j))p))(w(p r)(o p(* 13 r)))(f(i)(find i b))
(a(&aux(i(position(read-char)"IOZTLSJ")))(when i(push(o(nth i'((0 13 26 39)(0 1 13 14)(0 1 14 15)(0 1 2 14)(0 13 26 27)(1 2 13 14)(1 14 26 27)))(read))m)(a))))
(a)(dotimes(i 90)(if(find(read-char)"#=")(push i b)))(dolist(p(reverse m))
(setf b`(,@b,@(w p(1-(position-if(lambda(i)(some #'f(w p i)))e)))))
(dotimes(i 6)(when(every #'f(w e i))(setf s(1+ s)b(mapcar(lambda(k)(+(if(>(* 13 i)k)13(if(<=(* 13(1+ i))k)0 78))k))b)))))
(dotimes(i 6)(format t"[~{~:[ ~;#~]~}]
"(mapcar #'f(w e i))))(format t"[==========]
~a0"s)))

Pruebas

T2 Z6 I0 T7
[          ]
[          ]
[          ]
[          ]
[ #    #  #]
[ ## ######]
[==========]
[          ]
[          ]
[          ]
[#      ###]
[#     ### ]
[##### ####]
[==========]
10
NIL
lpetru
fuente
¡No demasiado corto, pero +1 por fealdad! Me imagino que así se vería la sopa de letras si tuviera paréntesis.
P Daddy
@P Daddy: Gracias. Sí, probablemente así se vería :).
lpetru
2

Rubí 505 479 474 442 439 426 caracteres

Un primer intento. Lo he hecho con IronRuby. Estoy seguro de que se puede mejorar, ¡pero realmente debería trabajar un poco hoy!

p,q,r,s=(0..9),(0..2),(0..6),0
t=[*$<]
f=p.map{|a|g=0;r.map{|b|g+=2**b if t[6-b][a+1]==?#};g}
t.pop.split.map{|x|w,y=[15,51,306,562,23,561,113]["IOZTLSJ"=~/#{x[0]}/],x[1].to_i
l=q.map{|d|r.inject{|b,c|f[d+y]&(w>>(d*4)&15-c+1)>0?c:b}}.max
q.map{|b|f[b+y]|=w>>(b*4)&15-l}
r.map{i=f.inject{|a,b|a&b};f.map!{|a|b=i^(i-1);a=((a&~b)>>1)+(a&(b>>1))};s+=i>0?10:0}}
p.map{|a|r.map{|b|t[6-b][a+1]=f[a]&2**b>0??#:' '}}
puts t,s

Pruebas

cat test.txt | ruby tetris.rb
[          ]
[          ]
[          ]
[          ]
[#      ###]
[#     ### ]
[##### ####]
[==========]
10

Editar ahora usando rubí normal. Tengo la salida de las paredes ...

Mongus Pong
fuente
¡Un Rubist más, bonito! Pero haz un vaso alrededor de los ladrillos.
Nakilon
1

Otro en Ruby, 573546 caracteres

: **

Z={I:?#*4,J:'#,###',L:'###,#',O:'##,##',S:'#,##, #',Z:' #,##,#',T:' #,##, #'}
t=[*$<]
R=->s{s.reverse}
T=->m{m.transpose}
a = T[R[t].join.scan /.#{'(\D)'*10}.$/]
t.pop.split.each{|z|
t,o=Z[z[0].to_sym].split(',').map{|x|x.split //},z[1].to_i
r=0..t.size-1
y=r.map{|u|1+a[o+u].rindex(?#).to_i-t[u].count(' ')}.max
(0..3).each{|i|r.each{|j|t[j][i]==?#&&a[o+j][y+i]=t[j][i]}}}
s=0
a.each{|x|s=a.max_by(&:size).size;x[s-=1]||=' 'while s>0}
a=R[T[a].reject{|x|x*''=~/[#]{10}/&&s+=10}.map{|x|?[+x*''+?]}[0..6]]
puts (0..8-a.size).map{?[+' '*10+?]},a,s

Pruebas:

cat test.txt | ruby 3858384_tetris.rb
[          ]
[          ]
[          ]
[          ]
[#      ###]
[#     ### ]
[##### ####]
[==========]
10
glebm
fuente
Corregido cona.each{|x|s=a.max_by(&:size).size;x[s-=1]||=' 'while s>0}
glebm