首页 > 解决方案 > 删除二进制行

问题描述

如果我使用复制俄罗斯方块使用无符号长的位来表示和 8 x 8 的块网格,我可以做哪些按位操作来查找 1 的行(填充的行)并删除它们?

我可以使用循环找到一行并使用简单的位移删除最后一行,但不仅我的方法无效,位移仅适用于最后一行,因为在上面使用时它也会删除其他行。

标签: cbinarybit-manipulation

解决方案


假设您正在处理 8x8 位的单个 64 位无符号整数,让我们从使用正确的数据类型开始:uint64_t

现在,让我们采用“高于”位的约定,因为您没有具体说明这一点。我假设高位代表更高的行,因此删除一行将涉及向右移动位。

最后,我假设“底部”是第 0 行,而“顶部”是第 7 行。这意味着例如,您可以执行以下操作来获取与单行对应的位的掩码,如下所示:

uint64_t row_mask = (uint64_t)0xff << row * 8;

我相信您可以看到如何使用此掩码来检测所有掩码。您只需对您的状态进行按位与运算并检查结果是掩码本身。这是一个非常简单的方法:

int row = 0;
while (row < 8) {
    uint64_t row_mask = (uint64_t)0xff << row * 8;
    if ((state & row_mask) == row_mask) {
        remove_row(&state, row);
    } else {
        ++row;
    }
}

现在我们已经建立了如何处理行的实际规范,假设您现在要删除第 3 行。为此,您必须将第 3 行上方的所有行向右移动,而第 0-2 行保持不变。

请注意,您可以使用一种技巧来获取特定行下方每一行中的位的掩码,如下所示:

uint64_t rows_below_mask = ((uint64_t)1 << row * 8) - 1;

这是通过将 1 移到当前行的最后一位,然后减去 1 来实现的,这将清除该位并将所有位设置为右侧。

您可以使用相同的值来为当前行上方的所有行派生掩码。只需反转它,然后向左移动,以便我们忽略当前行:

uint64_t rows_above_mask = ~rows_below_mask << 8;

这几乎就是您所需要的......只需掩盖零件,移动和组合:

void remove_row(uint64_t *state, int row)
{
    uint64_t rows_below_mask = ((uint64_t)1 << row * 8) - 1;
    uint64_t rows_above_mask = ~rows_below_mask << 8;
    *state = (*state & rows_below_mask) | (*state & rows_above_mask) >> 8;
}

如果您希望将多个值链接在一起,假设您的俄罗斯方块游戏中有超过 8 行,则需要更多的逻辑来处理选择正确的行和携带数据。这是一个让你考虑自己做的练习。


推荐阅读