【问题标题】:Naive Gravity for Tetris game using 2D Array for the playfieldNaive Gravity for Tetris 游戏使用 2D 数组作为运动场
【发布时间】:2014-02-04 02:54:32
【问题描述】:

我正在编写俄罗斯方块克隆,并且正在使用 C# 进行原型设计。最终的代码应该在嵌入式系统上运行(使用 8 位 CPU 和非常少的 RAM),所以我尝试使用一种简单的算法来清除行。

现在,我的运动场是一个 2D 数组:

private readonly TetrominoType[][] _playfield;

(其中TetrominoType 是一个枚举,表示 None 或 7 种类型之一,用于着色块)

当一行被清除时,我想就地修改这个数组,这就是我的问题所在。举个例子:

   Before       After
0 #      #     #      #
1 #      #     #      #
2 #      #     #      #
3 #      #     #      #
4 #      #     #      #
5 #xxxxxx#     #      #
6 #x   xx#     #      #
7 #xxxxxx#     #      #
8 #xxxxxx#     #x   xx#
9 #x xxxx#     #x xxxx#
  ########     ########

我已经确定需要删除第 5、7 和 8 行,因此其他行应该倒下,留下右边的状态。

我的幼稚方法是向后迭代并复制已清除的行上方的行,基本上:

for(int iy = 9; iy >= 0; iy--) {
    if(_linesToClear.Contains(iy)) {
        for(int ix = 0; ix < 6; ix++) {
            _playfield[iy][ix] = _playfield[iy-1][ix];
        }
    }
 }

这里的问题是上面的行也可能被清除(例如,如果iy == 8,那么我不想复制第 7 行而是第 6 行)并且我需要清除复制的行(iy-1 ) - 或复制该行上方的行,而该行又需要向上滴流。

我尝试计算我已经跳过了多少行,但这只有在我创建一个新数组然后将它们换出时才有效,但我无法获得就地修改运动场数组的数学工作。

这可能真的很简单,但我只是没有看到算法。有没有人知道我该怎么做?

【问题讨论】:

  • 它的性能不会那么好,但是您是否考虑过扫描您的比赛场地以查找 single 已删除的线,将其删除,并相应地调整其余部分,然后重复此算法直到没有删除线?这种“扫描”算法通常用于解构依赖图——虽然效率低下,但它们确实很容易推理并且可能适合这里的账单。另一方面,它们可能效率不高。
  • @KirkWoll 我想了想,虽然没有测试它似乎效率低下,所以我可能会再试一次。即使在 1 MHz 6502 处理器(目标)上,也应该相当快。虽然感觉有点太蛮力了,但话又说回来,有时蛮力正是需要的。
  • 我认为通常的代码是“从下到上,复制所有应该保留的行,用空行填充其余行” - 只需记住 2 个索引 - 要写入的当前行和当前行读取(读取> =写入)...此外,您可能需要删除一行/重绘,具体取决于您要显示的内容。
  • @MichaelStum 我建议使用List&lt;TetrominoType[]&gt;,这样当您删除一行时,您只需执行List.Remove()
  • @MatthewPigram 事实上,如果 C# 是最终的语言,我会使用它。我用它作为原型/概念证明来理解算法,最终程序将在 4k RAM 系统上用 6502 汇编编写,因此我尽可能保持简单:)

标签: c# .net arrays algorithm tetris


【解决方案1】:

主要问题是多条已清除的行,因此我们需要确保当您连续有多个已清除的行时,将所有内容都向下移动。由于您有一个需要清除哪些行的列表,而不是仅仅复制它上面的行,您可以找到下一个未清除的行有多远,并将其上方的所有内容向下移动这么多行。例如,您可以这样做:

for(int iy = 9; iy >= 0; iy--)
{
    if(_linesToClear.Contains(iy))
    {
        int nextLineIndex = iy-1;
        while( _linesToClear.contains(nextLineIndex) && nextLineIndex >= 0 )
        {
            nextLineIndex--;
        }
        if ( nextLineIndex >= 0 )
        {
            int amountToDrop = iy - nextLineIndex
            for(int ix = 0; ix < 6; ix++)
            {
                _playfield[iy][ix] = _playfield[iy-amountToDrop][ix];
            }
        }
    }
 }

这将计算出一行中有多少已清除的行,然后将所有内容放到这么多行中。我希望这会有所帮助!

【讨论】:

    【解决方案2】:

    它会起作用吗?

    int k = 0;
    for(int iy = 9; iy >= 0; iy--) {
        if(!_linesToClear.Contains(iy)) {
            for(int ix = 0; ix < 6; ix++) {
                _playfield[iy + k][ix] = _playfield[iy][ix];
            }
        }
        else
            k++;
    }
    

    【讨论】:

    • 这样做的问题是它不处理多行,并且可能会重复清除行上方的行。这就是给我带来麻烦的事情。
    • @MichaelStum 循环创建“底部”。在那之后,你不能只清理“顶部”,9 - k 行吗?
    • 宾果游戏!甚至不需要清理它,k 用于保持跳过行的运行计数的方式是完美的。非常感谢!
    猜你喜欢
    • 2021-07-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多