Code Golf: Jogando Tetris

83

O básico:

Considere os seguintes tetrominós e o campo de jogo vazio:

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

As dimensões do campo de jogo são fixas. Os números no topo estão aqui apenas para indicar o número da coluna (veja também a entrada).

Entrada:

1 . Você recebe um campo de jogo específico (com base no acima) que já pode ser preenchido parcialmente com tetrominós (isso pode estar em um arquivo separado ou fornecido via stdin).

Entrada de amostra:

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

2 . Você recebe uma string que descreve (separada por espaços) qual tetrominó inserir (e drop down) em qual coluna. Os tetrominós não precisam ser girados. A entrada pode ser lida em stdin.

Entrada de amostra:

T2 Z6 I0 T7

Você pode presumir que a entrada é 'bem formada' (ou produzir um comportamento indefinido quando não está).

Resultado

Renderize o campo resultante (as linhas 'completas' devem desaparecer) e imprima a contagem de pontuação (cada linha descartada representa 10 pontos).

Saída de amostra com base na entrada de amostra acima:

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

Vencedora:

Solução mais curta (por contagem de caracteres de código). Os exemplos de uso são bons. Divirta-se jogando golfe!

Edit : adicionou uma recompensa de +500reputação para chamar um pouco mais a atenção para os bons esforços que os respondentes já fizeram (e possivelmente algumas novas soluções para esta questão) ...

ChristopheD
fonte
5
@omouse: verifique meta.stackoverflow.com - código de golfe geralmente é permitido (na forma de wiki da comunidade)
ChristopheD,
18
@omouse: É para isso que serve a votação para fechar. Arrastar moderadores aqui para sinalizar a questão provavelmente não o tornará tão popular, visto que repetidas vezes a comunidade (relutantemente) permitiu a existência de code golf (veja a tag code-golf e meta discussões; não é novidade) .
Mark Peters,
8
@omouse: Off-topic! = spam. Mesmo que você não possa votar para fechar, esse sinalizador de spam não foi solicitado.
BoltClock
3
Estou esperando um atleta APL! Aposto que ele pode fazer isso em 3,5 símbolos
n8wrl
3
As dimensões devem ser fixas, mas a entrada de amostra e o campo em branco têm alturas diferentes. Qual deve ser a altura?
Nabb

Respostas:

27

GolfScript - 181 caracteres

Novas linhas não são necessárias. A saída está na saída padrão, embora alguns erros estejam presentes em stderr.
\10deve ser substituído pelo caractere ASCII correspondente para que o programa tenha 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*

Amostra de E / S:

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

Compressão Tetromino: As
peças são armazenadas como três dígitos de base 8. Esta é uma representação binária simples, por exemplo T=[7,2,0], S=[6,3,0], J=[2,2,3]. [1]é usado para a Iparte em compressão, mas é explicitamente definido para [1,1,1,1]mais tarde (ou seja, 4*no código). Todas essas matrizes são concatenadas em uma única matriz, que é convertida em um inteiro e, em seguida, em uma string (base 126 para minimizar caracteres não imprimíveis, comprimento e não encontrar utf8). Essa seqüência é muito curto: "R@1(XBc_".

A descompressão é então direta. Primeiro fazemos uma conversão de base 126 seguida por uma conversão de base 8 ( "~\10"{base}/ou seja, iteramos "~\10"e fazemos uma conversão de base para cada elemento). O array resultante é dividido em grupos de 3, o array para Ié fixo ( 3/~4*). Em seguida, convertemos cada elemento para a base 2 e (após remover zeros) substituímos cada dígito binário pelo caractere daquele índice na string " #"( 2base{" #"=}%...-1%- observe que precisamos inverter o array, caso contrário, 2se tornaria em "# "vez de " #").

