2048 tile fusion algorithm

I'm trying to recreate a 2048 game in C, but I can't seem to get the algorithms to move or merge tiles together to function properly. In the original 2048 game, you have to move the tiles as follows:

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

      

This way, two tiles that are the same can be combined into one tile that is twice as large. My version is pretty much the same, but instead of using numbers, I'm using characters that increment by one when merged, so [A|A]

will merge with [B]

, etc. I did this to avoid having to deal with different sizes.

So my board is stored as a 4 * 4 char array inside a struct I called grid (I know, probably a little redundant)

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

      

I tried to make algorithms to move and merge up, down, left and right, but they don't work as expected.

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 have tested these algorithms with some hardcoded testdata. The algorithm to push the tiles on the left seems to be working correctly. pushRight almost works, but it merges the two levels at the same time, so [B|A|A]

it merges in [C]

, but must merge at [B|B]

.

pushUp almost always just wipes the entire board with empty tiles (spaces). pushDows seems to be removing some fragments.

Does anyone see the problem or know a way to do this? I've been thinking about using recursive algorithms, but I just can't wrap my head around myself.

+3


source to share


2 answers


I personally would break the napkins two steps, since the napkins on the left and the swipe on the right are actually functionally the same with regard to the tile combination. The only difference is that the remaining tiles are grouped either left or right depending on the direction.

Below is a quick algorithm to replace two slabs with a new one. I scan left-> right and replace the left tile with a new tile, zero correct tile, and then make sure I exclude this new tile from the 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;
}

      



The result 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 that, you can do a simple compacting step or output the second buffer in real time, or ever. Less duplication.

0


source


I only made a case of clicking the lines on the left, but this is the same method for each direction. I took the response code and changed 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("\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 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;
                    }
                }
            }
        }
    }
}

      

The result 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    |        |        |

      



You can of course compress the steps:

-> PushLeft

-> FindGreatTile

-> PushLeft

0


source







All Articles