Solving Sudoku using fully connected networks

[Summary]

This article introduces the design and implementation of a solver that can solve Sudoku problems and has multiple functions. It can identify Sudoku questions by identifying the Sudoku grid in the picture, or randomly generate Sudoku questions; verify and prompt the Sudoku, and can display algorithm demonstrations of the solving process.

The solver has two ways of producing Sudoku puzzles. The first is random generation: pre-store a large number of Sudoku questions in the question bank, and then use a random function to randomly generate Sudoku questions. The second is image recognition: a fully connected neural network is used to establish a digital image recognition model. After the user clicks on image recognition to start the game, the program cuts the image into 81 small pieces, and normalizes and grayscales the divided pieces. After processing, preprocessing and extraction, it is then fed into a solver for solving.

To solve Sudoku puzzles, the solver uses an efficient algorithm based on backtracking. It systematically explores possible solutions by continuously assigning numbers to empty cells and backtracking when a contradiction is encountered. This solver is guaranteed to find the correct solution to a valid Sudoku problem.

For Sudoku verification and hints, the solver checks whether a given problem follows the Sudoku rules by verifying the uniqueness of numbers in each row, column and each 3x3 subgrid. This function can help users verify the correctness of manually entered or generated Sudoku questions.

In addition, the solver also provides an algorithm demonstration mode that shows a step-by-step demonstration of the backtracking problem-solving process. This mode helps users understand the backtracking algorithm and can serve as an educational tool for learning backtracking strategies.

The proposed solver integrates Sudoku solving, image recognition, question generation, verification, hints and algorithm demonstration, providing a multifunctional tool for Sudoku enthusiasts. The implementation demonstrates the practicality and effectiveness of the functionality, providing a comprehensive solution for Sudoku-related tasks.

[Key words]

Sudoku solver, image recognition, question generation, Sudoku verification, backtracking algorithm, algorithm demonstration.

Table of contents

1 Introduction

2. Creation of Sudoku page

3. The generation of  Sudoku questions

4. Implementation of Sudoku function

5. Backtracking algorithm operation demonstration


  • introduction

  Sudoku is a logical intelligence game that originated from the Latin Square studied by Swiss mathematician Euler and others. It is now popular all over the world. As the number of Sudoku enthusiasts increases, many variations of Sudoku have emerged, such as mini Sudoku and diagonal Sudoku, with countless types of rules.

  Sudoku is composed of squares, rows, columns, palaces and other elements. It divides a larger square into 9 rows and 9 columns, forming 81 squares, also called a nine-square grid. Each square is a small square, horizontally There are 9 squares where the three horizontal rows in the direction and the three columns in the vertical direction intersect, forming a small nine palaces, called palaces. The rules of the Sudoku game are: fill in the numbers 1 to 9 in the squares, and satisfy that each row can only be filled with numbers 1 to 9, and cannot be repeated; each column can only be filled with numbers 1 to 9, and cannot be repeated. ; Fill each 3×3 square with numbers from 1 to 9, and they cannot be repeated.

  • Creation of Sudoku page
  1. Code

Figure 1-1 tk page rendering code

  This code implements a graphical user interface (GUI) for a Tkinter-based Sudoku solver. The following is the implementation logic of the code:

        1. In the `__init__` method, initialize the initial disk `board` of the Sudoku solver and the root window `root` of Tkinter. Set the title and size of the window, and create frames for the input and output boxes.

        2. The `create_start` method is used to create an input box for the Sudoku board. Through nested loops, a 9x9 grid is created, with each cell corresponding to an input box. Set the input box's width, alignment, and padding, and place it in the input box frame .

        3. The `create_input_widgets` method is used to create labels and input boxes for inputting Sudoku. Similar to `create_start`, but whether to create an input box or a label is determined based on the value of the initial board `board`. If the value of the initial disk is 0, create an input box; otherwise, create a label to display the value of the initial disk.

        4. The `create_output_widgets` method is used to create labels and buttons for displaying status or results. The following components are created:

   - `status_label`: Displays the status of the Sudoku solver.

   - `next_button`: Start a new game.

   - `pic_button`: Start the game through picture recognition.

   - `solve_button`: View the solution of Sudoku.

   - `step_button`: prompts the next step of Sudoku.

   - `show_button`: shows the running process of the algorithm.

   - `verify_button`: Verify whether the Sudoku solution is correct.

  In the `SudokuSolverGUI` class, create various parts of the GUI interface by calling the `create_output_widgets` and `create_start` methods. At the same time, some methods for handling button click events are also defined, such as `next_game`, `pic_game`, `solve_sudoku`, etc.

  Through this GUI interface, users can enter Sudoku problems, view solutions, hints, and verify the correctness of the solutions.