Formato do tabuleiro / peça, soltando peças
O tabuleiro é simplesmente um conjunto de cordas, uma para cada linha. Nenhum trabalho é feito inicialmente nisso, então podemos gerá-lo com n/(a entrada. As peças também são arranjos de cordas, preenchidas com espaços à esquerda para sua posição X, mas sem espaços à direita. As peças são descartadas ao se anexar à matriz e testar continuamente se há uma colisão.

O teste de colisão é feito iterando-se todos os personagens da peça e comparando-os com o personagem da mesma posição no tabuleiro. Queremos considerar #+ =e #+ #como colisões, então testamos se ((piecechar & 3) & boardchar) é diferente de zero. Ao fazer essa iteração, também atualizamos (uma cópia de) o tabuleiro com ((piechar & 3) | boardchar), que define corretamente o valor dos pares #+ , + #, + [. Usamos este tabuleiro atualizado se houver uma colisão após mover a peça para outra linha.

Remover linhas preenchidas é bastante simples. Removemos todas as linhas para as quais "= "&retornam falso. A linha cheia terá nenhum =ou , de modo que o conjunto será uma string em branco, o que equivale a falsa. Em seguida, contamos o número de linhas que foram removidas, adicionamos a contagem à pontuação e acrescentamos essa quantidade de "[ ... ]"s. Nós geramos isso compactamente pegando a primeira linha da grade e substituindo #por .

Bônus
Como calculamos a aparência do tabuleiro em cada posição da peça conforme ela cai, podemos mantê-los na pilha em vez de excluí-los! Para um total de mais três caracteres, podemos imprimir todas essas posições (ou dois caracteres se tivermos os estados do tabuleiro com espaçamento simples).

{):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
fonte
Algum código extremo de golfe acontecendo bem aqui (não achei que isso pudesse ser feito com menos de 200 caracteres). Bom trabalho!
ChristopheD,
8
Surpreendente. Eu gostaria de ter entendido GolfScript. Espere ... não, eu não.
P Daddy
26

Perl, 586 523 483 472 427 407 404 386 387 356 353 caracteres

(Necessita do Perl 5.10 para o //operador definido ou ).

Recebe todas as entradas de stdin. Ainda precisa de um golfe sério.
Observe que ^ Q representa ASCII 17 (DC1 / XON), ^ C representa ASCII 3 e ^ @ 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,$/

Versão 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,$/

Editar 1: golfe sério, correção de bug de saída.
Edição 2: alguns inlining, mesclaram dois loops em um para uma economia líquida de (rufar de tambores ...) 3 caracteres, golfe diverso.
Editar 3: alguma eliminação de subexpressão comum, uma pequena fusão constante e um regex ajustado.
Edição 4: mudou a representação de tetrominós em um vetor de bits compactado, golfe diverso.
Edit 5: tradução mais direta da letra do tetromino para o índice do array, use caracteres não imprimíveis, golfe diverso.
Edição 6: linha superior de limpeza de bug corrigida, introduzida em r3 (edição 2), localizada por Nakilon. Use mais caracteres não imprimíveis.
Editar 7: use vecpara obter dados de tetromino. Aproveite o fato de que o campo de jogo tem dimensões fixas. ifdeclaração =>ifmodificador, a fusão de loops da edição 2 começa a dar frutos. Use //para o caso de pontuação 0.
Edit 8: corrigido outro bug, introduzido no r6 (edição 5), detectado por Nakilon.
Edição 9: não crie novas referências ao limpar linhas, apenas mova as referências por meio do corte de matriz. Mesclar dois mapem um. Regex mais inteligente. "Mais inteligente" for. Golfe diverso.
Edit 10: array tetromino embutido, versão comentada adicionada.

ninjalj
fonte
Funcionando muito bem (e já com uma boa contagem de caracteres para este problema não trivial). Uma pequena peculiaridade é que meu perl (perl, v5.10.0 construído para darwin-thread-multi-2level) parece imprimir o resultado duas vezes (entrada canalizada).
ChristopheD,
@ChristopheD: consertou a saída duplicada, estava imprimindo dentro do meu loop principal, mas apenas para linhas sem playfield. Você provavelmente teve uma nova linha demais :)
ninjalj
Mais 4 caracteres para vencer o python !!
Vivin Paliath,
1
Eu ainda não desisti perl! xD (Embora eu também gostaria de ver algumas outras soluções até agora ..)
toque em
@Nakilon: Boa pegada! Você tem um bom caso de teste aí.
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
fonte
Solução muito boa! Terei que dar uma olhada em como exatamente você codificou as formas de terominós (parece muito compacto desta forma).
ChristopheD de
3
Eu realmente adoraria ver uma explicação expandida deste código. Parece tão doente ... não consigo entender.
Nils Riedemann,
1
@Nils Riedemann, escrevo uma explicação agora, mas estou pensando em postá-la agora, ou após o anúncio do vencedor) De qualquer forma, uma vez postarei e responderei todas as perguntas, porque é uma wiki da comunidade com a ideia principal para ser útil)
Nakilon
No ruby ​​1.9.2dev do Debian (30/07/2010), isso falha em seu caso de teste em paste.org.ru/?6ep1on . Além disso, sempre estende o campo de jogo para dez linhas?
ninjalj,
@ninjalj, ruby ​​1.9.2p0 (2010-08-18) [i386-mingw32] paste.org.ru/?1qnjhj Parece bom. Largura de 10 é um padrão do Tetris, eu suponho.
Nakilon,
17

