在俄罗斯方块游戏中找到最佳动作


10

我非常喜欢俄罗斯方块,但是我不太擅长。我只想看到那艘飞船在我眼前起飞!而且由于计算机在所有方面都非常出色,因此唯一可行的解​​决方案是编写一个程序供我使用...除非您要为我做这件事!

给定一个tetromino(由四个正方形组成的形状)和一个运动场的地图,您将放置tetromino,以使其得分最多的行(使最大的行数完全充满块)并创建最少的行的(一个空的空间不能“看”比赛场上的顶部1)。

输入值

输入将在一行上包含一个代表下降的tetromino的字符,然后是一个10 * 18的网格2,由空格()和加号(+)组成。

该字符代表在俄罗斯方块中发现的七个基本四聚体中的任何一个。所有的片段都可以旋转90度,但不能翻转。所有四蛋白骨及其旋转如下:

         #
S =  ##  ##
    ##    #

          #
Z = ##   ##
     ##  #

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

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

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

O = ##
    ##

    #
I = #  ####
    #
    #

网格代表俄罗斯方块的运动场,+并被预先放置。因此,示例输入如下:

I











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

输出量

您的输出将与输入相同,但四合胺在理想位置。应该用tetromino #来区分它们和预先放置的块。除此之外,您还需要xL yH在新行上输出您的展示位置在表格中创建的行数/孔数。

上面给出的示例的输出为以下3

I











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

您仅输出最佳结果;如果两个或更多个案例给出相同的分数,则将它们全部输出(用空行分隔)。最好的结果是通过先按得分(降序)的行数排序,然后再按创建(升序)的新孔数进行排序来确定的。因此,1L 1H比更好0L 0H

我将创建一个可以用来测试程序的各种输入和预期输出的列表。关注此空间。

规则与歧义

  • 这是,因此最短的正确实现为胜。
  • 输入/输出可以使用适合您目标语言的任何媒体(例如文件,stdin / stdout,文本区域)。
  • 如果您的目标语言不支持多行输入(或者这样做很不方便),则可以用逗号(,)分隔输入的每一行。
  • 您可以省略网格中任何空白行的输出。
  • 请记住,tetromino从天而降-您不得将其放置在“地下”。因此,您可以假定该块的所有可能放置都在“表面级别”(即,该块与板的顶部之间没有障碍物)。
  • 假定永远不会出现您被迫进入游戏的情况(放置的四聚丁胺纸碰触到该区域的顶部中心)。
  • 输出中相同的溶液必须省略(例如,如果您天真地旋转O工件,则有3个溶液输出)。

1我知道这会产生一些误报,但这只是一种简化。

2这是Game Boy版本中使用的网格大小。

3是的,0H是正确的。再次检查,我说有孔; ^)


如果一个棋子会造成1个新洞,但得分2行而只得分1行怎么办?
弥敦道·美林

首先按行数排序。2行> 1行,因此无论孔数多少,它都会赢。
肖恩·拉瑟姆

2
如果您释放了一个孔,是否给您-1H?
overactor 2014年

嗯,我没想到这一点-它可能是由于天真的孔定义而导致的。是的,我想会的。
肖恩·拉瑟姆

在我的解决方案中,我没有考虑释放漏洞。我理解问题定义的方式是不应该修改现有块。因此,不应去除整条生产线,也不会释放任何孔。
mikail sheikh 2014年

Answers:


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数组包含7个图块的19种可能旋转的所有模式。

编译时,请忽略gets不建议使用的警告。我知道是这样,但这是从输入中读取一行的最短方法。


在全球范围内,您可以int像假定的那样删除。您的几个printfs仅输出一个字符。您也许可以将它们替换为等效putchar字符以节省几个字符。例如,更改printf("\n")putchar(10):)
乔什(Josh)2014年

如果只需要换行符,puts(“”)会更短。您也可以使用return!c(无空格)。第一次使用for循环的索引时,由于变量是全局声明的,因此可以将初始化省略为0。另外,我认为您只使用函数E一次,因此应该可以将其内联。
Alchymist 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.