Trova la mossa migliore in una partita di Tetris


10

Tetris mi piace molto, ma non sono molto bravo. Solo una volta mi piacerebbe vedere quell'astronave decollare davanti ai miei occhi! E dato che i computer sono davvero fantastici in tutto, l'unica soluzione possibile è creare un programma per riprodurlo per me ... tranne per il fatto che farai quel qualcosa per me!

Dato un tetromino (forma composta da quattro quadrati) e una mappa del campo di gioco, devi posizionare il tetromino in modo tale da segnare il maggior numero di linee (rende il maggior numero di righe completamente pieno di blocchi) e crea il minor numero di nuovi buchi (uno spazio vuoto che non può "vedere" la parte superiore del campo di gioco 1 ).

Ingresso

L'input conterrà un carattere su una sola riga che rappresenta il tetromino in caduta, seguito da una griglia 10 * 18 2 di spazi ( ) e segni più ( +).

Il personaggio rappresenta uno dei sette tetromini base trovati in Tetris. Tutti i pezzi possono essere ruotati di 90 gradi, ma non capovolti. Tutti i tetromini e le loro rotazioni sono i seguenti:

         #
S =  ##  ##
    ##    #

          #
Z = ##   ##
     ##  #

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

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

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

O = ##
    ##

    #
I = #  ####
    #
    #

La griglia rappresenta il campo di gioco di Tetris, con +blocchi precedentemente posizionati. Pertanto, un input di esempio potrebbe essere il seguente:

I











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

Produzione

L'output sarà identico all'input, ma con il tetromino nella posizione ideale. Il tetromino dovrebbe essere rappresentato con #per differenziarli dai blocchi pre-posizionati. Inoltre, devi anche generare quante linee / buchi crea il posizionamento nel modulo xL yHsu una nuova linea.

L'output dell'esempio sopra riportato sarebbe il seguente 3 :

I











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

Devi produrre solo i migliori risultati; nel caso in cui due o più casi forniscano lo stesso punteggio, devi emetterli tutti (separati da una riga vuota). I risultati migliori devono essere determinati ordinando prima il numero di linee segnate (in ordine decrescente), quindi il numero di nuove buche create (in ordine crescente). Quindi, 1L 1Hè un punteggio migliore di 0L 0H.

Lavorerò sulla creazione di un elenco di vari input e output previsti su cui è possibile testare il programma. Guarda questo spazio.

Regole e chiarimento delle ambiguità

  • Questo è , quindi vince l'implementazione corretta più breve.
  • L'input / output può essere su qualsiasi supporto adatto alla lingua di destinazione (ad es. File, stdin / stdout, area di testo).
  • Se la tua lingua di destinazione non supporta l'immissione di più righe (o è scomodo farlo), puoi invece delimitare ciascuna riga dell'input con virgole ( ,).
  • È possibile omettere l'output di eventuali righe vuote nella griglia.
  • Ricorda che il tetromino cade dall'alto: non puoi posizionare il pezzo "sottoterra". Si può quindi presumere che tutti i possibili posizionamenti del pezzo saranno a "livello di superficie" (cioè non ci sono blocchi tra il pezzo e la parte superiore della tavola).
  • Supponi che non ci sarà mai una situazione in cui sei costretto a una partita (il tetromino piazzato tocca il centro del campo).
  • Le soluzioni identiche nell'output devono essere omesse (ad es. Ci sono 3 output di soluzioni se si ruota il Opezzo in modo ingenuo ).

1 Sono consapevole che ciò creerà alcuni falsi positivi, ma è una semplificazione.

2 Questa è la dimensione della griglia utilizzata nella versione Game Boy.

3 Sì, 0Hè corretto. Ricontrolla, ho detto nuovi buchi; ^)


Cosa succederebbe se un pezzo causasse 1 nuova buca, ma segnasse 2 linee, invece di segnare solo 1 linea?
Nathan Merrill,

Ordina prima per numero di righe. 2 linee> 1 linea, quindi vince indipendentemente dal numero di buche.
Sean Latham,

2
Se liberi un buco, ti dà -1H?
overactor

Hm, non ci ho pensato - potrebbe succedere a causa della definizione ingenua del buco. Sì, suppongo che sarebbe.
Sean Latham,

Nella mia soluzione non ho considerato di liberare i buchi. Ho compreso la definizione del problema in modo tale che i blocchi esistenti non debbano essere modificati. Pertanto, non è necessario rimuovere le linee complete e liberare i buchi.
Mikail Sheikh,

Risposte:


3

C 1009 byte

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

Ecco la versione ungolfed

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

Ho visto che la principale fonte di codice lungo sarebbe probabilmente la definizione delle tessere. Così ho deciso di rappresentarli come modelli di bit in un array di 4x4 bit. Ciò si traduce in 16 bit che si inseriscono facilmente in un singolo int. La tilesmatrice contiene tutti i modelli per le 19 possibili rotazioni delle 7 tessere.

Durante la compilazione, ignorare l'avviso che getsè deprecato. So che lo è, ma è il modo più breve di leggere una riga dall'input.


Su scala globale, è possibile rilasciare intcome si presume. Molti dei tuoi hanno printfsprodotto un solo personaggio. Potresti essere in grado di sostituirli con equivalenti putcharper salvare un paio di personaggi. Ad esempio, cambiando printf("\n")in putchar(10):)
Josh,

put ("") è ancora più breve se vuoi solo una nuova riga. Inoltre puoi usare return! C (nessuno spazio). La prima volta che si utilizza un indice di un ciclo for, è possibile omettere l'inizializzazione su 0 poiché le variabili vengono dichiarate globalmente. Inoltre, penso che tu usi la funzione E solo una volta, quindi dovrebbe essere possibile metterla in linea.
Alchymist,
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.