Script shell Bash ( 301 304 caracteres)


ATUALIZAÇÃO: Corrigido um bug envolvendo peças que se estendem até a linha superior. Além disso, a saída agora é enviada para a saída padrão e, como bônus, é possível executar o script novamente para continuar jogando (nesse caso, você deve somar a pontuação total).

Isso inclui caracteres não imprimíveis, portanto, forneci um dump hexadecimal. Salve-o 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....

Em seguida, no prompt de comando bash, de preferência com em elvisvez 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

Como funciona

O código se autoextrai de maneira semelhante à maneira como os programas executáveis ​​compactados usando o gzexescript fazem. As peças do Tetromino são representadas como sequências de comandos do editor vi. A contagem de caracteres é usada para detectar colisões e a contagem de linhas é usada para calcular a pontuação.

O código descompactado:

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

O código original antes de jogar golfe:

#!/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
PleaseStand
fonte
1
Um arquivo bash, descompactando e executando o vi .. não tenho certeza sobre a legalidade de tal abominação .. mas é mais impressionante, +1. parabéns para você, senhor.
Michael Anderson,
Leva um tempo ridiculamente longo para ser concluído e, em seguida, gera a saída errada para o caso de teste "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 "(mesma placa como exemplo de entrada). Além disso, milhares de linhas de lixo irão para o stdout quando canalizadas, e o resultado da placa provavelmente deveria estar indo para lá.
Nabb
Seria muito mais rápido se Elvis fosse instalado em vez do Vim como vi.
favor, fique em
2
@Nabb: Acabei de consertar todos esses problemas a um custo de apenas três caracteres.
favor, fique em
Uau. Isso é um abuso impressionante de bash.
P Daddy
13

Python: 504 519 caracteres

(Solução Python 3) Atualmente requer definir a entrada no formato mostrado na parte superior (o código de entrada não é contado). Expandirei para ler o arquivo ou stdin mais tarde. Agora funciona com um prompt, basta colar a entrada (8 linhas no 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')

Não tenho certeza se posso economizar muito mais lá. Muitos caracteres são perdidos na transformação para bitfields, mas isso salva muito mais caracteres do que trabalhar com as strings. Além disso, não tenho certeza se posso remover mais espaços em branco, mas vou tentar mais tarde.
Não conseguirá reduzir muito mais; após ter a solução baseada em bitfield, eu fiz a transição de volta para strings, pois encontrei uma maneira de compactá-la mais (economizei 8 caracteres no bitfield!). Mas dado que esqueci de incluir o Le tive um erro com os pontos dentro, minha contagem de personagens só aumenta suspiro ... Talvez eu encontre algo mais tarde para comprimir um pouco mais, mas acho que estou perto do fim. Para o código original e comentado, veja abaixo:

Versão 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] ) )
picar
fonte
Eu não acho que isso seja correto. Não há regra dizendo que apenas o resultado final pode desaparecer, mas a julgar pelos seus comentários, você só verifica essa linha.
Michael Madsen,
Por favor, faça sua entrada como em uma tarefa. Quer dizer, entrada de arquivo ou STDIN.
Nakilon
6
Você não ama como mesmo o código Python reduzido ainda é razoavelmente legível?
EMP
@Evgeny, apenas se comparado com Perl ou Malbolge)
Nakilon
Bem, eu quis dizer "legível" em relação a outras respostas de código de golfe!
EMP
13

