Encontre a melhor jogada em um jogo de Tetris

10

Gosto muito de Tetris, mas não sou muito bom nisso. Apenas uma vez eu gostaria de ver aquela nave espacial decolar na frente dos meus próprios olhos! E como os computadores são ótimos em tudo, a única solução possível é criar um programa para reproduzi-lo para mim ... exceto que você fará isso por mim!

Dado um tetromino (forma feita de quatro quadrados) e um mapa do campo de jogo, você deve colocar o tetromino de forma que ele marque o maior número de linhas (faça o maior número de linhas completamente cheio de blocos) e crie o menor número de novos buracos (um espaço vazio que não pode "ver" o topo do campo de jogo 1 ).

Entrada

A entrada conterá um caractere em uma única linha que representa o tetromino que cai, seguido por uma grade 10 * 18 2 de espaços ( ) e sinais de adição ( +).

O personagem representa qualquer um dos sete tetrominos básicos encontrados em Tetris. Todas as peças podem ser giradas 90 graus, mas não viradas. Todos os tetrominos e suas rotações são os seguintes:

         #
S =  ##  ##
    ##    #

          #
Z = ##   ##
     ##  #

    #   ###  ##
L = #   #     #    #
    ##        #  ###

     #  ###  ##
J =  #    #  #     #
    ##       #   ###

          #   #   #
T = ###  ##  ###  ##
     #    #       #

O = ##
    ##

    #
I = #  ####
    #
    #

A grade representa o campo de jogo de Tetris, +sendo blocos colocados anteriormente. Portanto, um exemplo de entrada pode ser o seguinte:

I











+       ++
+    +++++
++ +++++++
++ +++++++
++ +++++++
++ +++++++
++++++ +++

Resultado

Sua saída será idêntica à entrada, mas com o tetromino na posição ideal. O tetromino deve ser representado com #para diferenciá-los dos blocos pré-colocados. Além disso, você também deve gerar quantas linhas / furos sua veiculação cria no formulário xL yHem uma nova linha.

A saída para o exemplo dado acima seria a seguinte 3 :

I











+       ++
+    +++++
++#+++++++
++#+++++++
++#+++++++
++#+++++++
++++++ +++
4L 0H

Você deve produzir apenas os melhores resultados; no caso de dois ou mais casos com a mesma pontuação, você deve produzir todos eles (separados por uma linha em branco). Os melhores resultados devem ser determinados pela classificação pelo número de linhas marcadas (descendente) primeiro e depois pelo número de novos furos criados (ascendente). Então, 1L 1Hé uma pontuação melhor que 0L 0H.

Vou trabalhar na criação de uma lista de várias entradas e saídas esperadas contra as quais você pode testar seu programa. Assista esse espaço.

Regras e Desambiguação

  • Isso é , portanto, a implementação correta mais curta vence.
  • A entrada / saída pode estar em qualquer meio que seja adequado ao seu idioma de destino (por exemplo, arquivo, stdin / stdout, área de texto).
  • Se o seu idioma de destino não suportar entrada de várias linhas (ou for inconveniente), você poderá delimitar cada linha da entrada com vírgulas ( ,).
  • Você pode omitir a saída de qualquer linha em branco na grade.
  • Lembre-se de que o tetromino cai de cima - você não pode colocar a peça "no subsolo". Portanto, você pode assumir que todas as colocações possíveis da peça estarão no "nível da superfície" (ou seja, não há blocos entre a peça e a parte superior do quadro).
  • Suponha que nunca haverá uma situação em que você seja forçado a terminar o jogo (o tetromino colocado toca o centro superior do campo).
  • Soluções idênticas na saída devem ser omitidas (por exemplo, existem 3 saídas da solução se você girar a Opeça ingenuamente ).

1 Estou ciente de que isso criará alguns falsos positivos, mas é uma simplificação.

2 Esse é o tamanho da grade usado na versão do Game Boy.

3 Sim, 0Hestá correto. Verifique novamente, eu disse novos buracos; ^)

Sean Latham
fonte
E se uma peça causar um novo buraco, mas marcar 2 linhas, contra apenas marcar 1 linha?
Nathan Merrill
Classifique primeiro o número de linhas. 2 linhas> 1 linha, para ganhar, independentemente do número de furos.
Sean Latham
2
Se você liberar um buraco, isso lhe dá -1H?
overactor
Hum, eu não pensei nisso - isso poderia ocorrer como resultado da definição ingênua do buraco. Sim, acho que sim.
Sean Latham
Na minha solução, não considerei liberar buracos. Entendi a definição do problema de tal maneira que os blocos existentes não deveriam ser modificados. Portanto, nenhuma linha completa deve ser removida e nenhum furo liberado.
mikail sheikh

Respostas:

3

C 1009 bytes

