Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I figure out which tiles move and merge in my implementation of 2048?

I am building a little 2048 WinForms game just for fun.

Note that this is not about a 2048 AI. I am just trying to make a 2048 game that can be played by humans.

I first decided to use 0-17 to represent the tiles. 0 represents an empty tile. 1 represents a 2 tile. 2 represents a 4 tile. 3 represents a 8 tile, and so on.

Then I was thinking about how to calculate the resulting board, given the direction of movement and the board before the move. Here's what I thought about:

  • To move up, it's just rotating the board counterclockwise by 90 degrees, move left, then rotate the board back
  • To move right, it's just rotating the board clockwise by 180 degrees, move left, then rotate back
  • To move down, it's just rotating the board clockwise by 90 degrees, move left, then rotate back.

So I just need to figure out how to calculate the resulting board when the player moves left, then I can just figure out the rest of the directions by rotating the board, move left, and rotating back. I then came p with this quite bizarre algorithm for moving left.

  • Convert each of the initial board's integers into characters by adding 96 and casting to char. Now a back tick (`) represents an empty tile, a represents a 2 tile, b represents a 4 tile, and so on, al the way to p.
  • Concatenate the characters to form 4 strings, each representing a row of the board.
  • An example board might look like this:

    aa``
    ````
    ```b
    ``cb
    
  • For each string,

    • Remove all the back ticks
    • Use the regex (yes I'm using a regex in a 2048 game) ([a-p])\1 and get the first match of the string.
    • replace the first match with the new tile
    • match the rest of the string which hasn't been matched yet until no more matches is found.
  • Pad the string to the right if it has fewer than 4 characters.
  • Turn each string back to an array of integers by subtracting 96

So this is how I evaluate each row:

    int[] EvaluateRow(int[] row) {
        // RowToString converts an int[] to a string like I said above
        StringBuilder rowString = new StringBuilder(RowToString(row));
        rowString.Replace("`", "");
        var regex = new Regex("([a-p])\\1");
        int lastIndex = -1;
        while (true) {
            var match = regex.Match(rowString.ToString(), lastIndex + 1);
            if (match.Success) {
                // newChar is the new tile after the merge
                char newChar = (char)(match.Value[0] + 1);
                rowString.Remove(match.Index, match.Length);
                rowString.Insert(match.Index, newChar);
                lastIndex = match.Index;

                Score += // some calculation for score, irrelevant
            } else {
                break;
            }
        }
        // StringToRow converts a string to an int[]
        return StringToRow(rowString.ToString());
    }

However, there is a really big problem with my current algorithm. This algorithm only tells me the final result of a move, but I don't know which picture box (I'm using picture boxes to show the tiles) I need to move, how many spaces should each picture box move, and which picture boxes need to show a new image. I really want to not use another solution and I want to just make some changes to my current solution.

Here are the things I need to get from each row (string):

  • A List<(int x, int spaces)>. Each element represents which tile needs to move (the x coordinate), and how many spaces it should move (spaces).
  • A List<int>. Each element represents the x coordinates of the tiles which is merged into.

How can I get these information from a row string? Example:

The row string:

`a`a

will produce a list like [(1, 1), (3, 3)] and another list like [1].

like image 329
Sweeper Avatar asked Feb 19 '18 18:02

Sweeper


People also ask

What is the trick for 2048 game?

Keep it Cornered An easy way to beat the game is to keep your largest number in a corner. The way to do this is to keep the row or column filled with other numbers. This guarantees it will not move while you move around the other numbers.

Is there an algorithm to solve 2048?

Problem Explanation. Solving this game is an interesting problem because it has a random component. It's impossible to correctly predict not only where each new tile will be placed, but whether it will be a “2” or a “4”. As such, it is impossible to have an algorithm that will correctly solve the puzzle every time.

How many moves does it take to get 2048?

In this post, we'll answer that question by modeling the game of 2048 as a Markov chain and analyzing it to show that, no matter how well the player plays, the number of moves required to win the game is at least 938.8 on average.

What the highest tile you can get in 2048?

Higher-scoring tiles emit a soft glow; the highest possible tile is 131,072.


1 Answers

I don't think the transformation to characters is really adding anything useful. If you stick with the number representation (0 = empty), then you can employ the following logic to find both the target configuration and which block went where. This is pseudo code (row is given):

fromTo = [-1, -1, -1, -1];
result = [0, 0, 0, 0];
prevVal = 0;
pos = 0;

for (i = 0; i < 4; i++) {
    if (row[i]) { // Not empty
        if (row[i] == prevVal) {
            result[pos-1]++; // merged
            fromTo[i] = pos-1;
            prevVal = 0; // avoid triple merge
        } else {
            result[pos] = row[i];
            fromTo[i] = pos;
            prevVal = row[i];
            pos++;
        }
    }
}

Now the fromTo array will indicate for each index, where the block at that original position went to. The result will have the final values. From those two pieces of information you can also know which blocks were merged. A block at original position i is merged when result[fromTo[i]] != row[i]. You also know the distance a block will travel: i - fromTo[i]. In short, you have all information to set up an animation for each block.

Examples

row         |   fromTo       |   result
------------+----------------+-----------
[0,1,0,1]   |  [-1,0,-1,0]   |  [2,0,0,0]
[0,1,1,1]   |  [-1,0,0,1]    |  [2,1,0,0]
[1,1,1,1]   |  [0,0,1,1]     |  [2,2,0,0]
[1,2,2,3]   |  [0,1,1,2]     |  [1,3,3,0]
like image 75
trincot Avatar answered Oct 07 '22 17:10

trincot