-3

I am trying to create a sudoku solver by first

  1. calculating the possible values that can be put in the square.
  2. Then using these constraints to backtrack
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Sudoku_Solver
{
    static class Program
    {


    private static int[,] grid = new int[,]{
            { 3, 0, 6, 5, 0, 8, 4, 0, 0 },
            { 5, 2, 0, 0, 0, 0, 0, 0, 0 },
            { 0, 8, 7, 0, 0, 0, 0, 3, 1 },
            { 0, 0, 3, 0, 1, 0, 0, 8, 0 },
            { 9, 0, 0, 8, 6, 3, 0, 0, 5 },
            { 0, 5, 0, 0, 9, 0, 0, 6, 0 },
            { 1, 3, 0, 0, 0, 0, 2, 5, 0 },
            { 0, 0, 0, 0, 0, 0, 0, 7, 4 },
            { 0, 0, 5, 2, 0, 6, 3, 0, 0 }
        };

    private static List<int>[,] constraints;
    static void Main(string[] args)
    {
        GetConstraints();

        SolveSudokuGrid();

        PrintGrid();
        Console.Read();

    }

    static bool SolveSudokuGrid()
    {
        int row = -1 , col = -1;

        if (IsGameOver(ref row, ref col) == true)
            return true;

        //Get constraint
        List<int> constraint = constraints[row, col];

        for (int i = 0; i < constraint.Count; i++)
        {
            //Assume correct number by adding a constraint
            grid[row, col] = constraint[i];

            if (SolveSudokuGrid() == true)
                return true;

            //Cant solve. Backtrack
            grid[row, col] = 0;
        }

        return false;
    }

    static void PrintGrid()
    {
        for (int row = 0; row < 9; row++)
        {
            for (int col = 0; col < 9; col++)
                    Console.Write(grid[row,col]);
            Console.WriteLine();
        }
    }
    static bool IsGameOver(ref int row, ref int col)
    {
        for(int r = 0; r < 9; r++)
        {
            for(int c = 0; c < 9; c++)
            {
                if (grid[r, c] == 0)
                {
                    row = r;
                    col = c;
                    return false;
                }
            }
        }

        return true;
    }
    static void GetConstraints()
    {
        constraints = new List<int>[9, 9];

        for(int row = 0; row < 9; row++)
        {
            for(int col = 0; col < 9; col++)
            {
                if(grid[row,col] == 0)
                {
                    constraints[row, col] = ComputeConstraint(row, col);
                    continue;
                }

                constraints[row, col] = null;
            }
        }
    }

    static List<int> ComputeConstraint(int row, int col)
    {
        List<int> constraint = new List<int>();

        for (int i = 1; i <= 9; i++)
            if (HasConflicts(row, col, i) == false)
                constraint.Add(i);

        return constraint;
    }
    static bool usedInRow(int row, int num)
    {
        /*
        * Scans through that "row" till a match is found.
        * So "row" will be the same, but "col" will keep changing as we scan
        */
        for (int col = 0; col < 9; col++)
        {
            if (grid[row,col] == num)
            {
                return true;
            }

        }
        return false;
    }

    static bool usedInCol(int col, int num)
    {
        /*
        * Scans through that "col" till a match is found.
        * So "col" will be the same, but "row" will keep changing as we scan
        */
        for (int row = 0; row < 9; row++)
        {
            if (grid[row,col] == num)
            {
                return true;
            }

        }
        return false;
    }

    static bool usedInBox(int boxStartRow, int boxStartCol, int num)
    {
        /*
        * Scans through the mini 3x3 box, looking for a duplicate number
        */
        for (int row = boxStartRow; row < boxStartRow + 3; row++)
        {
            for (int col = boxStartCol; col < boxStartCol + 3; col++)
            {
                if (grid[row,col] == num)
                    return true;
            }
        }
        return false;
    }

    static bool HasConflicts(int row, int col, int num)
    {
        bool isInRow, isInCol, isInBox, hasConflicts = false;

        isInRow = usedInRow(row, num);
        isInCol = usedInCol(col, num);


        int startRow = (row / 3) * 3;
        int startCol = (col / 3) * 3;
        isInBox = usedInBox(startRow, startCol, num);

        if (isInRow)
        {

            hasConflicts = true;
        }

        if (isInCol)
        {

            hasConflicts = true;
        }

        if (isInBox)
        {

            hasConflicts = true;
        }

        return hasConflicts;
    }
}
}
Biffen
  • 6,249
  • 6
  • 28
  • 36
