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

Posted

技术标签:

【中文标题】如何确定在我的 2048 实现中哪些图块移动和合并?【英文标题】:How can I figure out which tiles move and merge in my implementation of 2048? 【发布时间】:2018-07-30 00:41:21 【问题描述】:

我正在构建一个 2048 WinForms 小游戏,只是为了好玩。

请注意,这与2048 AI 无关。我只是想做一个人类可以玩的2048游戏。

我首先决定使用 0-17 来表示瓷砖。 0 代表一个空的瓦片。 1 代表 2 瓦。 2 代表一个 4 瓦。 3 代表 8 块,以此类推。

然后我在考虑如何计算得到的棋盘,给定移动方向和移动前的棋盘。这是我的想法:

要向上移动,只需将板逆时针旋转 90 度,向左移动,然后将板向后旋转 要向右移动,只需将板顺时针旋转 180 度,向左移动,然后再旋转回来 要向下移动,只需将板顺时针旋转 90 度,向左移动,然后再旋转回来。

所以我只需要弄清楚当玩家向左移动时如何计算结果棋盘,然后我可以通过旋转棋盘、向左移动和向后旋转来计算剩下的方向。然后我用这个非常奇怪的左移算法来了 p。

通过添加 96 并强制转换为 char,将初始棋盘的每个整数转换为字符。现在,反勾号 (`) 表示空磁贴,a 表示 2 磁贴,b 表示 4 磁贴,依此类推,一直到p。 将字符连接成 4 个字符串,每个字符串代表棋盘的一行。

一个示例板可能如下所示:

aa``
````
```b
``cb

对于每个字符串,

删除所有反引号 使用正则表达式(是的,我在 2048 游戏中使用正则表达式)([a-p])\1 并获取字符串的第一个匹配项。 用新图块替换第一个匹配项 匹配尚未匹配的其余字符串,直到找不到更多匹配项为止。 如果字符串少于 4 个字符,则将其填充到右侧。 通过减去 96 将每个字符串变回整数数组

这就是我评估每一行的方式:

    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());
    

但是,我当前的算法存在一个非常大的问题。这个算法只告诉我一个移动的最终结果,但是我不知道我需要移动哪个图片框(我正在使用图片框显示瓷砖),每个图片框应该移动多少空间,以及哪个图片框需要显示新图像。我真的不想使用其他解决方案,我只想对我当前的解决方案进行一些更改。

这是我需要从每一行(字符串)中得到的东西:

List<(int x, int spaces)>。每个元素表示需要移动哪个图块(x 坐标),以及它应该移动多少个空格 (spaces)。 List<int>。每个元素代表被合并到的瓦片的 x 坐标。

如何从行字符串中获取这些信息?示例:

行字符串:

`a`a

将生成一个类似[(1, 1), (3, 3)] 的列表和另一个类似[1] 的列表。

【问题讨论】:

在下面的帖子中查看我的解决方案。我创建了自己的按钮数组(您可以改用图片框)。我创建了一个继承标准按钮类的类,然后添加了行号和列号等属性,以便我可以轻松判断按下了哪个按钮:***.com/questions/37165402/… @jdweng 我可以毫不费力地确定哪个图片框对应于哪一行和哪一列。我只是无法确定在执行特定方向的移动时移动了哪些图块。 可以给继承的类添加属性,更好的识别每个图片框。您可以添加图片名称或其他任何可以为您提供更多信息的名称。在您的情况下,磁贴名称可能会有所帮助。 【参考方案1】:

我不认为对字符的转换真的增加了任何有用的东西。如果您坚持使用数字表示(0 = 空),那么您可以使用以下逻辑来查找目标配置 哪个块去了哪里。这是伪代码(给出row):

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++;
        
    

现在fromTo 数组将为每个索引指示该原始位置的块所在的位置。 result 将具有最终值。从这两条信息中,您还可以知道合并了哪些块。 result[fromTo[i]] != row[i] 时合并原始位置 i 的块。您还知道一个块将行进的距离:i - fromTo[i]。简而言之,您拥有为每个块设置动画的所有信息。

示例

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]

【讨论】:

你能举个例子吗?如果行是 [0,1,0,1]fromToresult 会是什么?为什么? 我添加了一些示例。第一个将有[-1,0,-1,0] 用于fromTo。 -1 是默认值,当初始位置没有块时不会触及。两个零表示位置 1 和 3 的块都将转到位置 0(它们将合并的位置)。 result 将在第一个位置反映此合并:[2,0,0,0]。其他零是默认值,表示该位置将为空。

以上是关于如何确定在我的 2048 实现中哪些图块移动和合并?的主要内容,如果未能解决你的问题,请参考以下文章

用 90 行 Haskell 代码实现 2048 游戏

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

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

如何实现 2048 的合并功能

用js实现2048小游戏

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