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) ([ap])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] .


    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]
    
    链接地址: http://www.djcxy.com/p/40216.html

    上一篇: 胜出的Hang子手优化算法

    下一篇: 在2048年的实现中,如何确定哪些图块移动和合并?