RStyle
  • 875
  • 2
  • 10
  • 29
  • 4
    So what's the actual problem/question? All you did was say you had a problem and mentioned nothing about what it was. – Lunyx Aug 07 '15 at 21:01
  • You must check for duplicate numbers each time you assume a number is correct. before putting it you must check if the same value is added before or not. currently you dont check. i think thats the problem. `grid[row, col] = constraint[i];`. this may cause duplicates in same rows or columns. – M.kazem Akhgary Aug 07 '15 at 21:47

1 Answers1

1

You must recheck for duplicate numbers when you want to assume a number is correct. if the number is added before and exist in current column or row or box then go for the next constraint.

you get wrong answer because you dont recheck before putting numbers to table.

So you can simply fix it using your own methods again before putting numbers in table.

your for loop in SolveSudokuGridmethod would be like

        for (int i = 0; i < constraint.Count; i++)
        {
            //Assume correct number by adding a constraint
            // But lets do a check if we already added in current column or row or box.
            if (usedInRow(row, constraint[i])) continue;
            if(usedInCol(col, constraint[i])) continue;

            int startRow = (row / 3) * 3;
            int startCol = (col / 3) * 3;
            if (usedInBox(startRow, startCol, constraint[i])) continue;

            grid[row, col] = constraint[i];

            if (SolveSudokuGrid() == true)
                return true;

            //Cant solve. Backtrack
            grid[row, col] = 0;
        }

Not that this will not solve your current Sudoku because the given table is impossible to solve. so it will return the table with no change. you can also check if the table is solvable just by using the final result of SolveSudokuGrid:

    static void Main(string[] args)
    {
        GetConstraints();

        bool solvable = SolveSudokuGrid();

        if(!solvable) Console.WriteLine("Cannot solve!");
        else  PrintGrid();
        Console.Read();
    }

however sometimes you may fell into infinite loops because of wrong table and the design of algorithm(if you put duplicate numbers). but it should work fine if the table does not contain duplicate numbers it self.

Update: To speed up the algorithm instead of assigning numbers by order you have to assign numbers to elements that have less correct possibilities.

This will greatly increase the speed of the algorithm. also this is the usual way of solving Sudoku.

Your IsGameOver method would be like

    static bool IsGameOver(ref int row, ref int col)
    {
        for (int r = 0; r < 9; r++)
        {
            for (int c = 0; c < 9; c++)
            {
                if (grid[r, c] == 0)
                {
                    // set the row and col.
                    row = r;
                    col = c;

                    // but Lets check for the element with least possibilities in constraints.
                    // and prefer it against current row and col
                    int min = constraints[r, c].Count;
                    for (int i = 0; i < 9; i++)
                    {
                        for (int j = 0; j < 9; j++)
                        {
                            if (constraints[i, j] != null && // if the constraint is available
                                constraints[i, j].Count < min && // if found less possibilities
                                grid[i, j] == 0) // if the element of the table is 0 (its not assigned yet)
                            {
                                // set the row and col with less possibilities
                                row = i;
                                col = j;

                                min = constraints[i, j].Count;
                            }
                        }
                    }

                    return false;
                }
            }
        }

        return true;
    }
M.kazem Akhgary
  • 18,645
  • 8
  • 57
  • 118
  • Hi it worked! but it took 80 seconds to solve. Is that normal? My Prime Minister's code took 0.6 seconds to solve. https://docs.google.com/folderview?id=0B2G2LjIu7WbdfjhaUmVzc1lCR2hUdk5fZllCOHdtbFItbU5qYzdqZGVxdmlnRkJyYVQ4VU0 – RStyle Aug 08 '15 at 05:20
  • I dont know what algorithm he is using. also i didnt know how to solve Sudoku i mean i have never tried it before. i just looked at your code to understand the algorithm you have used. btw i think 80 second is normal to solve because there are lots of possibilities that will go wrong and values must be reassigned. there may be better algorithms to solve Sudoku. however you can optimize your code to reduce the time it takes. @RStyle – M.kazem Akhgary Aug 08 '15 at 08:01
  • one way i was thinking to speed it up is to start from the constraints that have less possibilities. also thats how you solve the Sudoku! @RStyle – M.kazem Akhgary Aug 08 '15 at 08:06
  • OMG. Took a total of 0.4262835 seconds to solve. Thanks so much LOL – RStyle Aug 08 '15 at 09:56
  • @RStyle glad it helped. however i updated the answer to show how start from elements with less possibilities. you may check it with the way you did your self. however 0.4 seconds seems fast enough ! – M.kazem Akhgary Aug 08 '15 at 12:37