Ruby 1.9, 357 355 353 339 330 310 309 chars

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

Observe que os \000escapes (incluindo os bytes nulos na terceira linha) devem ser substituídos por seus equivalentes reais não imprimíveis.

Entrada de amostra:

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

Uso:

ruby1.9 tetris.rb < input

ou

ruby1.9 tetris.rb input
14 rotações
fonte
Outra maneira de largar os tetrominos e manter todos os vidros arrumados, mesmo com bordas ... legal. Agora você será o líder Ruby / Perl. PS: Eu não sabia ?\s.
Nakilon,
12

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

Este é meu primeiro código de golfe, acho que a contagem de caracteres pode ficar muito menor, seria bom se jogadores de golfe experientes pudessem me dar algumas dicas.

A versão atual também pode lidar com campos de jogo com dimensões diferentes. A entrada pode ter quebras de linha nos formatos DOS / Windows e Unix.

O código era bastante direto antes da otimização, os tetrominós são armazenados em 4 inteiros que são interpretados como uma matriz de (7 * 3) x4 bits, o campo de jogo é armazenado como está, as peças são descartadas e as linhas completas são removidas no início e após cada queda da telha.

Eu não tinha certeza de como contar caracteres, então usei o tamanho do arquivo do código com todas as quebras de linha desnecessárias removidas.

EDIT 596 => 581: Graças ao KitsuneYMG, tudo exceto a %lssugestão funcionou perfeitamente, além disso, percebi que em putchvez de putcharpode ser usado (de getchalguma forma não funciona) e removi todos os parênteses #define G.

EDIT 581 => 556: Não estava satisfeito com os loops restantes fore aninhados F, então houve alguma fusão, alteração e remoção de loops, bastante confuso, mas definitivamente valeu a pena.

EDIT 556 => 517: Finalmente encontrei uma maneira de fazer aum array int. Alguns se N;fundiram c, não breakmais.

EDIT 496 => 471: Largura e altura do campo de jogo corrigidas agora.

EDIT 471 => 461: Pequenas modificações, putcharusado novamente como putchnão é uma função padrão.

EDIT: Correção de bug, linhas completas foram removidas antes da queda do ladrilho em vez de depois , então linhas completas poderiam ser deixadas no final. Fix não altera a contagem 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 revs
fonte
1
Você não pode definir forcomo #define F(x,m) for(x=0;x++<m;)? Funciona em C # ...: P
BrunoLM 07/10/10
@BrunoLM: Obrigado, mas isso não vai funcionar, fe F(x,3){printf("%i",x}imprime ao 12invés de 012com essa mudança. Pode mudar para for(x=-1;x++<m;), mas isso não salva nada :)
schnaader
1
Se você escreveu o código corretamente, se você compilar como C, não precisa incluir stdio.h (a menos que eu tenha esquecido alguma coisa?). Salve alguns caracteres :)
1
Você pode substituir sua definição de N por (c=getchar())e remover todas as linhas c = N economizando 6 caracteres. A menos que eu esteja errado sobre isso, você deve baixar para 585
KitsuneYMG
1
tipo padrão também para variáveis, pelo menos para C89.
ninjalj
8

Python 2.6+ - 334 322 316 caracteres

397 368 366 caracteres descompactados

#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')

A única nova linha é necessária e eu a contei como um caractere.

O mumbo jumbo da página de código do navegador pode impedir um copiar e colar desse código, portanto, você pode gerar o arquivo opcionalmente a partir deste 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()))

Testando

Intetris

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

As novas linhas devem ser no estilo Unix (somente alimentação de linha). Uma nova linha à direita na última linha é opcional.

Testar:

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

Este código descompacta o código original e o executa com exec. Este código descompactado pesa 366 caracteres e se parece com isto:

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

As novas linhas são obrigatórias e têm um caractere cada.

Não tente ler este código. Os nomes das variáveis ​​são literalmente escolhidos ao acaso em busca da compressão mais alta (com nomes de variáveis ​​diferentes, vi até 342 caracteres após a compressão). Uma versão mais compreensível a seguir:

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

