Tìm nước đi tốt nhất trong trò chơi Tetris


10

Tôi thích Tetris rất nhiều, nhưng tôi không giỏi lắm. Chỉ một lần tôi muốn thấy con tàu vũ trụ cất cánh trước mắt tôi! Và vì máy tính rất tuyệt vời trong mọi thứ, giải pháp khả thi duy nhất là tạo một chương trình để chơi nó cho tôi ... ngoại trừ bạn sẽ làm điều đó cho tôi!

Cho một tetromino (hình dạng được tạo bởi bốn hình vuông) và bản đồ của sân chơi, bạn phải đặt tetromino sao cho nó có số dòng lớn nhất (tạo ra số lượng lớn nhất các hàng đầy đủ các khối) và tạo ra số lượng ít nhất các lỗ mới (một khoảng trống không thể "nhìn thấy" đỉnh của sân chơi 1 ).

Đầu vào

Đầu vào sẽ chứa một ký tự trên một dòng đại diện cho tetromino đang rơi, theo sau là 10 * 18 lưới 2 khoảng trắng ( ) và dấu cộng ( +).

Nhân vật đại diện cho bất kỳ trong số bảy tetrominoes cơ bản được tìm thấy trong Tetris. Tất cả các mảnh có thể được xoay 90 độ, nhưng không được lật. Tất cả các tetrominoes và phép quay của chúng như sau:

         #
S =  ##  ##
    ##    #

          #
Z = ##   ##
     ##  #

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

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

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

O = ##
    ##

    #
I = #  ####
    #
    #

Lưới đại diện cho sân chơi của Tetris, với +các khối được đặt trước đó. Vì vậy, một ví dụ đầu vào có thể là như sau:

I











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

Đầu ra

Đầu ra của bạn sẽ giống hệt với đầu vào, nhưng với tetromino ở vị trí lý tưởng. Tetromino nên được đại diện #để phân biệt chúng với các khối được đặt trước. Ngoài ra, bạn cũng phải xuất ra bao nhiêu dòng / lỗ mà vị trí của bạn tạo trong biểu mẫu xL yHtrên một dòng mới.

Đầu ra cho ví dụ nêu trên sẽ là 3 :

I











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

Bạn chỉ xuất kết quả tốt nhất (s); trong trường hợp có hai hoặc nhiều trường hợp cho cùng một số điểm, bạn phải xuất tất cả chúng (cách nhau bằng một dòng trống). Các kết quả tốt nhất được xác định bằng cách sắp xếp theo số lượng dòng được ghi (giảm dần) trước, sau đó số lượng lỗ mới được tạo (tăng dần). Vì vậy, 1L 1Hlà một điểm tốt hơn 0L 0H.

Tôi sẽ làm việc để tạo một danh sách các đầu vào khác nhau và (các) đầu ra dự kiến ​​mà bạn có thể kiểm tra chương trình của mình. Xem không gian này.

Quy tắc và định hướng

  • Đây là , do đó, việc thực hiện đúng ngắn nhất sẽ thắng.
  • Đầu vào / đầu ra có thể ở bất kỳ phương tiện nào phù hợp với ngôn ngữ đích của bạn (ví dụ: tệp, stdin / stdout, vùng văn bản).
  • Nếu ngôn ngữ đích của bạn không hỗ trợ đầu vào nhiều dòng (hoặc bất tiện khi làm như vậy), thay vào đó, bạn có thể phân định từng dòng của đầu vào bằng dấu phẩy ( ,).
  • Bạn có thể bỏ qua đầu ra của bất kỳ dòng trống nào trong lưới.
  • Hãy nhớ rằng tetromino rơi từ trên cao xuống - bạn có thể không đặt mảnh "dưới lòng đất". Do đó, bạn có thể giả định rằng tất cả các vị trí có thể có của mảnh sẽ ở "cấp độ bề mặt" (nghĩa là không có khối nào giữa mảnh và mặt trên của bảng).
  • Giả sử rằng sẽ không bao giờ có tình huống bạn bị buộc phải tham gia trò chơi (tetromino được đặt chạm vào trung tâm trên cùng của trường).
  • Các giải pháp giống hệt nhau ở đầu ra phải được bỏ qua (ví dụ: có 3 giải pháp đầu ra nếu bạn xoay một cách ngây thơ O).

1 Tôi biết rằng điều này sẽ tạo ra một số tích cực sai, nhưng đó là một sự đơn giản hóa.

2 Đây là kích thước lưới được sử dụng trong phiên bản Game Boy.

3 Vâng, 0Hđúng. Kiểm tra lại, tôi nói lỗ mới ; ^)


Điều gì sẽ xảy ra nếu một mảnh sẽ gây ra 1 lỗ mới, nhưng ghi được 2 dòng, so với chỉ ghi được 1 dòng?
Nathan Merrill

Sắp xếp theo số dòng đầu tiên. 2 dòng> 1 dòng, vì vậy nó thắng bất kể số lượng lỗ là bao nhiêu.
Sean Latham

2
Nếu bạn giải phóng một lỗ hổng, điều đó có cho bạn -1H không?
overactor

Hừm, tôi đã không nghĩ về điều đó - nó có thể xảy ra như là kết quả của định nghĩa lỗ ngây thơ. Vâng, tôi đoán nó sẽ.
Sean Latham

Trong giải pháp của tôi, tôi đã không xem xét giải phóng các lỗ hổng. Tôi hiểu định nghĩa vấn đề theo cách mà các khối hiện có không nên được sửa đổi. Vì vậy, không có dòng hoàn chỉnh nên được loại bỏ và không có lỗ giải phóng.
mikail sheikh

Câu trả lời:


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

Đây là phiên bản chưa được chỉnh sửa

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

Tôi thấy rằng nguồn chính của mã dài có lẽ sẽ là định nghĩa của gạch. Vì vậy, tôi quyết định biểu diễn chúng dưới dạng các mẫu bit trong một mảng 4 x 4. Điều này dẫn đến 16 bit dễ dàng phù hợp với một int. Các tilesmảng chứa tất cả các mô hình cho 19 phép quay có thể của 7 gạch.

Khi biên dịch, bỏ qua cảnh báo getsbị phản đối. Tôi biết nó là nhưng đó là cách ngắn nhất để đọc một dòng từ đầu vào.


Ở quy mô toàn cầu, bạn có thể bỏ qua intnhư giả định. Một số printfschỉ xuất ra một ký tự duy nhất của bạn. Bạn có thể thay thế chúng bằng tương đương putcharđể lưu một vài ký tự. Ví dụ: đổi printf("\n")thành putchar(10):)
Josh

đặt ("") thậm chí còn ngắn hơn nếu bạn chỉ muốn một dòng mới. Ngoài ra, bạn có thể sử dụng return! C (không có không gian). Lần đầu tiên bạn sử dụng chỉ mục của vòng lặp for, bạn có thể bỏ qua việc khởi tạo thành 0 vì các biến được khai báo trên toàn cầu. Ngoài ra, tôi nghĩ rằng bạn chỉ sử dụng chức năng E một lần nên có thể chỉ cần đặt nội tuyến.
Alchymist
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.