C#MineSweeper项目:任何更简单的方式来显示相邻的矿山?

时间:2016-10-10 17:10:54

标签: c# oop minesweeper

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

我想知道是否会有任何变通方法/方法让它变得更清洁或/和更短?

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

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

1 个答案:

答案 0 :(得分:1)

循环适用于所有编程语言中的功能,OOP和其他。

每当你遇到一个问题,你可以表达&#34;我有一个类似于N个案例的算法&#34;,你通常会谈论一个循环。在这个例子中,您有几个概括:

  1. 对每个相邻的细胞进行相同的处理。即如果在mines集合中找到了其地址,则会将总数增加一个。
  2. 无论您从哪个特定索引开始,都要检查相邻单元格的相同模式。
  3. 一个问题是当你不在边缘时,上面的效果很好,但是当你是你时,你需要忽略一些你通常会找到相邻单元格的地方。

    使您的场景更难以立即看到解决方案的一部分原因是您选择将您的单元格作为一维索引进行处理,即使实际游戏板是二维的。作为一般规则,最好使数据结构尽可能地与数据结构建模的内容进行匹配。代码将更容易编写,特别是它将使得更容易获得关于如何解决特定问题的见解,因为您可以根据原始问题来考虑它们(例如,这里,二维搜索)而不是必须在数据结构和原始问题之间进行心理映射。

    通过时间和练习,你可以更好地进行这种心理测绘,但即使对于经验丰富的程序员来说,最好还是避免这种情况。对于没有经验的程序员来说,现在是时候专注于始终确保您的数据结构尽可能接近原始问题。

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

    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()方法和代码的类似区域可以使用的方法实现。

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

    当然,还有其他方法可以解决这个问题。如果我可以改变任何东西,我要做的第一件事就是停止使用一维数据结构来存储数据。不要使用索引集合,而是创建一个跟踪单元状态的二维数组,包括是否存在地雷,单元格是否已被暴露,标记或其他任何内容。然后实现变得更加简单。

    更加艰难的是,当然也可以在一维空间中严格执行上述所有操作。但是处理边缘情况变得复杂得多,并且在代码中读取和写入要困难得多。恕我直言,它不值得。 :)