0% found this document useful (0 votes)
7 views

DSA_GROUP PROJECT_REPORT GR 3

The document outlines a group project aimed at creating a Sudoku solver using Python, which includes a GUI interface and employs search algorithms such as backtracking and forward-checking with heuristics. The project focuses on solving a 9x9 Sudoku grid by ensuring that each row, column, and 3x3 block contains unique digits from 1 to 9, while also discussing the implementation details and limitations of the approach. Recommendations for further improvements include adding input validation and calculating the time complexity of the solution.

Uploaded by

Deepak
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
7 views

DSA_GROUP PROJECT_REPORT GR 3

The document outlines a group project aimed at creating a Sudoku solver using Python, which includes a GUI interface and employs search algorithms such as backtracking and forward-checking with heuristics. The project focuses on solving a 9x9 Sudoku grid by ensuring that each row, column, and 3x3 block contains unique digits from 1 to 9, while also discussing the implementation details and limitations of the approach. Recommendations for further improvements include adding input validation and calculating the time complexity of the solution.

Uploaded by

Deepak
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 16

DATA STRUCTURES AND

ALGORITHM
GROUP PROJECT
SUDOKU SOLVER

GROUP 3:
Agrani Anirudh Mishra MB17GBA174
Deepak Kannan MB17GBA190
Jitendra Ramshakal Varma MB17GBA200
Mahesh Kumar Yadav MB17GBA204
Raunak Kumar Gupta MB17GBA217
Vikas Gupta MB17GBA238
Executive Summary:
Sudoku Solver
Problem Statement
Create a GUI interface and create a Python program that takes in an incomplete Sudoku grid and
returns the same grid with all the completed values. The solution will use some search algorithms to
solve a puzzle, and return the puzzle solution, as follows:

