Найти лучший ход в игре тетрис


10

Мне очень нравится тетрис, но я не очень хорош в этом. Только однажды я хотел бы увидеть, как этот космический корабль взлетает на моих глазах! А поскольку компьютеры очень хороши во всем, единственно возможное решение - создать программу, которая будет играть для меня ... за исключением того, что вы собираетесь сделать это для меня!

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

вход

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

Символ представляет собой любое из семи основных тетромино, найденных в тетрисе. Все части можно повернуть на 90 градусов, но не перевернуть. Все тетромино и их вращения следующие:

         #
S =  ##  ##
    ##    #

          #
Z = ##   ##
     ##  #

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

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

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

O = ##
    ##

    #
I = #  ####
    #
    #

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

I











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

Вывод

Ваш вывод будет идентичен вводу, но с идеальным положением тетромино. Тетромино должно быть представлено, #чтобы отличать их от предварительно размещенных блоков. В дополнение к этому, вы также должны вывести, сколько линий / отверстий создает ваше размещение в форме xL yHна новой строке.

Выходные данные для приведенного выше примера будут следующими 3 :

I











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

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

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

Правила и устранение неоднозначности

  • Это , поэтому выигрывает самая короткая правильная реализация.
  • Ввод / вывод может быть на любом носителе, который подходит вашему целевому языку (например, файл, стандартный ввод / вывод, текстовая область).
  • Если ваш целевой язык не поддерживает многострочный ввод (или это неудобно), вы можете вместо этого разделить каждую строку ввода запятыми ( ,).
  • Вы можете опустить вывод любых пустых строк в сетке.
  • Помните, что тетромино падает сверху - вы не можете поместить кусок «под землю». Поэтому вы можете предположить, что все возможные размещения фигуры будут на «уровне поверхности» (то есть между блоком и верхней частью доски нет блоков).
  • Предположим, что никогда не будет ситуации, в которой вы будете вынуждены участвовать в игре (размещенное тетромино касается верхнего центра поля).
  • Решения, идентичные по выходу, должны быть опущены (например, есть 3 выхода решений, если вы наивно вращаете Oфигуру).

1 Я знаю, что это создаст некоторые ложные срабатывания, но это упрощение.

2 Это размер сетки, используемый в версии Game Boy.

+3 Да, 0Hэто правильно. Проверьте еще раз, я сказал новые дыры; ^)


Что если фигура вызовет 1 новую дыру, но наберет 2 линии, а не 1?
Натан Меррилл

Сортировать по количеству строк в первую очередь. 2 строки> 1 строка, поэтому она выигрывает независимо от количества лунок.
Шон Лэтэм

2
Если вы освободите дыру, это даст вам -1H?
overactor

Хм, я не думал об этом - это могло произойти в результате определения наивной дыры. Да, я думаю, что так и будет.
Шон Лэтэм

В моем решении я не рассматривал освобождение дыр. Я понял определение проблемы таким образом, что существующие блоки не должны быть изменены. Таким образом, полные линии не должны быть удалены, а отверстия не должны быть освобождены.
Михаил Шейх

Ответы:


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 (без пробела). Когда вы в первый раз используете индекс цикла for, вы можете опустить инициализацию до 0, поскольку переменные объявлены глобально. Кроме того, я думаю, что вы используете функцию E только один раз, поэтому должна быть возможность просто вставить ее в строку.
Алхимик
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.