0

I’m trying to make a C# application using Visual Studio 2008, that solves Sudoku puzzles. My problem is that I can’t seem to figure the code for generating a new puzzle by the application. The idea that I’m trying to implement is as follows:

  1. Start with an empty puzzle
  2. Generate numbers for all cells in the puzzle
  3. Empty the appropriate number of cells, based on required level of difficulty
  4. Solve the puzzle
  5. Is the score for the puzzle in the acceptable range for the required level of difficulty?

6a. If NO -> Regenerate puzzle (go to 1.)

6b. If YES -> Puzzle generated (display it)

Used code for “New game” button:

    //--------------------------------------------------
    // Starting a new game menu button
    //--------------------------------------------------
    public void NewToolStripMenuItem_Click(System.Object sender, System.EventArgs e)
    {

        if (GameStarted) // this cheking part seems to work (message is displayed and game gets saved if selected)
        {
            MsgBoxResult response = (MsgBoxResult)(MessageBox.Show("Doriți salvarea jocului curent?", "Salvează jocul curent...", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question));

            if (response == MsgBoxResult.Yes)
            {
                SaveGameToDisk(false);
            }
            else if (response == MsgBoxResult.Cancel)
            {
                return;
            }
        }

        // Changing the cursor while generating
        System.Windows.Forms.Cursor.Current = Cursors.WaitCursor;
        ToolStripStatusLabel1.Text = "Se generează un puzzle nou...";

        // Creating an instance for the SudokuPuzzle class
        SudokuPuzzle sp = new SudokuPuzzle();
        string puzzle = string.Empty;

        // Determining the difficulty level selected (from menu objects)
        if (EasyToolStripMenuItem.Checked)
        {
            puzzle = sp.GetPuzzle(1);
        }
        else if (MediumToolStripMenuItem.Checked)
        {
            puzzle = sp.GetPuzzle(2);
        }
        else if (DifficultToolStripMenuItem.Checked)
        {
            puzzle = sp.GetPuzzle(3);
        }
        else if (ExtremelyDifficultToolStripMenuItem.Checked)
        {
            puzzle = sp.GetPuzzle(4);
        }
        else if (EmptyPuzzleToolStripMenuItem.Checked)
        {
            puzzle = sp.GetPuzzle(5);
        }

        // Changing to default cursor
        System.Windows.Forms.Cursor.Current = Cursors.Default;

        StartNewGame();

        // Initialisation of the grid
        int counter = 0;
        for (int row = 1; row <= 9; row++)
        {
            for (int col = 1; col <= 9; col++)
            {
                if (puzzle[counter].ToString() != "0")
                {
                    SetCell(col, row, System.Convert.ToInt32(puzzle[counter].ToString()), (short)0);
                }
                counter++;
            }
        }
    }

So the code for the next function called, GetPuzzle(1) :

    //--------------------------------------------------
    // Obtaining a new puzzle (of the required level)
    //--------------------------------------------------
    public string GetPuzzle(int level)
    {
        int score = 0;
        string result;
        do
        {
            result = GenerateNewPuzzle(level, ref score);
            if (result != string.Empty)
            {
                // Verify if the generated puzzle is of the selected dificulty
                switch (level)
                {
                    // The average for dificutly 1
                    case 1:
                        if (score >= 42 && score <= 46)
                        {
                            goto endOfDoLoop;
                        }
                        break;
                    // The average for dificutly 2
                    case 2:
                        if (score >= 49 && score <= 53)
                        {
                            goto endOfDoLoop;
                        }
                        break;
                    // The average for dificutly 3                                   case 3:
                        if (score >= 56 && score <= 60)
                        {
                            goto endOfDoLoop;
                        }
                        break;
                    // The average for dificutly 4
                    case 4:
                        if (score >= 112 && score <= 116)
                        {
                            goto endOfDoLoop;
                        }
                        break;
                }
            }
        } while (!false); // loops ending 
    endOfDoLoop:
        return result;
    }