1. Back-tracking (Constraint Satisfaction Problem (CSP)


2. Forward-checking with Mininum Remaining Values (MRV) heuristics

Description
Sudoku is a partially completed 9x9 grid puzzle. The task is to fill the grid with digits such that
each row, each column, and each 3x3 block contains digits from 1 to 9 exactly once. We focus
on the Sudoku puzzle because it is both constraint satisfaction problems (CSP) and requires the knowledge
of the common data structures learned in class like sets, Numpy matrix, Forward Checking and
Backtracking algorithm etc.
The scope of this project is to minimize time to solve 9x9 grid Sudoku puzzle by using different
search algorithms and heuristics.

Approach
1. For the first model, we cast the Sudoku puzzle as a constraint satisfaction
problem .
The details are as followed:

Figure 1: Illustration of CSP constraints: blue and green lines represents binary constraint for tile
A1 while red represents unary constraints for the input tiles.

The variables are each cell on the grid, so there are 81 variables: Xij = the cell value at row
i and column j, A ≤ i ≤ I; 1 ≤ j ≤ 9.
• The domain of each cell is 1, 2, 3, ..., 9.
• The constraints are:
1. Unary Constraint: m constraints, f(Xij) : xij = initially assigned value, where m is
number of initially assigned cells
2. Binary Constraint: 810 constraints, f(Xij; Yij): return x ! = y, for
– Digits in the same row have to be distinct (8 constraints per tile)
– Digits in the same column have to be distinct (8 constraints per tile)
– Digits in the same block have to be distinct (4 more constraints per tile)
There are 20 constraints per tile. Therefore, there are 20 * 81 / 2 = 810 constraints in
total where factor of 1/2 accounts for double counting.
With this setup, we will use backtracking search with following heuristics to solve for solution.
• Most Constrained Variable (MCV): to choose an unassigned cell, pick the cell that has the
fewest consistent values left in the domain

2. Forward Checking and Finding minimum remaining values, the available values to be filled in a
cell were stored in a set. The row and column values were stored in Python list. And the sudoku
matrix was initialized using Numpy matrix.

3. The output grid was designed using PyQt5 UI designer library for python. It also helped us to
debug the code and check for any anomaly in intermediate solution steps.

All these approached are discussed in detail in forthcoming sections.

1. FINDING POSSIBLE VALUE FOR THE CELL


When the games open the Sudoku will appear with 9*9 rows and
columns with each 9 grids of 3*3 rows-column. These grids with
pre-filled numbers which can’t be changed. The user need to fills the
empty cells.

But there are three important point to be highlighted while filling a value in EMPTY cell. We have to look for
missing value in first 3*3 grid. Than we have to look for the missing values in each row and their respective
columns and then fill the no. without repeating them in Grid, Rows and Columns.

The first and most naïve solver starts at the top‐left square and moves from left‐to‐right, top‐to bottom, filling
each blank square with a number 1 to 9 until the grid is invalid (that is, a number is duplicated in a row, column
or 3×3 region) or until the grid is filled and valid (that is, solved). If the grid is invalid, the solver will backtrack
until it is valid and continue forward again.

Each square in a grid has a domain of up to 9 values (1 to 9) that is reduced according to numbers already
present in the intersecting row, column and region.

In this grid, we can reduce the domain of the top‐left square to {1,4} since 2 and 3 already appear in the first
column. Likewise, we can restrict the domain of the top‐right square to {2}, since 3 and 1 occur in the rightmost
column and 4 appears in the top‐right region.
This solver acts in the same way as the previous implementation except that it restricts the domains of the grid
before it starts, only using numbers present
in the initial domains. While the domain
restriction requires computation, it should result
in significantly less backtracking to find the
solution of a grid.
2. Backtracking:
In backtracking algorithms, you try to build a solution one step at a time. If at some step it becomes clear that
the current path that you are on cannot lead to a solution you go back to the previous step (backtrack) and
choose a different path. Briefly, once you exhaust all your options at a certain step you go back. Backtracking is
also known as depth-first search.

A backtracking algorithm visits the empty cells in some order, filling in digits sequentially, or backtracking
when the number is found to be not valid. Briefly, a program would solve a puzzle by placing the digit "1" in the
first cell and checking if it is allowed to be there. If there are no violations (checking row, column, and box
constraints) then the algorithm advances to the next cell, and places a "1" in that cell. When checking for
violations, if it is discovered that the "1" is not allowed, the value is advanced to "2". If a cell is discovered
where none of the 9 digits is allowed, then the algorithm leaves that cell blank and moves back to the previous
cell. The value in that cell is then incremented by one. This is repeated until the allowed value in the last (81st)
cell is discovered.

Advantages of this method are:

 A solution is guaranteed (as long as the puzzle is valid).


 Solving time is mostly unrelated to degree of difficulty.
 The algorithm (and therefore the program code) is simpler than other algorithms, especially compared
to strong algorithms that ensure a solution to the most difficult puzzles.

The disadvantage of this method is that the solving time may be comparatively slow compared to algorithms
modelled after deductive methods.

Approach for Solving Sudoku Using Recursive Backtracking Algorithm

1. Like all other Backtracking problems, we can solve Sudoku by one by one assigning numbers to empty
cells.
2. Before assigning a number, we need to confirm that the same number is not present in current row, current
column and current 3X3 sub grid.
3. If number is not present in respective row, column or sub grid, we can assign the number, and recursively
check if this assignment leads to a solution or not. If the assignment doesn’t lead to a solution, then we try
next number for current empty cell. And if none of number (1 to 9) lead to solution, we return false.

3. Forward Checking
The first improvement on backtracking search is forward checking. Backtracking search had to place a value and
then check for conflicts. Instead it is easier to just maintain a list of which possible values each square can
possibly have given the other numbers that have been assigned. Then when the values are being assigned to that
square, only consider the ones that do not directly conflict with the other already placed numbers.
For a size three puzzle forward checks can be stored in a nine by nine by nine boolean array. Basically, each
square has its own array of nine boolean values corresponding to each of the numbers that could possibly go in
that square. If the third value in the array is set to false, then that square cannot contain a three. Maintaining these
lists is simple. Whenever a new value x is assigned is assigned, go to every other square in the same row, column
and box and mark false in its array for value x.
The basic idea with forward checking is to make a backtracking depth-first search in the solution space, but
every time a variable is assigned, the domains of all the affected variables (due to constraints) will be modified.
If a domain becomes totally empty, we backtrack. It all sounds terribly complicated but it's really a
straightforward approach, assigning the variables one at a time and whenever something is assigned revise the
possible values for the rest of the values.

4. Minimum Remaining Values:

Another method for improving the backtracking search is the minimum remaining values
heuristic. The minimum remaining values heuristic is used to alter the order in which squares are guessed
in order to reduce the number of branches at each level.
Basically instead of choosing the first empty square, the square with the least number of possible
values is chosen. For example, in the puzzle given below one of the two shaded squares would be
chosen next. This is because the two shaded squares have two possible values and the other squares
have three possible values.

By choosing the square with only two possible values instead of three the search tree only
branches in two directions instead of three. Basically, the search tree is reduced in size by a factor of two thirds.
The backtracking algorithm will be implemented using the minimum remaining value (MRV) heuristic. The
order of values to be attempted for each variable can be arbitrary. When a variable is assigned, forward
checking will be applied to further reduce variables domains.

In our code, above mentioned logic is implemented by storing the eligible digits for a cell in the sets and a list is
prepared from these sets. So, in short, we have a list of sets from which we find the cell with minimum number
of eligible digits. This is done by calculating the set with minimum number of values in it. Then the Sudoku
problem is started with this set. Once the smallest set is exhausted we again start searching for the next smallest
set and the loop is repeated till the time all the available sets in the list are exhausted.

The above approaches were implemented using the code given in annexure 1.

Test Input Given and Output:

Output:
Limitations:
1). No input validation:
Input validation such as unique values, character, zero does not exist. Validation for
characters and only natural numbers till 9 should be added.
2). Works on only 9*9 matrix