O ponto crucial está nas três linhas crípticas que eu disse que explicaria.

A forma dos tetrominós é codificada no número hexadecimal lá. Cada tetronimo é considerado ocupar uma grade 3x4 de células, onde cada célula está em branco (um espaço) ou cheia (um sinal de número). Cada peça é então codificada com 3 dígitos hexadecimais, cada dígito descrevendo uma coluna de 4 células. Os dígitos menos significativos descrevem as colunas mais à esquerda e o bit menos significativo em cada dígito descreve a célula mais alta de cada coluna. Se um bit for 0, então essa célula está em branco, caso contrário, é um '#'. Por exemplo, o I tetronimo é codificado como 00F, com os quatro bits do dígito menos significativo definido para codificar os quatro sinais numéricos na coluna mais à esquerda, e o T é131 , com o bit superior definido à esquerda e à direita e os dois bits superiores colocados no meio.

O número hexadecimal inteiro é então deslocado um bit para a esquerda (multiplicado por dois). Isso nos permitirá ignorar a parte inferior. Vou explicar por que em um minuto.

Assim, dada a parte atual da entrada, encontramos o índice neste número hexadecimal onde os 12 bits que descrevem sua forma começam, então mudamos isso para baixo de forma que os bits 1–12 (pulando o bit 0) da bitsvariável descrevem a parte atual.

A atribuição de dropdetermina quantas linhas do topo da grade a peça cairá antes de pousar em outros fragmentos da peça. A primeira linha encontra quantas células vazias existem no topo de cada coluna do campo de jogo, enquanto a segunda encontra a célula ocupada mais baixa em cada coluna da peça. A zipfunção retorna uma lista de tuplos, onde cada tuplo consiste em o n ° de células de cada item na lista de entrada. Então, usando a placa de entrada de amostra, zip(board[:6] + [full])retornará:

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

Selecionamos a tupla dessa lista correspondente à coluna apropriada e encontramos o índice da primeira '#'na coluna. É por isso que acrescentamos uma linha "completa" antes de chamar zip, para que indextenha um retorno sensato (em vez de lançar uma exceção) quando a coluna estiver em branco.

Em seguida, para encontrar o mais baixo '#'em cada coluna da peça, deslocamos e mascaramos os quatro bits que descrevem essa coluna e, em seguida, usamos a binfunção para transformá-la em uma sequência de uns e zeros. A binfunção retorna apenas bits significativos, portanto, precisamos apenas calcular o comprimento dessa string para encontrar a célula ocupada mais baixa (bit do conjunto mais significativo). A binfunção também precede '0b', então temos que subtrair isso. Também ignoramos o bit menos significativo. É por isso que o número hexadecimal é deslocado um bit para a esquerda. Isso leva em conta as colunas vazias, cujas representações de string teriam o mesmo comprimento de uma coluna com apenas a célula superior cheia (como a peça T ).

Por exemplo, as colunas da I tetromino, como mencionado anteriormente, são F, 0e0 . bin(0xF)é '0b1111'. Depois de ignorar o '0b', temos um comprimento de 4, o que é correto. Mas bin(0x0)é 0b0. Depois de ignorar o '0b', ainda temos o comprimento '1, o que é incorreto. Para explicar isso, adicionamos um bit adicional ao final, para que possamos ignorar esse bit insignificante. Conseqüentemente, o +3no código está lá para explicar o comprimento extra ocupado pelo '0b'no início e o bit insignificante no final.

Tudo isso ocorre em uma expressão geradora para três colunas ( (0,1,2)), e pegamos o minresultado para encontrar o número máximo de linhas que a peça pode perder antes de tocar em qualquer uma das três colunas.

O resto deve ser bem fácil de entender lendo o código, mas o forloop após essas atribuições adiciona a peça ao tabuleiro. Depois disso, o whileloop remove as linhas inteiras, substituindo-as por linhas em branco no topo, e registra a pontuação. No final, o tabuleiro e a pontuação são impressos na saída.

P papai
fonte
6

Python, 298 caracteres

Vence todas as soluções de linguagem não esotérica até agora (Perl, Ruby, C, bash ...)