Next function used is GenerateNewPuzzle():

    //--------------------------------------------------
    // Generating a new puzzle
    //--------------------------------------------------
    public string GenerateNewPuzzle(int level, ref int score)
    {
        int c;
        int r;
        string str;
        int numberofemptycells = 0;

        // Initializing the entire grid
        for (r = 1; r <= 9; r++)
        {
            for (c = 1; c <= 9; c++)
            {
                actual[c, r] = 0;
                possible[c, r] = string.Empty;
            }
        }

        // Empty the stacks used
        ActualStack.Clear();
        PossibleStack.Clear();

        // Complete by solving an empty grid
        try
        {
            // First used logical methods to solve the grid
            if (!SolvePuzzle())
            {
                // Then use brute force
                SolvePuzzleByBruteForce();
            }
        }
        catch (Exception)
        {
            // If there’s any error, return emptry string
            return string.Empty;
        }

        // Create a copy for the actual array
        actual_backup = (int[,])(actual.Clone());

        // Set the number of empty cells based on the difficulty  level
        switch (level)
        {
            // For difficulty level 1
            case 1:
                numberofemptycells = RandomNumber(40, 45);
                break;
            // For difficulty level 2
            case 2:
                numberofemptycells = RandomNumber(46, 49);
                break;
            // For difficulty level 3
            case 3:
                numberofemptycells = RandomNumber(50, 53);
                break;
            // For difficulty level 4
            case 4:
                numberofemptycells = RandomNumber(54, 58);
                break;
        }

        // Empty the stacks used by brute force
        ActualStack.Clear();
        PossibleStack.Clear();
        BruteForceStop = false;

        // Create empty cells
        CreateEmptyCells(numberofemptycells);

        // Convert the values from the actual array to string
        str = string.Empty;
        for (r = 1; r <= 9; r++)
        {
            for (c = 1; c <= 9; c++)
            {
                str += (string)(actual[c, r].ToString());
            }
        }

        // Verrify that the puzzle has only one solution
        int tries = 0;
        do
        {
            totalscore = 0;
            try
            {
                if (!SolvePuzzle())
                {
                    // If puzzle is not solved and difficulty level is 1-3
                    if (level < 4)
                    {
                        // Choose another combination of cells to empty
                        VacateAnotherPairOfCells(ref str);
                        tries++;
                    }
                    else
                    {
                        // Puzzles of difficulty 4 don’t guranty a single solution
                        SolvePuzzleByBruteForce();
                        goto endOfDoLoop;
                    }
                }
                else
                {
                    // The puzzle has 1 solution
                    goto endOfDoLoop;
                }
            }
            catch (Exception)
            {
                return string.Empty;
            }

            // If too many tries are executed, exit at 50
            if (tries > 50)
            {
                return string.Empty;
            }
        }
        while (true);
    endOfDoLoop:

        // Return the obtained score and the puzzle as a string
        score = totalscore;
        return str;
    }