Figure 1-2 Initialized tk interface   

  • Sudoku question generation
  1. Start game randomly

        1.1 Principle

  In order to realize the function of randomly generating Sudoku questions, this program uses the following steps. First, load the questions from the preset question bank file. The question bank file contains the representation of multiple Sudoku questions. Then, by generating a random index, a question is randomly selected from the question bank as the current Sudoku question. The selected topics are represented in the form of a two-dimensional list and serve as input to the program.

        1.2 Code implementation

Figure 1-3 Randomly generated code 

  This code contains a main function `main` and an auxiliary function `load_puzzles`. Here is an explanation of these two parts of code:

1. `load_puzzles` function:

   - This function is used to load questions from the question bank file. The question bank file is named "question bank". The function opens the file using the `open` function and reads all lines using the `readlines` method.

   - Then, use the `strip` method to remove leading and trailing whitespace characters from each line, and add the question string to a list called `puzzles`.

   - Finally, return the `puzzles` list containing all questions.

2. `main` function:

   - First, call the `load_puzzles` function to load the questions and store them in the `puzzles` list.

   - Then, use the `random.randint` function to randomly select an index, which is used to select a question from the `puzzles` list.

   - The selected question is obtained through the index `random_index` and stored in the `selected_puzzle` variable.

   - `selected_puzzle` is a Sudoku puzzle represented by a string.

   - Next, use the `eval` function to convert `selected_puzzle` into a Sudoku board in the form of a two-dimensional list, and store it in the `board` variable.

   - Finally, create a `SudokuSolverGUI` instance named `gui` and call the `run` method to run the interface program.

Figure 1-4 Question bank 

  Figure 1-5 tk interface after rendering is completed 

2. Start the game with picture recognition

  In order to realize the function of randomly generating Sudoku questions, this program uses the following steps.

2.1 Training the digital recognition model: 

        2.1.1 Looking for a framework for training models
After a lot of comparative analysis, I finally chose the PyTorch framework. The reasons are as follows: First, PyTorch provides a simple and intuitive API, making it easier to define, train and evaluate deep learning models. Compared with other deep learning frameworks, PyTorch's code is more readable, closer to Python's programming style, and lowers the entry barrier; secondly, PyTorch uses a dynamic graph mechanism, which means that it can be dynamically defined and modified at runtime. and debugging calculation graphs. This flexibility makes building and debugging models more intuitive and flexible.

        2.1.2 Finding a suitable data set
Training the model is inseparable from the preparation of the preliminary data set. After repeated comparisons and weighings, I finally chose to use the MNIST data set (handwritten digit data set). The reasons are as follows: First, mnist is an open public data set with a large sample size (60,000 training samples and 10,000 test samples, the pixel size of each sample is 28*28); second, the samples of the mnist data set They are handwritten digits. Compared with written digits, handwritten digits are very difficult to recognize, and feature values ​​are more difficult to extract. If the model can recognize handwritten digits, then written digits must also be accurately recognized.
         2.1.3 Training model

  A simple multi-layer perceptron (MLP) model is implemented with PyTorch for MNIST handwritten digit recognition.

1. Model definition: A multi-layer perceptron model (Net) containing three linear layers (fully connected layers) is defined. Each linear layer is followed by an activation function (ReLU), and the output of the last linear layer is used as the output of the model.

2. Model training: The loss function (cross-entropy loss function) and optimizer (stochastic gradient descent method) are defined, and the model parameters are updated through iterative training. During the training process, the training data is input into the model for forward propagation, the loss value is calculated, and then back propagation is performed and the model parameters are updated.

