Tile merging algorithm 2048 game
I am trying to recreate the game 2048 in C, but I can't get the algorithms to move or merge tiles together to function properly. In the original 2048 game you would move tiles together like this:
2 | 2 | 4 | 4 4 | 8 | |
---+---+---+--- *swipes to the left* -> ---+---+---+---
8 | | 8 | 16| | |
So two tiles that are the same can merge into one tile that is twice the size. My version is almost the same, but instead of using numbers I use characters that increment by one when they merge, so [A|A]
would merge to [B]
, etc. I did that only to not have to deal with varying size tiles.
So my board is stored as a 4*4 char array inside a struct I called grid (I know probably a bit redundant)
typedef struct grid {
char tiles[4][4];
} Grid;
I have tried to make algorithms to move and merge up, down, left and right, but they don't work properly.
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
}
}
I tested these algorithms with some hardcoded testdata. The algorithm to push the tiles to the left seems to be working correctly. pushRight almost works, but it merges two levels at the same time, so [B|A|A]
merges into [C]
but should merge into [B|B]
.
pushUp seems to be almost always just wiping the entire board with empty tiles (spaces). pushDows seems to be removing some tiles.
Does anyone see the problem or know a way to do this? I have thought about using recursive algorithms, but I just can't wrap my head around it.
I would personally break the swipe into two steps as the swipe left and swipe right are actually functionally the same regarding tile combination. The only difference is that the remaining tiles are bunched to either the left or the right depending on direction.
Below is a quick algorithm to replace two tiles with the a new one. I scan left->right and replace the left tile with the new tile, zero the right tile and then make sure I exclude this new tile from comparison:
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;
}
Output of this:
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
After this a simple compaction step could be made, or output realtime to a second buffer, or which ever. Less duplication.
I only have done the case of pushing the lines to the left, but it the same method for every direction. I took the code of the answer and modify it; take a look:
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("nnnn");
showGrid (&g);
printf("nnnn");
pushLeft(&g);
showGrid (&g);
printf("nnnn");
find_great_tile(&g);
showGrid(&g);
printf("nnnn");
pushLeft(&g);
showGrid(&g);
printf("nnnn");
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, "tt |");
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, "nn");
}
}
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;
}
}
}
}
}
}
Output of this:
| 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 | | |
Of course, you can compress the steps doing:
--> PushLeft
--> FindGreatTile
--> PushLeft
链接地址: http://www.djcxy.com/p/6848.html上一篇: 在C ++中使用'&'符号
下一篇: 瓷砖合并算法2048游戏