And last useful (I think) function, VacateAnotherPairOfCells():

    //--------------------------------------------------
    // Empty another pair of cells
    //--------------------------------------------------
    private void VacateAnotherPairOfCells(ref string str)
    {
        int c;
        int r;

        // Search for a pair of cells to empty (the empty cells should be simetrical from the center of the grid)
        do
        {
            c = RandomNumber(1, 9);
            r = RandomNumber(1, 9);
        } while (!(int.Parse(str[(c - 1) + (r - 1) * 9].ToString()) == 0));

        // Restore the value of the cell from the backup array
        str = str.Remove(System.Convert.ToInt32((c - 1) + (r - 1) * 9), 1);
        str = str.Insert(System.Convert.ToInt32((c - 1) + (r - 1) * 9), (string)(actual_backup[c, r].ToString()));

        // Restore the value of the simetrical cell
        str = str.Remove(System.Convert.ToInt32((10 - c - 1) + (10 - r - 1) * 9), 1);
        str = str.Insert(System.Convert.ToInt32((10 - c - 1) + (10 - r - 1) * 9), (string)(actual_backup[10 - c, 10 - r].ToString()));

        // Search for another pair of cells that can be emptyed
        do
        {
            c = RandomNumber(1, 9);
            r = RandomNumber(1, 9);
        } while (!(int.Parse(str[(c - 1) + (r - 1) * 9].ToString()) != 0));

        // Delete the cell from the string
        str = str.Remove(System.Convert.ToInt32((c - 1) + (r - 1) * 9), 1);
        str = str.Insert(System.Convert.ToInt32((c - 1) + (r - 1) * 9), "0");

        // Delete the simetrical cell from the string
        str = str.Remove(System.Convert.ToInt32((10 - c - 1) + (10 - r - 1) * 9), 1);
        str = str.Insert(System.Convert.ToInt32((10 - c - 1) + (10 - r - 1) * 9), "0");

        // Reinitilisation of the grid
        short counter = (short)0;
        for (int row = 1; row <= 9; row++)
        {
            for (int col = 1; col <= 9; col++)
            {
                if (System.Convert.ToInt32(str[counter].ToString()) != 0)
                {
                    actual[col, row] = System.Convert.ToInt32(str[counter].ToString());
                    possible[col, row] = (string)(str[counter].ToString());
                }
                else
                {
                    actual[col, row] = 0;
                    possible[col, row] = string.Empty;
                }
                counter++;
            }
        }
    }
} }

The rest of the functions and code I didn't think to be necessary as everything else works. If I import an empty or partial puzzle the application can automatically solve it using the same methods used for solving the automatically generated grids. But when I click "New puzzle" from the menu, the application gets stuck with no error (so I have to kill the process).

Maybe this isn't the easiest method to generate a valid board, and I apologize for the length of the code, but I really need to fix and use this one. I tried to fix this myself, many times, but in the last 2 months I came to no solution (just a lot of frustration from my incompetence in this matter)… So I would appreciate any kind of help that I could get from here.

tshepang
  • 12,111
  • 21
  • 91
  • 136
Gab
  • 19
  • 1
  • 1
  • 1
    Isn't there a ton of literature on generating Sudokus? – Patashu Jun 04 '13 at 22:25
  • Yes, that's how I solved the other problems with this application that I need to do... but this is the 1st problem that I had and haven't fixed it yet in 2 months or so (after checking many forums and books). Problem is, I can't use another way, I need to fix this one... :( – Gab Jun 04 '13 at 22:28
  • The answers on [this question](http://stackoverflow.com/questions/9295537/sudoku-generator-algorithm) helped me out a lot when I made a similar program. I would encourage reading up a lot on sudoku generation algorithms, it is a more complex problem than it seems. – Wilson Jun 04 '13 at 22:29
  • 6
    OMG... please don't use `goto` in C#... create the proper flow control structures, but please for the sake of mankind.. don't use `goto`... – Federico Berasategui Jun 04 '13 at 22:30
  • @Gab I'd also recommend debugging to figure out where your program is getting stuck/slowing down. There is a reason it isn't finishing, and you can likely figure it out with some breakpoints. – Wilson Jun 04 '13 at 22:34
  • -1 This is too vague a question, and seems like a homework problem. – Mark Lakata Jun 04 '13 at 23:06

1 Answers1

0

Problem solved!

There is actually no problem with the code above, my problem was given to the fact that I had some coding mistake in "SolvePuzzleByBruteForce()" function. The function in question was not posted here, so the code above needs no correction and it functions properly.

I can put the "SolvePuzzleByBruteForce()" if anyone wants to, but I find it irrelevant given the fact that it's nothing special and the internet is full of such functions anyway.

To reply to the comments: @HighCore the use of goto is not relevant to my question.

@Mark Lakata the question was not vague. I stated the problem, the thing it needed to do, and the code used. And it wasn't homework problem as I'm not in school, nor would I try 2 months to make a homework.

@Wilson thank you for your comments, it helped me trace down and figure out the problem in the bad function.

tshepang
  • 12,111
  • 21
  • 91
  • 136
Gab
  • 19
  • 1
  • 1