3. Model evaluation: Use the test set to evaluate the trained model, and calculate the average loss and accuracy of the model on the test set. At the same time, some test set samples are visually displayed to show the prediction results of the model.

  When I train 200 times, the accuracy of the model is around 0.95. I wanted to improve the accuracy again, and then adjusted the number of model training times to 300 times, but after the adjustment, the accuracy of the model was only 0.93. I decided to use the model for the time being. If the recognition could not be accurately performed later, I would adjust the parameters of the model to improve its accuracy (later it turned out that the accuracy of the model could meet the requirements of this program for identifying numbers).

2.2 Image processing:
        2.2.1 Image cutting and conversion

      - Use the OpenCV library (`cv2`) to read the input image.

      - Get the width and height of the image.

      - Calculate the width and height of each small grid based on the number of Sudoku grids.

      - Loop through each small grid, and convert the small grid image into a 28x28 pixel grayscale image through operations such as cropping, grayscale processing, and threshold processing (because the data in the mnist data set is 28*28, with white text and black background, so The original image is processed in this step).

   - Save the image of each small grid to the specified folder (grid_images).

Figure 1-5 Image processing code

Figure 1-6 Processed file 

        2.2.2 Image recognition and Sudoku generation:

   - Define a function (`recognize_handwriting`) that recognizes handwritten digit images and returns the predicted digit label.

   - Define a function (`is_black_background`) to determine whether the image has a black background.

   - Define a function (`get_sudoku_array`) to obtain the array representation of Sudoku from the divided small grid image.

   - Define a function (`run_game`) for processing pictures, calling recognition functions and generating Sudoku arrays.

   - When solving Sudoku, the input boxes and labels on the interface are updated based on the running status of the Sudoku solving algorithm.

 Figure 1-4 Original read Sudoku picture

 Figure 1-5 tk interface after recognition 

  • Sudoku function implementation


4.1 Use the backtracking algorithm to find the solution to Sudoku
        4.1.1 Principle
     - Define the legality check function: Write a function to check whether the given Sudoku state is legal. This function needs to check every row, every column and every 3x3 subgrid for duplicate numbers. If there are duplicates, it means that the current status is illegal.

 - Find a blank position: Starting from the upper left corner, in row-major order, find the first blank position in the Sudoku grid (that is, the position with a value of 0). If there are no empty positions, a solution has been found. 3. Try to fill in the numbers: choose a number from 1 to 9 and fill in the current blank position one by one. Then, check whether the populated state is legal. If it is legal, continue to the next step; if it is not legal, try to choose the next number.

 - Recursive backtracking: After filling a number at the current position, call the backtracking function recursively, move to the next empty position, and continue trying to fill in the number. If an illegal state is found during the subsequent filling process, then backtrack to the previous empty position and try to select the next number.

 - Solution found or backtracking completed: Repeat steps 3 and 4 until a legal solution is found, or backtracking returns to the first blank position and all numbers have been tried. In the process of finding the solution, if an illegal state is encountered, it will backtrack to the previous blank position.

        4.1.2Code _ _

1. A `checkBoardX` function is defined to check whether the current number in the Sudoku board complies with the Sudoku rules. This function checks whether there are duplicates of the current number in the row, column, and 3x3 small square.

2. Define a `helper` function for recursively filling the spaces in the Sudoku board. This function accepts a parameter `index`, which represents the index of the current space to be filled. The function first determines whether the current space has been filled with numbers. If it is a space, it tries to fill in the numbers 1-9 and uses the `checkBoardX` function to verify. If the validation passes, the `helper` function is called recursively to fill in the next space. If validation fails, backtrack to the previous space and try the next number.

3. An empty list `res` is defined to store all solutions. Call the `helper` function to start solving Sudoku problems.

4. A Sudoku board `board` is defined, where 0 represents a space.

Generally speaking, the solution of Sudoku is implemented using a backtracking algorithm, by checking whether each number in the Sudoku board conforms to the Sudoku rules, recursively filling in the spaces, and finding all solutions. 4.1.3 Implementation of
        View Answer Button

When the "View Answer" button is clicked, the call to the `solve_sudoku` method is triggered. This method will call the `update_output_widgets` method to update the part of the interface that displays the answer.

In the `update_output_widgets` method, first check if a solution exists (`self.solution`). If a solution exists, the solution list is traversed, then each cell is traversed, and the solution value is inserted into the corresponding input box. This is achieved by using the `input_frame.grid_slaves` method to obtain the input box reference at the corresponding position, and then using the `widget.insert` method to insert the answer value.

