首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C# MineSweeper项目:有没有更简单的方法来显示邻近的地雷?

C# MineSweeper项目:有没有更简单的方法来显示邻近的地雷?
EN

Stack Overflow用户
提问于 2016-10-10 17:10:53
回答 1查看 1.4K关注 0票数 1

我做了一个扫雷WPF项目,作为研究面向对象编程的实践。然而,我的方法显示多少地雷按钮“看”是有点长和混乱。当前的网格是6x6,正如您所看到的,它是一长串行。主要的问题是,如果我想将网格扩展到100x100,它将需要更多的行。

我在想,是否有什么办法可以使它变得更干净或更短呢?

这是显示一个按钮看到多少枚地雷的方法:

代码语言:javascript
复制
 private int MineInfo(int index)
        //shows how many mines one button can "see".
        {
           // n = mines
            int n = 0;

         // Edges:
            if (index == 0)
            {
                if (mines.Contains(index + 1))
                {
                    n++;
                }
                if (mines.Contains(index + 6))
                {
                    n++;
                }
                if (mines.Contains(index + 7))
                {
                    n++;
                }
            }
            if (index == 5)
            {
                if (mines.Contains(index - 1))
                {
                    n++;
                }
                if (mines.Contains(index + 6))
                {
                    n++;
                }
                if (mines.Contains(index + 5))
                {
                    n++;
                }
            }
            if (index == 30)
            {
                if (mines.Contains(index + 1))
                {
                    n++;
                }
                if (mines.Contains(index - 6))
                {
                    n++;
                }
                if (mines.Contains(index - 5))
                {
                    n++;
                }
            }
            if (index == 35)
            {
                if (mines.Contains(index - 1))
                {
                    n++;
                }
                if (mines.Contains(index - 6))
                {
                    n++;
                }
                if (mines.Contains(index - 7))
                {
                    n++;
                }
            }

         // Top Row
            if (index > 0 && index < 5)
            {
                if (mines.Contains(index - 1))
                {
                    n++;
                }
                if (mines.Contains(index + 1))
                {
                    n++;
                }
                if (mines.Contains(index + 6))
                {
                    n++;
                }
                if (mines.Contains(index + 5))
                {
                    n++;
                }
                if (mines.Contains(index + 7))
                {
                    n++;
                }
            }

            // Bottom row
            if (index > 30 && index < 35)
            {
                if (mines.Contains(index - 1))
                {
                    n++;
                }
                if (mines.Contains(index + 1))
                {
                    n++;
                }
                if (mines.Contains(index - 6))
                {
                    n++;
                }
                if (mines.Contains(index - 5))
                {
                    n++;
                }
                if (mines.Contains(index - 7))
                {
                    n++;
                }
            }

           // left side
            if ((index == 6) || (index == 12) || (index == 18) || (index == 24))
            {
                if (mines.Contains(index - 6))
                {
                    n++;
                }
                if (mines.Contains(index + 6))
                {
                    n++;
                }
                if (mines.Contains(index + 1))
                {
                    n++;
                }
                if (mines.Contains(index - 5))
                {
                    n++;
                }
                if (mines.Contains(index + 7))
                {
                    n++;
                }
            }

            // Right side
            if ((index == 11) || (index == 17) || (index == 23) || (index == 29))
            {
                if (mines.Contains(index - 6))
                {
                    n++;
                }
                if (mines.Contains(index + 6))
                {
                    n++;
                }
                if (mines.Contains(index - 1))
                {
                   n++;
                }
                if (mines.Contains(index - 7))
                {
                    n++;
                }
                if (mines.Contains(index + 5))
                {
                    n++;
                }
            }


            // Middle buttons

            if ((index > 6 && index < 11) || (index > 12 && index < 17) || (index > 18 && index < 23) || (index > 24 && index < 29))
            {
                if (mines.Contains(index - 1))
                {
                    n++;
                }
                if (mines.Contains(index + 1))
                {
                    n++;
                }
                if (mines.Contains(index - 6))
                {
                    n++;
                }
                if (mines.Contains(index + 6))
                {
                    n++;
                }
                if (mines.Contains(index - 7)) 
                {
                    n++;
                }
                if (mines.Contains(index + 7)) 
                {
                    n++;
                }
                if (mines.Contains(index - 5)) // Right top
                {
                    n++;
                }
                if (mines.Contains(index + 5)) // right bottom
                {
                    n++;
                }
            }

            return n;
        }
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-10-10 18:15:00

