编程之美----俄罗斯方块

编程之美—-俄罗斯方块

问题:

让电脑自动下俄罗斯方块游戏。


解法:

对当前的积木块,枚举它旋转后的每一个形状从每一列落下的棋盘,将该棋盘和前一个棋盘进行对比,并打分,最后取得分最高的那个形状和那一列作为电脑的当前操作。

[cpp] view plaincopy

  1. #include <iostream>  
  2. #include <algorithm>  
  3. #include <cstdlib>  
  4. #include <ctime>  
  5. using namespace std;  
  6.   
  7. // 积木块的信息  
  8. #define BLOCK_SIZE  7  
  9. #define ROTATE_SIZE 4  
  10. #define SIDE_LEN    4  
  11. const int BLOCK_AREA = SIDE_LEN*SIDE_LEN;  
  12. // 棋盘的信息  
  13. #define HORIZ_LEN   12  
  14. #define VERT_LEN    15  
  15. const int CHESS_AREA = HORIZ_LEN*VERT_LEN;  
  16. // 其它信息  
  17. #define TOP_STEP    25  
  18. #define inf         10000  
  19. // 计分信息  
  20. const int clearLineScore[] = {0, 1, 3, 7, 13};  
  21.   
  22. struct Block    // 积木块  
  23. {  
  24.     void init(unsigned char *l)   
  25.     {  
  26.         memcpy(layout, l, BLOCK_AREA);  
  27.         int i, j;  
  28.         for (i=0; i<SIDE_LEN; i++)  
  29.         {  
  30.             for (j=SIDE_LEN-1; j>=0 && layout[j*SIDE_LEN+i]==0; j–);  
  31.             maxRow[i] = j+1;  
  32.         }  
  33.         for(i=0; i<SIDE_LEN && maxRow[i]==0; i++);  
  34.         minCol = i;  
  35.         for (i=SIDE_LEN-1; i>=0 && maxRow[i]==0; i–);  
  36.         maxCol = i;  
  37.     }  
  38.     unsigned char layout[BLOCK_AREA];   // 积木块的布局  
  39.     unsigned char maxRow[SIDE_LEN];     // 积木块每一列所占区域的最大行,取值从1开始  
  40.     unsigned char minCol;               // 积木块所占区域的最小值列和最大列,取值从0开始  
  41.     unsigned char maxCol;  
  42. };  
  43.   
  44. Block blockSet[BLOCK_SIZE][ROTATE_SIZE];    // 7种积木块,每种积木块有4种旋转方向  
  45. unsigned char chess[CHESS_AREA];        // 棋盘的布局  
  46. unsigned char nextChess[CHESS_AREA];    // 下一步棋盘的布局  
  47. int height[HORIZ_LEN];                  // 棋盘每一列所占区域的最小行,即高度  
  48.   
  49. void calcHeight(unsigned char *curchess)    // 计算当前棋盘的高度信息  
  50. {  
  51.     int i, j;  
  52.     for (i=0; i<HORIZ_LEN; i++)  
  53.     {  
  54.         for (j=0; j<VERT_LEN && curchess[j*HORIZ_LEN+i]==0; j++);  
  55.         height[i] = j;  
  56.     }  
  57. }  
  58.   
  59. // 计算若积木块从offsetX列落下,会落在第几行,即offsetY  
  60. int calcBottomOffsetY(const Block& b, int offsetX)    
  61. {  
  62.     int offsetY = VERT_LEN;  
  63.     for (int i=0; i<SIDE_LEN; i++)  
  64.     {  
  65.         if (b.maxRow[i]==0) continue;  
  66.         offsetY = min(offsetY, height[offsetX+i]-b.maxRow[i]);  
  67.     }  
  68.     return offsetY;  
  69. }  
  70.   
  71. // 将积木块贴到棋盘上  
  72. void pasteTo(unsigned char *curchess, const Block& b,  
  73.     int offsetX, int offsetY)  
  74. {  
  75.     for (int i=b.minCol; i<=b.maxCol; i++)  
  76.         for (int j=0; j<SIDE_LEN; j++)  
  77.         {  
  78.             unsigned char bij = b.layout[j*SIDE_LEN + i];  
  79.             unsigned char& cij = curchess[(j+offsetY)*HORIZ_LEN + i+offsetX];  
  80.             if (bij && cij==0)  
  81.                 cij = bij;  
  82.             else if (bij && cij)  
  83.                 cout << “ERROR” << endl;  
  84.         }  
  85. }  
  86.   
  87. // 消去[offsetY,offsetY+SIDE_LEN)中remline为1的行  
  88. void clearLines(unsigned char *curchess, int offsetY, unsigned char *remline)  
  89. {  
  90.     int i, j, gap=0;  
  91.     for (j=offsetY+SIDE_LEN-1; j>=0; j–)  
  92.     {  
  93.         if (j-offsetY>=0 && remline[j-offsetY])  
  94.             gap++;  
  95.         else if (gap)  
  96.         {  
  97.             memcpy(curchess+(j+gap)*HORIZ_LEN, curchess+j*HORIZ_LEN, HORIZ_LEN);  
  98.         }  
  99.     }  
  100. }  
  101.   
  102. // 计算[offsetX,offsetX+SIDE_LEN)列的洞的个数  
  103. int calcHoles(unsigned char *curchess, int offsetX, int offsetY)  
  104. {  
  105.     int i, j;  
  106.     int holeCount = 0;  
  107.     for (i=offsetX; i<offsetX+SIDE_LEN; i++)  
  108.     {  
  109.         if (i<0 || i>=HORIZ_LEN) continue;  
  110.         for (j=offsetY; j<VERT_LEN && curchess[j*HORIZ_LEN+i]==0; j++);  
  111.         for (; j<VERT_LEN; j++)  
  112.             if (curchess[j*HORIZ_LEN+i]==0)  
  113.                 holeCount++;  
  114.     }  
  115.     return holeCount;  
  116. }  
  117.   
  118. // 计算当前棋盘的得分  
  119. int calcScore(unsigned char *curchess, int offsetX, int offsetY)  
  120. {  
  121.     int i, j, score = 0;  
  122.     int remlineCount = 0;  
  123.     unsigned char remline[SIDE_LEN] = {0};  
  124.     // 统计消行数  
  125.     for (j=offsetY; j<offsetY+SIDE_LEN; j++)  
  126.     {  
  127.         for (i=0; i<HORIZ_LEN && curchess[j*HORIZ_LEN+i]; i++);  
  128.         if (i==HORIZ_LEN)   
  129.         {  
  130.             remlineCount++;  
  131.             remline[j-offsetY] = 1;  
  132.         }  
  133.     }  
  134.     score += clearLineScore[remlineCount];  
  135.     // 统计洞的个数  
  136.     if (remlineCount)  
  137.         clearLines(curchess, offsetY, remline);  
  138.     int holeCount = calcHoles(curchess, offsetX, offsetY) –   
  139.                     calcHoles(chess, offsetX, offsetY);  
  140.     score -= holeCount*4;  
  141.     // 位置过高则扣分  
  142.     if (holeCount > 5) score -= 15;  
  143.     if (offsetY-remlineCount < VERT_LEN*3/5)  
  144.         score -= VERT_LEN*3/5-(offsetY-remlineCount);  
  145.     return score;  
  146. }  
  147.   
  148. void output(unsigned char *curchess)  
  149. {  
  150.     for (int j=0; j<VERT_LEN; j++)  
  151.     {  
  152.         for (int i=0; i<HORIZ_LEN; i++)  
  153.             cout << curchess[j*HORIZ_LEN+i] << ” “;  
  154.         cout << endl;  
  155.     }  
  156. }  
  157.   
  158. int main()  
  159. {  
  160.     srand(time(0));  
  161.     int i, j, k, n, m;  
  162.     // 初始化积木块  
  163.     for (i=0; i<BLOCK_SIZE; i++)  
  164.         for (j=0; j<ROTATE_SIZE; j++)  
  165.         {  
  166.             unsigned char l[BLOCK_AREA];  
  167.             for (k=0; k<BLOCK_AREA; k++)  
  168.                 scanf(“%d”,&l[k]);  
  169.             blockSet[i][j].init(l);  
  170.         }  
  171.     // 初始化棋盘  
  172.     for (i=0; i<CHESS_AREA; i++)  
  173.         scanf(“%d”,&chess[i]);  
  174.     // 显示前TOP_STEP步  
  175.     int offsetX, offsetY;  
  176.     unsigned char tmpchess[CHESS_AREA];  
  177.     for (n=TOP_STEP; n>=0; n–)  
  178.     {  
  179.         output(chess);  
  180.         calcHeight(chess);  // 为每一步计算一次height数组,避免计算offsetY时过多的重复计算  
  181.         int bind = rand()%BLOCK_SIZE;   // 积木块的序号  
  182.         int maxScore = -inf;  
  183.         for (j=0; j<ROTATE_SIZE; j++)    // 要考虑积木块的各种旋转情况  
  184.         {  
  185.             const Block& b = blockSet[bind][j];     // 得到当前的积木块  
  186.             for (offsetX=-b.minCol; offsetX<HORIZ_LEN-b.maxCol; offsetX++)  
  187.             {  
  188.                 // 计算从offsetX列落下,所落在棋盘的位置offsetY  
  189.                 offsetY = calcBottomOffsetY(b, offsetX);  
  190.                 if (offsetY<0) continue;  
  191.                 memcpy(tmpchess, chess, CHESS_AREA);  
  192.                 pasteTo(tmpchess, b, offsetX, offsetY); // 在棋盘中添加积木块  
  193.                 int curScore = calcScore(tmpchess, offsetX, offsetY);   // 计算当前情况下的得分  
  194.                 if (curScore > maxScore)  
  195.                 {  
  196.                     maxScore = curScore;  
  197.                     memcpy(nextChess, tmpchess, CHESS_AREA);  
  198.                 }  
  199.             }  
  200.         }  
  201.         memcpy(chess, nextChess, CHESS_AREA);  
  202.     }  
  203. }  

参考来源:   http://blog.csdn.net/linyunzju/article/details/7686822

    原文作者:快乐的霖霖
    原文地址: https://blog.csdn.net/chdhust/article/details/8365844
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