If no solution is found, a "No solution found" label will be displayed on the interface.

In short, when the "View Answer" button is clicked, the click event of the button will trigger the operation of updating the interface and display the solution on the Sudoku panel.

        4.1.4  Implementation of prompt button
    When the "prompt" button is clicked, the call to the `step_solution` method is triggered. This method will display the solution to the Sudoku step by step, with each click of the button showing the solution to the next blank cell.

 

In the `step_solution` method, first check whether a solution exists (`self.solution`). If a solution exists, the list of solutions is traversed, then each cell. In each loop, it will check whether the current cell is blank (by judging whether the corresponding input box is empty). If the current cell is blank, the answer value is inserted into the input box of the cell and returned immediately. In this way, each time you click the "Hint" button, the answer to the next blank cell will be displayed.

If no solution is found, a "No solution found" label will be displayed on the interface.

In short, clicking the "Hint" button will gradually display the solution to the Sudoku, and each click will fill a blank cell with the solution value. In this way, users can obtain the Sudoku solution process step by step.

        4.1.5 Implementation of the verification button
When the "verify" button is clicked, the call to the `verify_solution` method is triggered. This method compares the Sudoku answer entered by the user with the correct answer, and verifies and marks the content in the input box.

In the `verify_solution` method, first check whether the correct solution exists (`self.solution`). Each cell in the Sudoku is iterated over if a correct solution exists.

For each cell, first obtain the corresponding input box object. Then determine the type . If it is of type `tk.Entry`, it means that the cell is an editable input box. Next, get the value entered by the user in the input box.

If the entered value is not a number, it means that the user entered the error incorrectly. Set the value of the cell to 0 and set the font color of the input box to red.

If the entered value is a number, it is converted to an integer and compared with the correct answer. If the value entered does not match the correct answer, update the value of the cell to the value entered by the user and set the font color of the input box to red. If the entered value matches the correct answer, set the font color of the input box to black.

In this way, after clicking the "Verify" button, the program will check whether the Sudoku answer entered by the user is correct and mark the incorrect input on the interface so that the user can immediately see the location of the error. This allows users to correct errors in time and continue solving Sudoku.

  • Backtracking algorithm running demonstration

When the "Algorithm Run Display" button is clicked, the program will call the `run_algorithm` function.

In the `run_algorithm` function, the GUI interface is first updated so that the current Sudoku board is displayed on the interface. Then execute the Sudoku solving algorithm by calling the `callback` function, and pass in the current disk `self.current_solution` and the current row number `ro` and column number `co` as parameters.

The Sudoku solving algorithm `sudoku.sudoku` will check whether the current number on the board holds true every time it is called recursively. Specifically, it checks whether the same number already exists in the row, column, and 3x3 area of ​​the current position. If the same number exists, the current number does not comply with the Sudoku rules and the algorithm will backtrack.

The algorithm uses a recursive approach, starting from the first blank space, trying to fill in the numbers 1 to 9, and checking the Sudoku rules. If the current number conforms to the rules, continue to call the function recursively to fill in the next space. If the current number does not comply with the rules, go back to the previous space and try to fill in the next number.

Before each recursive call, the algorithm checks whether there is a callable `callback` function. If so, pass the current disk surface, row number and column number as parameters into the `callback` function. In this way, the GUI interface can be updated every time a number is filled in, so that the user can see the execution process of the algorithm.

The algorithm continues until it finds a solution to the Sudoku or runs through all possible number combinations. If a solution is found, the GUI interface will be updated to display the solution results. If all possible number combinations are traversed but no solution is found, a message indicating that no solution was found will be displayed.

The entire algorithm running process is implemented through continuous recursive calls. Each call will update the GUI interface, allowing users to observe the execution process of the algorithm.

After my observation, I found that basically a Sudoku problem needs to be called recursively 20,000 times. If I use the callback function to fully display the running results on the tk interface, it will still cause the program to crash, so I refresh the entire process for 2 seconds to ensure The interface can realize the normal operation of the program while running the demonstration.

おすすめ

転載: blog.csdn.net/weixin_64890968/article/details/131737539