... e nem mesmo usa a chicana code-zip.

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

No exemplo de teste

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

isso produz

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

PS. consertou um bug apontado por Nakilon ao custo de +5

Nas Banov
fonte
Esse é um código bastante impressionante. Seriam necessários mais 14 caracteres para consertá-lo ( ideone.com/zeuYB ), a menos que haja uma maneira melhor, mas mesmo assim, bate tudo menos GolfScript e Bash. Certamente é uma solução inteligente.
P Daddy
Sim, uma solução absolutamente excelente!
ChristopheD de
@Nakilon: obrigado, obviamente perdi aquele. fixo @ custo 293-> 298
Nas Banov
@P Papai, obrigado - eu encontrei uma maneira de corrigir o bash & toolchain para me manter honesto ao dizer "tudo não esotérico" :)
Nas Banov
@Nabb: para manter o código mais curto, ele foi escrito com algumas limitações em mente. algo como 33 tetrominos no máximo e 99 quedas de linha no máximo. Pode ser facilmente estendido pelo preço de +3. Ou por um preço baixo-baixo :), as limitações podem ser totalmente removidas. BTW, este é um ótimo exemplo de como ter um conjunto de teste teria esclarecido as especificações (o que eu estava incomodando ChristopherD nos comentários)
Nas Banov
5

Golfscript 260 chars

Tenho certeza que isso poderia ser melhorado, sou meio novo no 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?$*

O fim das linhas é relevante (não deve haver uma no final). Enfim, aqui estão alguns dos casos de teste que usei:

> 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

Observe que não há fim de linha no arquivo de entrada, um fim de linha interromperia o script como está.

coderaj
fonte
2
/ me considera GolfScript não uma verdadeira linguagem de competição ... É apenas uma biblioteca, moldada diretamente para tarefas de golfe ... O tamanho desta biblioteca pode ser adicionado ao tamanho do código golfscript ...
Nakilon,
4
@Nakilon - Você não pode dizer algo semelhante sobre qualquer coisa que não seja escrita em linguagem de máquina bruta? :) O interpretador Python é apenas uma biblioteca, adicione seu tamanho à sua entrada. </sarcasm>
bta
2
@Nakilon: é apenas o intérprete. Pode ser escrito em qualquer outro idioma; você ainda diria que Golfscript não é uma linguagem real?
Michael Foukarakis
1
@Nabb: Obrigado, achei que havia alguns truques que esqueci ... Não se sinta mal, também não me incomodei em entender meu código :).
coderaj
1
@Michael Foukarakis, posso em 1 minuto escrever meu próprio intérprete para resolver essa tarefa em um char, e daí?
Nakilon,
4

O'Caml 809 782 Chars

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

Lisp comum 667 657 645 caracteres

Minha primeira tentativa de golfe de código, então provavelmente há muitos truques que eu ainda não conheço. Deixei algumas novas linhas lá para manter alguma "legibilidade" residual (contei as novas linhas como 2 bytes, portanto, remover 6 novas linhas desnecessárias ganha mais 12 caracteres).

Na entrada, coloque primeiro as formas e depois o 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)))

Testando

T2 Z6 I0 T7
[          ]
[          ]
[          ]
[          ]
[ #    #  #]
[ ## ######]
[==========]
[          ]
[          ]
[          ]
[#      ###]
[#     ### ]
[##### ####]
[==========]
10
NIL
lpetru
fonte
Não muito curto, mas +1 para feiúra! Imagino que seria assim que a sopa de letrinhas seria se viesse entre parênteses.
P Daddy
@P Daddy: Obrigado. Sim, provavelmente seria assim :).
lpetru
2

Ruby 505 479 474 442 439 426 chars

Uma primeira tentativa. Fiz isso com IronRuby. Tenho certeza que pode ser melhorado, mas eu realmente deveria trabalhar um pouco hoje!

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

Testando

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

Edite agora usando rubi normal. Peguei a saída das paredes ..

Mongus Pong
fonte
Mais um Rubista, legal! Mas faça um copo em volta dos tijolos.
Nakilon,
1

Outro em Ruby, 573 546 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

Teste:

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