Знайдіть найкращий хід у грі на тетрісі


10

Мені дуже подобається тетріс, але я в цьому не дуже добре. Якраз хотів би побачити, як космічний корабель злітає на моїх очах! А оскільки комп’ютери настільки чудові у всьому, єдине можливе рішення - це зробити програму, щоб вона відтворила її для мене ... за винятком того, що ви збираєтеся робити це для мене!

Враховуючи тетроміно (форма з чотирьох квадратів) та карту ігрового поля, ви повинні розмістити тетроміно таким чином, щоб він набрав найбільшу кількість рядків (робить найбільшу кількість рядків повністю заповненими блоками) і створює найменше число з нових отворів (порожнього простору , яке не може «бачити» у верхній частині ігрового поля 1 ).

Вхідні дані

Вхід буде містити символ в одному рядку, який представляє тетраміно, що випадає, з подальшим 10 * 18 сіткою 2 пробілів ( ) та знаками плюс ( +).

Символ являє собою будь-який із семи базових тетромінонів, знайдених у тетрісі. Усі шматки можна обертати на 90 градусів, але не перевертати. Усі тетроміно та їх обертання наступні:

         #
S =  ##  ##
    ##    #

          #
Z = ##   ##
     ##  #

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

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

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

O = ##
    ##

    #
I = #  ####
    #
    #

Сітка представляє ігрове поле Tetris, з +попередньо розміщеними блоками. Отже, прикладом введення може бути такий:

I











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

Вихідні дані

Ваш вихід буде ідентичним вхідному, але з тетроміно в ідеальному положенні. Тетроміно має бути представлене, #щоб відрізняти їх від попередньо розміщених блоків. На додаток до цього, ви також повинні вивести, скільки ліній / отворів створює ваше розташування у формі xL yHв новому рядку.

Вихід для наведеного вище прикладу буде таким 3 :

I











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

Ви повинні виводити лише найкращі результати; у випадку, коли два чи більше випадків дають однаковий бал, ви повинні вивести їх усі (розділені порожнім рядком). Найкращі результати слід визначати, сортуючи спочатку кількість набраних рядків (у спаді), а потім кількість нових отворів (зростаючих). Отже, 1L 1Hкращий бал, ніж 0L 0H.

Я буду працювати над створенням списку різних входів та очікуваних результатів, на які ви можете протестувати свою програму. Слідкуйте за цим простором.

Правила та розбіжність

  • Це , тому найкоротша правильна реалізація виграє.
  • Введення / вихід може бути на будь-якому носії, що відповідає вашій цільовій мові (наприклад, файл, stdin / stdout, область тексту).
  • Якщо ваша цільова мова не підтримує введення декількох рядків (або це незручно), натомість ви можете розмежувати кожен рядок введення комами ( ,).
  • Ви можете опустити вихід будь-яких порожніх рядків у сітці.
  • Пам’ятайте, що тетроміно падає зверху - ви можете не розмістити шматок «під землею». Ви можете, таким чином, припустити, що всі можливі розміщення шматка будуть знаходитись на рівні "поверхні" (тобто між шматком та верхом дошки немає блоків).
  • Припустимо, що ніколи не буде ситуації, в якій вас змушують переграти гру (розміщене тетроміно торкається верхнього центру поля).
  • Рішення, ідентичні у виході, повинні бути пропущені (наприклад, є 3 виходи рішення, якщо ви наївно обертаєте Oшматок).

1 Я знаю, що це створить помилкові позитиви, але це спрощення.

2 Це розмір сітки, який використовується у версії Game Boy.

3 Так, 0Hце правильно. Перевірте ще раз, я сказав нові дірки; ^)


Що робити, якщо шматок спричинить 1 нову лунку, але набрав 2 рядки проти 1 балу?
Натан Меррілл

Сортуйте спочатку за кількістю рядків. 2 рядки> 1 рядок, тому він виграє незалежно від кількості отворів.
Шон Латхем

2
Якщо ви звільнили отвір, це дає вам -1Н?
суперфінік

Гм, я не думав про це - це може статися в результаті визначення наївної діри. Так, я думаю, це було б.
Шон Латем

У своєму рішенні я не враховував звільнення отворів. Я зрозумів визначення проблеми таким чином, що існуючі блоки не слід змінювати. Таким чином, жодних повних ліній не слід видаляти і жодних отворів не звільняти.
mikail sheikh

Відповіді:


3

C 1009 байт

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

Ось версія, що не має волі

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

Я бачив, що головним джерелом тривалого коду, мабуть, буде визначення плиток. Тому я вирішив представити їх як бітові візерунки в масиві 4x4 біт. Це призводить до 16 біт, які легко вписуються в єдиний int. tilesМасив містить всі шаблони для 19 можливих поворотів 7 плиток.

Під час компіляції ігноруйте попередження, яке getsзастаріло. Я знаю, що це так, але це найкоротший шлях зчитування рядка з вхідних даних.


У глобальному масштабі ви можете опустити так, intяк це передбачається. Кілька ваших printfsлише виводить один символ. Ви можете замінити їх на еквівалент, putcharщоб зберегти пару символів. Наприклад, перехід printf("\n")на putchar(10):)
Джош

put ("") ще коротше, якщо ви просто хочете новий рядок. Також ви можете використовувати return! C (немає місця). Перший раз, коли ви використовуєте індекс циклу, ви можете опустити ініціалізацію до 0, оскільки змінні оголошуються глобально. Крім того, я думаю, що ви використовуєте функцію E лише один раз, тому її можна буде просто поставити в рядок.
Алхімік
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.