Recommendation and Further Improvement:

A functionality can be added which will calculate the time complexity of the solution from the given
input and the number of unfilled cells. This way, it can also estimate the time required for the solution.
Annexure 1: Python Code.

from QT_Designer_Output_Code import Ui_MainWindow

import numpy as np

from PyQt5 import QtWidgets

import sys

import time

class sudoku_gui(Ui_MainWindow):

def __init__(self,app):

self.sudoku = np.zeros(shape=(9,9),dtype=int) #Main Sudoku

self.valid_values = np.empty([9,9], dtype = set) #Matrix Set for storing valid values

self.empty_cell = [] #Row/Col for empty cell

self.fixed_cell = [] #Row/Col for fixed cell

self.app = app

#disable_input: Disable Textbox in GUI so that user cannot input any value

#while algorithm is solving the sudoku

def disable_input(self):

for row in range(0,9):

for col in range(0,9):

layout = self.GridLayout.itemAtPosition(row,col)

text_box = layout.widget()

text_box.setReadOnly(True)

#Read values from GUI grid cells

def read_input(self):

for row in range(0,9):

for col in range(0,9):


layout = self.GridLayout.itemAtPosition(row,col)

text_box = layout.widget()

text = text_box.text()

if text == "":

continue

else:

self.sudoku[row][col]=int(text)

#Get Empty cell and Fixed Row/Col

# Row/Col values are Stored as Tuple

def fixed_empty_cell(self):

for row in range(0,9):

for col in range(0,9):

value = self.sudoku[row][col]

if value == 0:

self.empty_cell.append((row,col))

else:

self.fixed_cell.append((row,col))

#Change font Colour of Text Box

def change_label_style(self):

for empty in self.empty_cell:

row,col = empty

layout = self.GridLayout.itemAtPosition(row,col)

text_box = layout.widget()

old_style = text_box.styleSheet()

new_style = old_style+"\ncolor: rgb(255, 0, 0);"

text_box.setStyleSheet(new_style)
self.app.processEvents()

#Input the cell value at given row, col

def set_solved_value(self,row,col):

layout = self.GridLayout.itemAtPosition(row,col)

text_box = layout.widget()

text_box.setText(str(self.sudoku[row][col]))

self.app.processEvents()

#Delete the Cell Value at given row,col

def del_cell_value(self,row,col):

layout = self.GridLayout.itemAtPosition(row,col)

text_box = layout.widget()

text_box.setText("")

self.app.processEvents()

#Returns the grid cells row and Coloum

def get_grid_cells(self,row,col):

grid = [(0,1,2),(3,4,5),(6,7,8)]

grid_cells = []

for i in range(0,3):

item = grid[i]

if row in item:

grid_row = item

if col in item:

grid_col = item

for row in grid_row:


for col in grid_col:

grid_cells.append((row,col))

return grid_cells

#Removes the values present in a given row from "Valid" set

def remove_invalid_row_values(self,row,valid):

