테트리스 게임에서 최고의 움직임을 찾으십시오


10

나는 테트리스를 많이 좋아하지만 그다지 능숙하지 않습니다. 우주선이 내 눈앞에서 이륙하는 것을보고 싶을 때만! 그리고 컴퓨터는 모든 것에 아주 뛰어 나기 때문에 가능한 해결책은 프로그램이 나를 위해 그것을 재생하도록 만드는 것입니다.

테트로 미노 (네 개의 사각형으로 이루어진 모양)와 경기장의지도가 주어지면, 가장 많은 수의 라인을 기록하고 ( 최대 행 수가 블록으로 가득 차도록) 테트로 미노를 배치 하고 가장 적은 수를 생성해야합니다. 새로운 구멍 (경기장 상단의 "볼"수없는 빈 공간 1 ).

입력

입력은 떨어지는 테트로 미노를 나타내는 한 줄의 문자를 포함하고 그 뒤에 공백 ( ) 및 더하기 부호 ( ) 의 10 * 18 그리드 2 가 옵니다 .+

이 문자는 테트리스에서 발견되는 7 가지 기본 테트로 미노 중 하나를 나타냅니다. 모든 조각을 90도 회전 할 수 있지만 뒤집을 수는 없습니다. 모든 테트로 미노 및 회전은 다음과 같습니다.

         #
S =  ##  ##
    ##    #

          #
Z = ##   ##
     ##  #

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

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

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

O = ##
    ##

    #
I = #  ####
    #
    #

그리드는 +이전에 배치 된 블록 으로 테트리스의 경기장을 나타냅니다 . 따라서 입력 예는 다음과 같습니다.

I











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

산출

출력은 입력과 동일하지만 이상적인 위치에 테트로 미노가 있습니다. tetromino는 #미리 배치 된 블록 과 구별하기 위해 표현해야합니다 . 이 외에도, 당신은 당신의 게재 위치가 xL yH새로운 라인에 폼 에서 얼마나 많은 라인 / 홀을 생성하는지 출력해야합니다 .

위에 제공된 예제의 출력은 다음 3입니다 .

I











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

최상의 결과 만 출력해야합니다. 동일한 점수를 부여하는 두 개 이상의 사례의 경우 모두 빈 칸으로 구분하여 출력해야합니다. 가장 좋은 결과는 먼저 득점 (내림차순) 라인 수를 기준으로 정렬 한 다음 생성 된 새 홀 (오름차순) 수를 기준으로 정렬하여 결정합니다. 따라서 1L 1H보다 나은 점수 0L 0H입니다.

프로그램을 테스트 할 수있는 다양한 입력 및 예상 출력 목록을 작성합니다. 이 공간을보십시오.

규칙과 명확성

  • 이것은 이므로 가장 짧은 올바른 구현이 승리합니다.
  • 입력 / 출력은 대상 언어 (예 : 파일, 표준 입력 / 표준 출력, 텍스트 영역)에 적합한 매체에있을 수 있습니다.
  • 대상 언어가 여러 줄 입력을 지원하지 않는 경우 (또는 그렇지 않은 경우) 입력의 각 줄을 쉼표 ( ,) 로 구분할 수 있습니다 .
  • 그리드에서 빈 줄의 출력을 생략 할 수 있습니다.
  • 테트로 미노가 위에서 떨어지는 것을 기억하십시오-조각을 "지하"에 놓을 수 없습니다. 따라서 조각의 가능한 모든 배치가 "표면 수준"에 있다고 가정 할 수 있습니다 (즉, 조각과 보드 상단 사이에 블록이 없음).
  • 당신이 게임 오버로 강요된 상황이 없을 것이라고 가정하자 (배치 된 테트로 미노는 필드의 중앙에 닿는다).
  • 출력이 동일한 솔루션은 생략해야합니다 (예 : O조각 을 순진하게 회전하는 경우 3 개의 솔루션 출력이 있음 ).

1 이것이 오탐 (false positive)을 유발할 수 있음을 알고 있지만 단순화입니다.

2 Game Boy 버전에서 사용되는 격자 크기입니다.

3 예, 0H맞습니다. 다시 확인, 나는 새로운 구멍을 말했다 ; ^)


만약 한 조각이 1 개의 홀을 만들지 만 2 개의 라인 만 득점한다면 1 개의 라인 만 득점한다면?
Nathan Merrill

먼저 줄 수를 기준으로 정렬하십시오. 2 줄> 1 줄이므로 구멍 수에 관계없이 승리합니다.
Sean Latham

2
구멍을 뚫 으면 -1H가 나옵니까?
overactor

흠, 나는 그것을 생각하지 않았다-순진한 구멍 정의의 결과로 발생할 수 있습니다. 그렇습니다.
Sean Latham

내 솔루션에서 나는 구멍을 비우는 것을 고려하지 않았습니다. 기존 블록을 수정해서는 안되는 방식으로 문제 정의를 이해했습니다. 따라서 완전한 라인을 제거하거나 구멍을 확보해서는 안됩니다.
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);}

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

긴 코드의 주요 소스는 아마도 타일의 정의 일 것입니다. 그래서 4x4 비트 배열에서 비트 패턴으로 표현하기로 결정했습니다. 이로 인해 단일 비트에 쉽게 맞는 16 비트가 만들어집니다 int. tiles어레이는 7 개 개의 타일 (19 개)의 회전 가능한 모든 패턴을 보유하고있다.

컴파일 할 때 gets사용되지 않는 경고를 무시하십시오 . 나는 그것이 알고 있지만 입력에서 줄을 읽는 가장 짧은 방법입니다.


글로벌 규모 int에서 가정 한대로을 (를) 삭제할 수 있습니다 . 당신의 몇몇은 printfs하나의 문자를 출력합니다. putchar두 문자를 저장 하기 위해 그것들을 동등한 것으로 바꿀 수 있습니다 . 예를 들어, 변화 printf("\n")하는 putchar(10):)
조쉬

개행을 원하면 puts ( "")가 더 짧아집니다. 또한 return! c (공백 없음)를 사용할 수 있습니다. for 루프의 인덱스를 처음 사용할 때는 변수가 전역 적으로 선언 될 때 초기화를 0으로 생략 할 수 있습니다. 또한 함수 E를 한 번만 사용하므로 인라인으로 넣는 것이 가능해야한다고 생각합니다.
Alchymist
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.