瓦片合并算法2048游戏

Posted

技术标签:

【中文标题】瓦片合并算法2048游戏【英文标题】:Tile merging algorithm 2048 game 【发布时间】:2015-02-10 12:48:44 【问题描述】:

我正在尝试用 C 语言重新创建游戏 2048,但我无法让算法将图块移动或合并在一起以正常运行。 在最初的 2048 游戏中,您可以像这样一起移动图块:

 2 | 2 | 4 | 4                             4 | 8 |   |   
---+---+---+---  *swipes to the left* ->  ---+---+---+---
 8 |   | 8 |                               16|   |   |

因此,两个相同的图块可以合并为一个大小为两倍的图块。我的版本几乎相同,但我没有使用数字,而是使用在它们合并时递增 1 的字符,所以[A|A] 将合并到[B] 等。我这样做只是为了不必处理不同大小的图块。

所以我的板子在我称为 grid 的结构中存储为 4*4 字符数组(我知道可能有点多余)

typedef struct grid 
    char tiles[4][4];
 Grid;

我试图让算法来上下左右移动和合并,但它们不能正常工作。

void pushLeft(Grid * grid)

    int i, j, k;
    for(i = 0; i < 4; i++) //Row number i
    
        for(j = 1; j < 4; j++) //Column number j
        
            if(grid->tiles[i][j] != ' ') //tile is not empty
            
                int flag = 1; //flag to prevent merging more than one level at a time
                //Starting on column k, push tile as far to the left as possible
                for(k = j; k > 0; k--)
                
                    if(grid->tiles[i][k-1] == ' ') //neighbor tile is empty
                    
                        grid->tiles[i][k-1] = grid->tiles[i][k];
                        grid->tiles[i][k] = ' ';
                    
                    else if(grid->tiles[i][k-1] == grid->tiles[i][k] && flag) //neighbor equals
                    
                        grid->tiles[i][k-1]++;
                        grid->tiles[i][k] = ' ';
                        flag = 0;
                    
                    else //Can't push or merge
                    
                        flag = 1;
                        break;
                    
                
            
         // Done with row
    


void pushRight(Grid * grid)

    int i, j, k;
    for(i = 0; i < 4; i++) //Row number i
    
        for(j = 2; j >= 0; j--) //Column number j
        
            if(grid->tiles[i][j] != ' ') //tile is not empty
            
                int flag = 1; //flag to prevent merging more than one level at a time
                //Starting on column k, push tile as far to the right as possible
                for(k = j; k < 3; k++)
                
                    if(grid->tiles[i][k+1] == ' ') //neighbor tile is empty
                    
                        grid->tiles[i][k+1] = grid->tiles[i][k];
                        grid->tiles[i][k] = ' ';
                    
                    else if(grid->tiles[i][k+1] == grid->tiles[i][k] && flag) //neighbor equals
                    
                        grid->tiles[i][k+1]++;
                        grid->tiles[i][k] = ' ';
                        flag = 0;
                    
                    else //Can't push or merge
                    
                        flag = 1;
                        break;
                    
                
            
         // Done with row
    


void pushUp(Grid * grid)

    int i, j, k;
    for(i = 0; i < 4; i++) //Column number i
    
        for(j = 1; j < 4; j++) //Row number j
        
            if(grid->tiles[j][i] != ' ') //tile is not empty
            
                int flag = 1; //flag to prevent merging more than one level at a time
                //Starting on row k, push tile as far upwards as possible
                for(k = j; k > 0; k--)
                
                    if(grid->tiles[k-1][i] == ' ') //neighbor tile is empty
                    
                        grid->tiles[k-1][i] = grid->tiles[i][k];
                        grid->tiles[k][i] = ' ';
                    
                    else if(grid->tiles[k-1][i] == grid->tiles[i][k] && flag) //neighbor equals
                    
                        grid->tiles[k-1][i]++;
                        grid->tiles[k][i] = ' ';
                        flag = 0;
                    
                    else //Can't push or merge
                    
                        flag = 1;
                        break;
                    
                
            
         // Done with column
    