#include <stdio.h>
#define L for(n=0;n<18;++n)
int T[19]={54,561,99,306,785,23,547,116,802,71,275,116,39,562,114,305,51,4369,15},W[19]={3,2,3,2,2,3,2,3,2,3,2,3,3,2,3,2,2,1,4},O[7]={0,2,4,8,12,16,17},R[7]={2,2,4,4,4,1,2},G[18],F[24],t=0,I,x,y,S[99],X[99],Y[99],N[99],s,M=0,i,j,k,l,m,n,h,c;char B[99],*C="SZLJTOI";void A(){for(m=0;m<24;++m)F[m]=0;for(m=0;m<4;++m)F[y+m]=(T[I]>>(m*4)&15)<<x;}void D(){S[s]=0;L S[s]+=(G[n]|F[n])==1023;S[s]=200*(S[s])+199;for(m=0;m<10;++m){l=0;L{h=(G[n]|F[n])&1<<m;l|=h;S[s]-=l&&!h;}}}int E(){A();c=0;L c|=G[n]&F[n];return !c;}int main(){S[0]=0;gets(B);while(C[t]!=B[0])++t;I=O[t];L{G[n]=0;gets(B);for(m=0;m<10;++m)G[n]|=(B[m]=='+')?(1<<m):0;}s=0;D();for(i=0;i<R[t];++i){for(x=0;x<10-W[I];x++){y=0;while(E())y++;--y;++s;A();D();if(S[s]>M)M=S[s];X[s]=x;Y[s]=y;N[s]=I;}I++;}for(i=1;i<s;++i)if(S[i]==M){j=i;x=X[i];y=Y[i];I=N[i];A();for(n=1;n<18;++n){for(m=0;m<10;++m)printf((G[n]&1<<m)!=0?"+":((F[n]&1<<m)!=0?"#":" "));printf("\n");}}printf("%dL %dH\n",S[j]/200,S[0]%200-S[j]%200);}

Aqui está a versão não destruída

#include <stdio.h>

int tiles[19] = {54,561,99,306,785,23,547,116,802,71,275,116,39,562,114,305,51,4369,15};
int widths[19] = {3,2,3,2,2,3,2,3,2,3,2,3,3,2,3,2,2,1,4};
char *codes = "SZLJTOI";
int offsets[7] = {0,2,4,8,12,16,17};
int transformations[7] = {2,2,4,4,4,1,2};
int gameField[18], tileField[24];

int i,j,k,l,m,n,h;
char buffer[99];
int tile=0, tileIndex;
int xpos, ypos;
int scores[99], xplacements[99], yplacements[99], tindex[99];
int scoreIndex, maxScore=0;

void readGame()
{
  gets(buffer);
  while (codes[tile]!=buffer[0]) ++tile;
  tileIndex = offsets[tile];
  for (n=0;n<18;++n)
  {
    gameField[n] = 0;
    gets(buffer);
    for (m=0; m<10;++m)
      gameField[n] |= (buffer[m]=='+')?(1<<m):0;
  }
}

void writeGame()
{
  for (n=1;n<18;++n)
  {
    for (m=0; m<10;++m)
      printf( (tileField[n] & 1<<m) != 0 ? "#" :((gameField[n] & 1<<m) != 0 ? "+" :" "));
    printf("\n");
  }
}

void placeTile()
{
  for (m=0;m<24;++m) tileField[m] = 0;
  for (m=0;m<4;++m) 
    tileField[ypos+m] = (tiles[tileIndex]>>(m*4) & 15) << xpos;
}

void score()
{
  scores[scoreIndex] = 0;
  for (n=0;n<18;++n) 
    if ((gameField[n] | tileField[n])==1023) scores[scoreIndex]++;

  scores[scoreIndex] = 200*(scores[scoreIndex]) + 199;

  for (m=0;m<10;++m)
  {
    l=0;
    for (n=0;n<18;++n)
    {
      h = (gameField[n] | tileField[n]) & 1<<m;
      l |= h;
      scores[scoreIndex] -= l && !h;
    }
  }
}

int noCollision()
{
  placeTile();
  int coll = 0;
  for (n=0;n<18;++n) coll |= gameField[n] & tileField[n];
  return !coll;
}

int main()
{ scores[0] = 0;
  readGame();
  scoreIndex = 0;
  score();
  for (i=0; i<transformations[tile]; ++i)
  {
    for (xpos=0; xpos<10-widths[tileIndex]; xpos++)
    {
      ypos = 0;
      while (noCollision()) ypos++;
      --ypos;
      ++scoreIndex;
      placeTile();
      score();
      if (scores[scoreIndex]>maxScore) maxScore=scores[scoreIndex];
      xplacements[scoreIndex] = xpos;
      yplacements[scoreIndex] = ypos;
      tindex[scoreIndex] = tileIndex;
    }
    tileIndex++;
  }

  for (i=1;i<scoreIndex; ++i) 
    if (scores[i]==maxScore)
    {
      j=i;
      xpos = xplacements[i];
      ypos = yplacements[i];
      tileIndex = tindex[i];
      placeTile();
      writeGame();
    }

  printf("%dL %dH\n", scores[j]/200, scores[0]%200-scores[j]%200);
}

Vi que a principal fonte de código extenso provavelmente seria a definição dos blocos. Então, decidi representá-los como padrões de bits em uma matriz de 4x4 bits. Isso resulta em 16 bits que se encaixam facilmente em um único int. A tilesmatriz contém todos os padrões para as 19 rotações possíveis dos 7 blocos.

Ao compilar, ignore o aviso que getsfoi descontinuado. Eu sei que é, mas é a maneira mais curta de ler uma linha da entrada.

mikail sheikh
fonte
Na escala global, você pode largar o intque é assumido. Vários de seus printfsúnicos produzem um único caractere. Você poderá substituí-los por equivalentes putcharpara salvar alguns caracteres. Por exemplo, mudando printf("\n")para putchar(10):)
Josh
puts ("") é ainda mais curto se você quiser apenas uma nova linha. Além disso, você pode usar return! C (sem espaço). Na primeira vez que você usa um índice de um loop for, pode omitir a inicialização para 0, pois as variáveis ​​são declaradas globalmente. Além disso, acho que você usa a função E apenas uma vez, para que seja possível colocá-la em linha.
Alchymist