for col in range(0,9):

valid.discard(self.sudoku[row][col])

#Removes the values present in a given col from "Valid" set

def remove_invalid_col_values(self,col,valid):

for row in range(0,9):

valid.discard(self.sudoku[row][col])

#Removes the values present in a given row/col grid from "Valid" set

def remove_invalid_grid_values(self,row,col,valid):

grid_cells = self.get_grid_cells(row,col)

for row,col in grid_cells:

valid.discard(self.sudoku[row][col])

#Find Values for each empty cell and stores it into a Numpy matrix of type set

def get_valid_value(self):

for row,col in self.empty_cell:

valid = set(range(1,10))

self.remove_invalid_row_values(row,valid)

self.remove_invalid_col_values(col,valid)

self.remove_invalid_grid_values(row,col,valid)
self.valid_values[row][col] = valid

#checks for Row, Column and Grid Validation

def validation(self,row,col,value):

status = 1

for i in range(0,9):

if value == self.sudoku[row][i]: #Row Constraint Checking

status = -1

break

elif value == self.sudoku[i][col]: #Column Constraint Checking

status = -1

break

else:

continue

if(status == 1):

grid = self.get_grid_cells(row,col)

for i,j in grid:

if value == self.sudoku[i][j]: #Grid Constraint Checking

status = -1

break

else:

continue

return status

#Finding location of a cell which needs to fill

#Location is found based minimum of possible values for a given cell

def get_cell_loc(self,valid_value):
mini = 10

row_loc = -1

col_loc = -1

for row in range(0,9):

for col in range(0,9):

set_values = valid_value[row][col]

if set_values == None:

continue

else:

length = len(set_values)

if length < mini:

mini = length

row_loc = row

col_loc = col

return(row_loc,col_loc)

#one by one start filling cells from set of possible values

#after filling a cell check for conflicts if conflict, try another value

#keep on filling unless all cells are filled

def bactracking(self,valid_value):

row,col = self.get_cell_loc(valid_value) #get location which needs to be filled

if (row == -1) and (col == -1):

status = 1

else:

try_values = valid_value[row][col] #get possibles values for a given row/col position

for num in try_values:

status = self.validation(row,col,num)
if status == -1:

continue

else:

old_value = self.sudoku[row][col]

self.sudoku[row][col] = num #set solved value

self.set_solved_value(row,col)

#time.sleep(0.1)

new_valid_val = np.copy(valid_value)

new_valid_val[row][col] = None #after filling a cell, put possible values as none

status = self.bactracking(new_valid_val) #fill another cell

if status == 1:

break

else:

self.sudoku[row][col] = old_value

self.del_cell_value(row,col)

return status

def button_connector(self):

self.pushButton.setEnabled(False)

self.pushButton_2.setEnabled(False)

self.disable_input()

self.read_input()

self.fixed_empty_cell()

self.change_label_style()

self.get_valid_value()

self.bactracking(self.valid_values)

self.pushButton_2.setEnabled(True)
def clear_sudoku(self):

self.sudoku = np.zeros(shape=(9,9),dtype=int) #Main Sudoku

self.valid_values = np.empty([9,9], dtype = set) #Matrix Set for storing valid values

self.empty_cell = [] #Row/Col for empty cell

self.fixed_cell = [] #Row/Col for fixed cell

for row in range(0,9):

for col in range(0,9):

layout = self.GridLayout.itemAtPosition(row,col)

text_box = layout.widget()

text_box.setText("")

old_style = text_box.styleSheet()

new_style = old_style.replace("\ncolor: rgb(255, 0, 0);","")

text_box.setStyleSheet(new_style)

text_box.setReadOnly(False)

self.app.processEvents()

def clear_button(self):

self.pushButton.setEnabled(False)

self.pushButton_2.setEnabled(False)

self.clear_sudoku()

self.pushButton.setEnabled(True)

self.pushButton_2.setEnabled(True)

def main():
app = QtWidgets.QApplication(sys.argv)

MainWindow = QtWidgets.QMainWindow()

ui = sudoku_gui(app)

ui.setupUi(MainWindow)

ui.pushButton.clicked.connect(ui.button_connector)

ui.pushButton_2.clicked.connect(ui.clear_button)

MainWindow.show()

sys.exit(app.exec_())

main()

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy