代码之家  ›  专栏  ›  技术社区  ›  EpicAshman

数独棋盘程序中同一列和同一行出现两次的数字

  •  1
  • EpicAshman  · 技术社区  · 10 月前

    这段代码让我头疼,总结一下我的程序的目标是生成完整的数独棋盘,可以在未来用于游戏。我快到了,在较小的网格中没有重复的数字,但在较大的网格中仍然有重复的数字。

    显示我的问题的程序截图: Sudoku board with repeating numbers in rows and columns

    我的代码前言:

    • gameGrid是一个由9个数组组成的二维数组,每个数组包含9个索引
    • btnGrid是gameGrid的镜像,但用于gui中的按钮
    • gridXX是指数独板上每个较小的3x3网格

    其余代码在函数中以编程方式解释,我认为问题出在标记有注释的“checkCol”和“checkRow”部分。

    非常感谢任何帮助,并提前感谢任何评论者。

    private void generateBtn_Click(object sender, EventArgs e){
    Random r = new Random();
    //Row and Col max is one less as arrays start at 0
    int rowMax = 9;
    int colMax = 9;
    
    for (int i = 0; i < rowMax; i++)
    {
        for (int j = 0; j < colMax; j++)
        {
            bool checking = false;
            bool foundSame = false;
            while (checking == false)
            {
                foundSame = false;
                string num = r.Next(1, 10).ToString();
                Debug.WriteLine(num);
                //check row
                for (int k = 0; k < rowMax; k++) {
                    if (gameGrid[i,k] == num) {  foundSame = true; break; }
                    if (gameGrid[i,k] == "") { break; }
                }
                //check col
                for (int k = 0; k < colMax; k++)
                {
                    if (gameGrid[k,j] == num) { foundSame = true; break; }
                }
                //check square
                if (grid00.Contains(btnGrid[i, j])) { foundSame = checkIfContains(grid00, num); }
                if (grid10.Contains(btnGrid[i, j])) { foundSame = checkIfContains(grid10, num); }
                if (grid20.Contains(btnGrid[i, j])) { foundSame = checkIfContains(grid20, num); }
    
                if (grid01.Contains(btnGrid[i, j])) { foundSame = checkIfContains(grid01, num); }
                if (grid11.Contains(btnGrid[i, j])) { foundSame = checkIfContains(grid11, num); }
                if (grid21.Contains(btnGrid[i, j])) { foundSame = checkIfContains(grid21, num); }
    
                if (grid02.Contains(btnGrid[i, j])) { foundSame = checkIfContains(grid02, num); }
                if (grid12.Contains(btnGrid[i, j])) { foundSame = checkIfContains(grid12, num); }
                if (grid22.Contains(btnGrid[i, j])) { foundSame = checkIfContains(grid22, num); }
    
    
                if (foundSame == false)
                {
                    checking = true;
                    gameGrid[i,j] = num;
                    btnGrid[i,j].Text = num;
                    
                }
            }
            
            
        }
    }}
    
    2 回复  |  直到 10 月前
        1
  •  2
  •   Enigmativity    10 月前

    这是一种生成完整电路板的方法。

    var possibles = new List<int>[9, 9];
    var board = new int[9, 9];
    for (var i = 0; i < 9; i++)
        for (var j = 0; j < 9; j++)
        {
            possibles[i, j] = Enumerable.Range(1, 9).ToList();
            board[i, j] = 0;
        }
    
    var query =
        from i in Enumerable.Range(0, 9)
        from j in Enumerable.Range(0, 9)
        where possibles[i, j].Any()
        select new { i, j, };
    
    var random = new Random();
    for (var n = 0; n < 81; n++)
    {
        var least = query.MinByWithTies(x => possibles[x.i, x.j].Count).OrderBy(x => random.Next()).First();
    
        var selected = possibles[least.i, least.j][random.Next(possibles[least.i, least.j].Count)];
    
        possibles[least.i, least.j] = new List<int>();
        board[least.i, least.j] = selected;
    
        var ii = least.i / 3 * 3;
        var jj = least.j / 3 * 3;
    
        for (var k = 0; k < 9; k++)
        {
            possibles[least.i, k] = possibles[least.i, k].Except(new[] { selected }).ToList();
            possibles[k, least.j] = possibles[k, least.j].Except(new[] { selected }).ToList();
            possibles[ii + k / 3, jj + k % 3] = possibles[ii + k / 3, jj + k % 3].Except(new[] { selected }).ToList();
        }
    }
    

    它始于一系列可能性和董事会。它找到所有选项最少的单元格,并随机选择一个。然后,它填充该单元格,并从与该单元格相关的行、列和块的所有可能性中删除该数字。

    它只是重复了81次。

    唯一的戏剧性是,它确实在某些时候失败了,但它的成功往往足以在例外情况下重复整个过程。这个 .First() 失败在 var least 线。

    以下是一个有效网格的输出示例:

    grid

    生成网格只需要不到半秒的时间。

        2
  •  1
  •   John Omielan    10 月前

    主要问题显然不是“checkCol”和“checkRow”部分。相反,这是代码所做的 检查 foundSame 仍在 false 之后2 for 循环 之前 它检查适当的较小网格。因此,例如,如果 found相同 true 由于在较大的网格中发现了一个重复项,但在相应的较小网格中找不到,那么 found相同 设置为 错误的 ,因此该值被设置在那里,即使它在较大网格的行或列中重复了一个值。这就是为什么数字可以在较大网格中重复,但不能在任何较小的网格中重复(注意我假设 checkIfContains 未显示的功能正常工作)。

    一个相对简单易行的解决方法是添加一个检查 found相同 未在内部设置 if 每个较小网格的语句。因此,对于第一个,代码行应该是这样的

    if (!foundSame && grid00.Contains(btnGrid[i, j])) { foundSame = checkIfContains(grid00, num); }
    

    我还没有仔细考虑过这是否会发生,但我相信一个潜在的问题是,可能会出现无限循环。如果 没有人 1到9之间的值可以满足特定单元格的要求,即每个值在较大网格的行或列中,或在较小网格内都是重复的。为了避免这种情况的发生,一种方法是使用一个9的数组 bool 值,其中每个值设置为 错误的 最初只是之前 while (checking == false) 在该循环结束时,如果 found相同 真的 ,设置对应的数组值 num 真的 此外,如果所有数组值都是 真的 那么 值会起作用,因此代码需要通过抛出异常(尽管您可能希望首先以某种方式记录这一点)来打破循环,然后清除电路板并重试。


    最后,正如在 Nguyen Manh Cuong's comment ,你的线 if (gameGrid[i,k] == "") { break; } 在代码中检查行可能存在问题。但是,由于您还没有显示初始化代码,因此这是不确定的。如果从来没有空白单元格,那么此行什么也不做。另一方面,如果存在空白单元格 之前 如果行中包含重复单元格,则代码将脱离循环 没有 设置 found相同 成为 真的 .

    不管怎样,我认为在那里放那行没有任何有效的目的,所以它很可能应该被删除,或者至少被注释掉。