void pushDown(Grid * grid)

    int i, j, k;
    for(i = 0; i < 4; i++) //Column number i
    
        for(j = 2; j >= 0; j--) //Row number j
        
            if(grid->tiles[j][i] != ' ') //tile is not empty
            
                int flag = 1; //flag to prevent merging more than one level at a time
                //Starting on row k, push tile as far down as possible
                for(k = j; k < 3; k++)
                
                    if(grid->tiles[k+1][i] == ' ') //neighbor tile is empty
                    
                        grid->tiles[k+1][i] = grid->tiles[i][k];
                        grid->tiles[k][i] = ' ';
                    
                    else if(grid->tiles[k+1][i] == grid->tiles[i][k] && flag) //neighbor equals
                    
                        grid->tiles[k+1][i]++;
                        grid->tiles[k][i] = ' ';
                        flag = 0;
                    
                    else //Can't push or merge
                    
                        flag = 1;
                        break;
                    
                
            
         // Done with column
    

我用一些硬编码的测试数据测试了这些算法。将瓷砖向左推的算法似乎工作正常。 pushRight 几乎可以工作,但它同时合并了两个级别,所以 [B|A|A] 合并到 [C] 但应该合并到 [B|B]

pushUp 似乎几乎总是用空瓷砖(空格)擦拭整个棋盘。 pushDows 似乎正在移除一些图块。

有没有人发现问题或知道解决方法?我曾考虑过使用递归算法,但我就是想不通。

【问题讨论】:

一个瓦片不能在一次移动中合并两次,因此应考虑将合并结果的瓦片标记为这样。然后,您将在合并之前检查是否有如此标记的图块在此移动期间不符合合并条件。 感谢您的评论。我试图通过在最内层循环中使用一个标志来实现它,如果当前图块被合并,该标志会变为假。我现在看到这还不够,因为推送的下一个图块可以与之前推送的图块合并。 【参考方案1】:

我个人会将滑动分为两个步骤,因为向左滑动和向右滑动实际上在功能上是相同的关于瓷砖组合。唯一的区别是剩余的瓷砖会根据方向向左或向右聚集。

下面是一个快速算法,用一个新的瓷砖替换两个瓷砖。我扫描左->右并用新图块替换左图块,将右图块归零,然后确保我从比较中排除这个新图块:

typedef struct grid 
    char tiles[4][4];
 Grid;

void eliminateHoriz (Grid* g)

    int row, col, col2;
    for (row=0; row<4; row++)
    
        for (col=0; col<4; col++)
        
            if (g->tiles[row][col])
            
                for (col2=col+1; col2<4; col2++)
                
                    if (g->tiles[row][col2])
                    
                        if (g->tiles[row][col] == g->tiles[row][col2])
                        
                            g->tiles[row][col++] *= 2;
                            g->tiles[row][col2] = 0;
                        
                        break;
                    
                
            
        
    


void showGrid (Grid* g)

    int row, col;
    for (row=0; row<4; row++)
        for (col=0; col<4; col++)
            printf ("%4d%c",
                g->tiles[row][col],
                col == 3 ? '\n' : ' ');
    printf ("\n");


int main() 

    Grid g = 2,2,4,4, 
               8,0,8,0,
               8,8,8,4, 
               2,2,2,2;

    showGrid (&g);
    eliminateHoriz (&g);
    showGrid (&g);

    system ("pause");
    return 0;

输出:

   2    2    4    4
   8    0    8    0
   8    8    8    4
   2    2    2    2

   4    0    8    0
  16    0    0    0
  16    0    8    4
   4    0    4    0

在此之后,可以进行一个简单的压缩步骤,或者实时输出到第二个缓冲区,或者其他任何一个。减少重复。

【讨论】:

我知道这已经晚了 6 年,但请注意输出在技术上是不正确的。第一行的 4 和 8 之间不能有零。【参考方案2】:

我只做过将线条向左推的情况,但每个方向都是相同的方法。我拿了答案的代码并修改了它;看看:

typedef struct grid 
    int tiles[4][4];
 Grid;

/* Functions prototypes */

void pushLeft(Grid* grid);
void showGrid (Grid* g);
void find_great_tile(Grid* grid);

/*  Main function   */

int main() 

    Grid g = 4,2,2,8, 
               2,8,2,2,
               16,2,0,2, 
               128,128,64,64;

    /*

    The sequence is:

    --> Show the grid
    --> PushLeft
    --> Find great tile
    --> PushLeft
    --> Show the grid

    */

    printf("\n\n\n\n");
    showGrid (&g);
    printf("\n\n\n\n");
    pushLeft(&g);
    showGrid (&g);
    printf("\n\n\n\n");
    find_great_tile(&g);
    showGrid(&g);
    printf("\n\n\n\n");
    pushLeft(&g);
    showGrid(&g);
    printf("\n\n\n\n");

    return 0;


/* Functions definitions */

void pushLeft(Grid* grid)

    int row, col, col2;

    for (row = 0; row < 4; row++)
    
        for (col = 0; col < 4; col++)
        
            if (!grid->tiles[row][col])
            
                for (col2 = col+1; col2 < 4; col2++)
                
                    if (grid->tiles[row][col2])
                    

                        /*
                        if (grid->tiles[row][col] == grid->tiles[row][col2])
                        
                            grid->tiles[row][col++] *= 2;
                            grid->tiles[row][col2] = 0;
                        

                        break;
                        */

                        grid->tiles[row][col] = grid->tiles[row][col2];
                        grid->tiles[row][col2] = 0;

                        break;
                    
                
            
        
    


void showGrid (Grid* grid)

    int row, col;

        for(row = 0; row < 4; row++)

            fprintf(stdout, "\t\t     |");

            for(col = 0; col < 4; col++)
            

            /*
             In case there's any number in the matrix, it will print those numbers, otherwise, it'll print a space (it is the alternative of putting a 0)
             */

                if(grid->tiles[row][col])
                
                    printf("%4d    |", grid->tiles[row][col]);
                else
                    printf("%4c    |", ' ');
            

            fprintf(stdout, "\n\n");
        


void find_great_tile(Grid* grid)

    int row, col, col2;

    for(row = 0; row < 4; row++)
    
        for(col = 0; col < 4; col++)
        
            if(grid->tiles[row][col])
            
                col2 = col+1;

                if(grid->tiles[row][col2])
                
                    if(grid->tiles[row][col] == grid->tiles[row][col2])
                    
                        grid->tiles[row][col++] *= 2;

                        grid->tiles[row][col2] = 0;
                    
                
            
        
    

这个输出:

         |   4    |   2    |   2    |   8    |

         |   2    |   8    |   2    |   2    |

         |  16    |   2    |        |   2    |

         | 128    | 128    |  64    |  64    |





         |   4    |   2    |   2    |   8    |

         |   2    |   8    |   2    |   2    |

         |  16    |   2    |   2    |        |

         | 128    | 128    |  64    |  64    |





         |   4    |   4    |        |   8    |

         |   2    |   8    |   4    |        |

         |  16    |   4    |        |        |

         | 256    |        | 128    |        |





         |   4    |   4    |   8    |        |

         |   2    |   8    |   4    |        |

         |  16    |   4    |        |        |

         | 256    | 128    |        |        |

当然,你可以压缩步骤做:

--> 左推

--> 查找GreatTile

--> 左推

【讨论】:

以上是关于瓦片合并算法2048游戏的主要内容,如果未能解决你的问题,请参考以下文章

2048游戏回顾二:算法总结(移动合并动画等)

JavaJavaSE算法之2048

js实现2048小游戏二维数组更新算法

如何确定在我的 2048 实现中哪些图块移动和合并?

微信小程序版2048

2048小游戏主要算法实现