这就是循环的目的,在所有编程语言、OOP和其他语言中都有这样的特性。

当您遇到问题时,您可以按照“我有一个类似于N种情况的算法”来表达,您通常是在谈论一个循环。在本例中,您有几个概括:

  1. 每个相邻的小区都被同等对待。也就是说,如果在您的mines集合中找到它的地址,则将您的总数增加1。
  2. 您有相同的相邻单元格模式要检查,而不管您从哪个特定的索引开始。

一个问题是,当你不在边缘的时候,上面的效果很好,但是当你在边缘的时候,你需要忽略一些你通常会找到一个相邻的细胞的地方。

让你的场景更难立即找到解决方案的部分原因是,你选择了将你的细胞定位为一个一维索引,即使实际的棋盘是二维的。通常情况下,最好让数据结构尽可能地与数据结构所要建模的内容相匹配。代码将更容易编写,特别是它将使您更容易获得关于如何解决特定问题的洞察力,因为您可以根据原始问题(例如,这里的二维搜索)来考虑这些问题,而不必在数据结构和原始问题之间进行思维映射。

随着时间的推移和实践,你可以更好地做这种心理映射,但即使对于一个非常有经验的程序员,最好避免这种情况。对于缺乏经验的程序员来说,这是一个非常好的时间来关注始终确保您的数据结构尽可能接近原始问题。

下面是一个与原始代码保持一致的解决方案。然而,它仍然内化了一个抽象,以便在您的一维数据结构和原始的二维问题空间之间进行转换。这在代码中引入了一点低效率,但与使算法更易于编写和理解相比,这是一个非常小的成本:

代码语言:javascript
复制
int MineInfo(int index, int rows, int columns)
{
    int centerRow = index / rows, centerColumn = index % columns,
        result = 0;

    for (int i = -1; i <= 1; i++)
        for (int j = -1; j <= 1; j++)
        {
            // Ignore the center cell
            if (i == 0 && j == 0)
            {
                continue;
            }

            int checkRow = centerRow + i, checkColumn = centerColumn + j;

            // Ignore cells not within the game board
            if (checkRow < 0 || checkRow >= rows ||
                checkColumn < 0 || checkColumn >= columns)
            {
                continue;
            }

            int checkIndex = checkRow * columns + checkColumn;

            if (mines.Contains(checkIndex))
            {
                result++;
            }
        }

    return result;
}

上面的逻辑将所有的逻辑封装到一个方法中。但是,即使在有令人信服的理由将数据存储在与原始问题空间不匹配的数据结构的情况下,在某些助手方法甚至包装类中抽象这种差异也是有用的。也就是说,在游戏板的一维索引和二维坐标之间的转换可以在MineInfo()方法和代码的类似区域可以使用的方法中实现。

我把这个练习留给读者。:)

当然,解决这个问题还有其他办法。如果我能改变任何事情,我要做的第一件事就是停止使用单维数据结构来存储数据。与其拥有一组索引,不如创建一个二维数组来跟踪单元格的状态,包括是否存在地雷、单元格是否已公开、标记等。然后实现就变得简单多了。

往更难的方向走,当然也有可能严格地在单维空间中做上述所有的事情。但是处理边缘情况变得更加复杂,用代码读写变得更加困难。IMHO,这不值得。:)

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39963397

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档