SlideShare a Scribd company logo
1 of 9
Download to read offline
Andrew Daws
CS 455-01
Final Project
Due 4/30/14
Final Project – Depth-First Sudoku Solver
Objective:
The purpose of this project is to write a program that will make use of some artificial
intelligence technique to solve a given problem. Thus through the completion of this final
project at least one problem solving method will be understood, such that it can be
implemented elsewhere.
Problem:
For centuries Sudoku puzzles have created and attempted to be solved through simple
brainstorming. For anyone who is unaware of Sudoku, it is a system of logic heavy,
combinatorial number-placement puzzles in which the objective is to fill a 9x9 grid such that
each of the nine 3x3 sub-grids, rows, and columns are composed of all digits from 1 to 9 so
there are no duplicates. Any generated Sudoku puzzle is composed of a partially filled grid,
from which a player is to try and fill the whole puzzle to completion, such that the unique
solution is found. As any Sudoku players will tell you, puzzles can vary greatly in
complexity, creating wide ranges of completion times. This presents the idea that the optimal
method to solve Sudoku puzzles is to instead use the great power of computing which should
finish puzzles considerably faster. Unfortunately the problem is still not nullified unless a
suitable solving method is established and used, which can require sophisticated strategies
with the massive number of possible routes that can be taken at any point, creating an
unimaginable number of potential solutions.
Approach:
One route that can be taken is to search for the solution through one of multiple
possible methods. There are several search algorithms that can be used such as Breadth-First
Search (BFS) and greedy search, but in the case of Sudoku my choice is relatively simple and
efficient algorithm called Depth-First Search (DFS). Depth-First Search involves the
algorithm recursively traversing a search tree or graph structure as far as possible along a
particular branch before backtracking to a previous untraveled option if it had not reached the
target goal along a branch. Thus in the case of Sudoku the DFS algorithm will recursively set
in the grid, checking that each placement is valid before changing to a new square, otherwise
it will backtrack up the tree, select a new value at a previous point in the grid, and then
continue back through the grid. This allows the algorithm to simplify the search tree on each
incorrect iteration due to the decreasing number of potential values in each square as it further
completes any particular set of rows, columns, or 3x3 sub-grids. The algorithm will finish
successfully upon finding the correct value for each square that results in the unique solution
in which all rows, columns, and 3x3 sub-grids are composed of all values from 1 to 9 which
the algorithm will know by the time it reaches the final grid position in the puzzle.
Evaluation Plan:
To test that I created a program that properly implemented DFS, I had to run multiple
test cases in which I would use multiple sample Sudoku puzzles and check that my program
was able to successfully solve the Sudoku puzzles. If the program ends up outputting an
incorrect solution for any test case than clearly the algorithm needs to be rechecked so it can
be modified, thus hopefully accounting for the failed test case, and hopefully other potential
test cases.
Test Case Puzzle
1
3 2 6
9 3 5 1
1 8 6 4
8 1 2 9
7 8
6 7 8 2
2 6 9 5
8 2 3 9
5 1 3
2
3 5 4
8 1 5
1 2
7 5 2 8
6 3
4 1 9 3
2 5 9 8
1 2 6
8 6 2
3
2 4
8 3 5
7 6 2
3 1 4 6 9 7
2
5 1 2 3
4 9 7 3
1
8 4
4
3 8
9 5
7 5 9 2
7 1 5 8
2 9 3
9 4 2 1
4 2 7 1
2 8
7 9
5
1 5 8
2 6 8
3 4
2 7 3 5 1
4 6 8 7 9
5 8
4 7 1
3 2 5
Evaluation Results:
After running the sample Sudoku puzzles through the program, I was able to confirm
that I had properly implemented DFS since the solution computed for each test case by the
program matched with the expected results. Thus my program passed for each test case, and
will likely pass for all potential test cases since the test cases had enough variation to
represent a large number of possible cases.
Test Case Computed Solution Expected Output Pass/Fail
1
4 8 3 9 2 1 6 5 7
9 6 7 3 4 5 8 2 1
2 5 1 8 7 6 4 9 3
5 4 8 1 3 2 9 7 6
7 2 9 5 6 4 1 3 8
1 3 6 7 9 8 2 4 5
3 7 2 6 8 9 5 1 4
8 1 4 2 5 3 7 6 9
6 9 5 4 1 7 3 8 2
4 8 3 9 2 1 6 5 7
9 6 7 3 4 5 8 2 1
2 5 1 8 7 6 4 9 3
5 4 8 1 3 2 9 7 6
7 2 9 5 6 4 1 3 8
1 3 6 7 9 8 2 4 5
3 7 2 6 8 9 5 1 4
8 1 4 2 5 3 7 6 9
6 9 5 4 1 7 3 8 2
Pass
2
1 3 7 2 5 6 8 4 9
9 2 8 3 1 4 5 6 7
4 6 5 8 9 7 3 1 2
6 7 3 5 4 2 9 8 1
8 1 9 6 7 3 2 5 4
5 4 2 1 8 9 7 3 6
2 5 6 7 3 1 4 9 8
3 9 1 4 2 8 6 7 5
7 8 4 9 6 5 1 2 3
1 3 7 2 5 6 8 4 9
9 2 8 3 1 4 5 6 7
4 6 5 8 9 7 3 1 2
6 7 3 5 4 2 9 8 1
8 1 9 6 7 3 2 5 4
5 4 2 1 8 9 7 3 6
2 5 6 7 3 1 4 9 8
3 9 1 4 2 8 6 7 5
7 8 4 9 6 5 1 2 3
Pass
3
6 9 7 1 2 8 3 4 5
4 2 8 6 3 5 1 9 7
3 1 5 4 7 9 6 8 2
5 3 1 2 4 6 9 7 8
2 8 6 3 9 7 4 5 1
9 7 4 5 8 1 2 6 3
1 4 9 8 5 2 7 3 6
7 5 2 9 6 3 8 1 4
8 6 3 7 1 4 5 2 9
6 9 7 1 2 8 3 4 5
4 2 8 6 3 5 1 9 7
3 1 5 4 7 9 6 8 2
5 3 1 2 4 6 9 7 8
2 8 6 3 9 7 4 5 1
9 7 4 5 8 1 2 6 3
1 4 9 8 5 2 7 3 6
7 5 2 9 6 3 8 1 4
8 6 3 7 1 4 5 2 9
Pass
4
2 3 5 7 6 1 4 8 9
4 1 9 3 2 8 5 7 6
8 6 7 5 4 9 2 1 3
7 4 6 1 3 5 9 2 8
5 2 1 8 9 6 7 3 4
9 8 3 4 7 2 6 5 1
3 9 4 2 8 7 1 6 5
6 5 2 9 1 3 8 4 7
1 7 8 6 5 4 3 9 2
2 3 5 7 6 1 4 8 9
4 1 9 3 2 8 5 7 6
8 6 7 5 4 9 2 1 3
7 4 6 1 3 5 9 2 8
5 2 1 8 9 6 7 3 4
9 8 3 4 7 2 6 5 1
3 9 4 2 8 7 1 6 5
6 5 2 9 1 3 8 4 7
1 7 8 6 5 4 3 9 2
Pass
5
4 6 9 1 5 8 3 7 2
7 1 2 4 6 3 8 5 9
5 3 8 2 9 7 6 4 1
9 2 7 6 3 4 5 1 8
3 8 5 7 1 9 4 2 6
1 4 6 5 8 2 7 9 3
6 5 3 9 4 1 2 8 7
2 9 4 8 7 6 1 3 5
8 7 1 3 2 5 9 6 4
4 6 9 1 5 8 3 7 2
7 1 2 4 6 3 8 5 9
5 3 8 2 9 7 6 4 1
9 2 7 6 3 4 5 1 8
3 8 5 7 1 9 4 2 6
1 4 6 5 8 2 7 9 3
6 5 3 9 4 1 2 8 7
2 9 4 8 7 6 1 3 5
8 7 1 3 2 5 9 6 4
Pass
Challenges and Lessons Learned:
The only significant challenge I encountered while attempting to create a successful
DFS Sudoku solver was finding a method to check each of the 3x3 sub-grids. I had no issues
with checking each of the rows nor columns since it was a relatively trivial task to
accomplish, but the sub-grids on the other hand were quite a task to figure out. Eventually I
established that the only reliable method to check the sub-grids was to use a similar method to
what I was using for the rows and columns in that I used list sets to compare the current
positions value against, but it did take awhile to figure out the math to determine which sub-
grid I was currently working with which is visible in the checkNumber function of my code.
Thus it seems I simply needed to approach that part of the problem a little bit differently and I
learned that experimentation is the best way to get the solution I was looking for, otherwise I
never would have figure out a way to check the sub-grids that would work in all cases. Only
other issues I encountered was not related to the algorithm itself, but to using the python
functions like raw_input. I certainly learned the hard way that I should always consult the
documentation first when having issues with the functions in a language, otherwise I will just
waste more time, like I did, being frustrated with something not working when it turns out I
was simply using the function incorrectly.
Conclusion:
Sudoku is a combinatorial number-placement puzzle which can vary in complexity,
creating wide ranges of completion times. Thus the best method to solve Sudoku puzzles is to
instead use various computing algorithms to quickly find the solutions. For my program I
chose to use an algorithm known as Depth-First Search (DFS). Using DFS I created a
program that recursively went through all of the grid positions, validating possible values, and
then backtracking up the search tree when an invalid value is found. Thus I allowed the
algorithm to simplify the search tree as it filled with an increasing number of correct values. I
was able to confirm my correct implementation of DFS through the use of multiple test cases.
My program was able to successfully output the expected solutions after I found a method to
test each of the 3x3 sub-grids, since I had already discovered a way to check each of the rows
and columns. Through the completion of this program I was able to find new ways to look at
similar programs and exercise my current methods of approaching problems.
References:
Daws, Andrew R. "Andrew Daws - Final Project." Youtube. N.p., n.d. Web.
<http://youtu.be/cu95he8ujJk>.
"50 Easy Sudoku Puzzles." Norvig. N.p., n.d. Web. <http://norvig.com/easy50.txt>.
"Top 95 Hard Sudoku Puzzles." Magic Tour. N.p., n.d. Web. <http://magictour.free.fr/top95>.
"Spyder Is the Scientific PYthon Development EnviRonment." Spyderlib. N.p., n.d. Web.
<https://code.google.com/p/spyderlib/>.
Python. N.p., n.d. Web. <https://www.python.org/>.
Source Code:
# Name: Andrew Daws
# ID: 1696873
# Course: CS 455-01
# Professor: Dr. Richard Stansbury
# Assignment: Final Project
# Due: 30 April 2014
# Program: Depth-First Sudoku Solver
# Obtains all user input and loops the program
def userInput():
# Initialize variables
selectContinue = 'Y'
selectPuzzle = 0
# User inputs anything other than no
while(not(selectContinue == 'N' or selectContinue == 'n')):
# User selects a puzzle
print "Please select a Sudoku Puzzle. [1-25]: ",
try:
selectPuzzle = int(raw_input())
print " "
# Proceed with program if valid selection
if selectPuzzle > 0 and selectPuzzle <= 25:
# Fill grid list with the selected puzzle
fillGrid(selectPuzzle)
# Solve puzzle
solvePuzzle()
# Solve another puzzle
print "Run again? [Y/N]: "
selectContinue = raw_input()
print " "
# Notify of error if invalid selection
else:
print "Invalid selection, please try again."
print " "
# Exception handling if a non-integer is entered
except ValueError:
print "Invalid input, please try again."
print " "
# Fill the grid solving for with file input
def fillGrid(selectPuzzle):
# Create the file string for the selected puzzle
puzzleString = "Puzzles" + str(selectPuzzle) + ".txt"
# Open puzzle input file with read access
input = open(puzzleString, "r")
# Read string from input file one character at a time
for inputIndex, i in enumerate(input.read()):
# If value is not 0, otherwise ignore
if (int)(i):
# Set the index position in the puzzle as a given
puzzle[inputIndex] = True
# Set the index position in the grid as a new value
grid[inputIndex] = (int)(i)
# Close the input file to prevent a lockup
input.close()
# Create a list of row sets [Top to Bottom]
def fillRows():
# Rows 1 - 3
row.append([ 0, 1, 2, 3, 4, 5, 6, 7, 8])
row.append([ 9, 10, 11, 12, 13, 14, 15, 16, 17])
row.append([18, 19, 20, 21, 22, 23, 24, 25, 26])
# Rows 4 - 6
row.append([27, 28, 29, 30, 31, 32, 33, 34, 35])
row.append([36, 37, 38, 39, 40, 41, 42, 43, 44])
row.append([45, 46, 47, 48, 49, 50, 51, 52, 53])
# Rows 7 - 9
row.append([54, 55, 56, 57, 58, 59, 60, 61, 62])
row.append([63, 64, 65, 66, 67, 68, 69, 70, 71])
row.append([72, 73, 74, 75, 76, 77, 78, 79, 80])
# Create list of column sets [Left to Right]
def fillColumns():
# Columns 1 - 3
column.append([0, 9, 18, 27, 36, 45, 54, 63, 72])
column.append([1, 10, 19, 28, 37, 46, 55, 64, 73])
column.append([2, 11, 20, 29, 38, 47, 56, 65, 74])
# Columns 4 - 6
column.append([3, 12, 21, 30, 39, 48, 57, 66, 75])
column.append([4, 13, 22, 31, 40, 49, 58, 67, 76])
column.append([5, 14, 23, 32, 41, 50, 59, 68, 77])
# Columns 7 - 9
column.append([6, 15, 24, 33, 42, 51, 60, 69, 78])
column.append([7, 16, 25, 34, 43, 52, 61, 70, 79])
column.append([8, 17, 26, 35, 44, 53, 62, 71, 80])
# Create list of subgrid sets [Top Left to Bottom Right]
def fillSubgrids():
# Subgrids 1 - 3
subgrid.append([ 0, 1, 2, 9, 10, 11, 18, 19, 20])
subgrid.append([ 3, 4, 5, 12, 13, 14, 21, 22, 23])
subgrid.append([ 6, 7, 8, 15, 16, 17, 24, 25, 26])
# Subgrids 4 - 6
subgrid.append([27, 28, 29, 36, 37, 38, 45, 46, 47])
subgrid.append([30, 31, 32, 39, 40, 41, 48, 49, 50])
subgrid.append([33, 34, 35, 42, 43, 44, 51, 52, 53])
# Subgrids 7 - 9
subgrid.append([54, 55, 56, 63, 64, 65, 72, 73, 74])
subgrid.append([57, 58, 59, 66, 67, 68, 75, 76, 77])
subgrid.append([60, 61, 62, 69, 70, 71, 78, 79, 80])
# Displays the current grid values
def displayGrid():
# Increment through rows
for i in range(9):
# Increment through sets of 3 on each row
for j in range(3):
# Print each set of 3
for k in range(3):
# Store current grid value to avoid duplicate calculations
gridTempValue = grid[i*9 + (j*(2+1)) + k]
# Separate sets of 3
if k == 0 and j > 0:
print "|",
# Convert grid to string values and print
if gridTempValue > 0:
gridString = str(gridTempValue)
print gridString,
# Print empty grid values
else:
print " ",
# Move to new print row
print " "
# Separate subgrids
if i == 2 or i == 5:
print "------+-------+------"
print " "
# Main function, solves sudoku puzzles
def solvePuzzle():
# Print out puzzle
print "Uncompleted Puzzle:n"
displayGrid()
# Initialize variables
i = 0
backtrack = 0
# Check all grid positions
while(i < 81):
# Check if the grid position is a given in the puzzle
if puzzle[i]:
# Move to next grid position
if not backtrack:
i = i + 1
# Move to previous grid position
else:
i = i - 1
# No gridValue in grid position
else:
gridValue = grid[i]
oldGridValue = grid[i]
# Check if gridValue is already 9
while(gridValue < 9):
# Not 9, increment
if (gridValue < 9):
gridValue = gridValue + 1
# If value is valid, add to grid postition
if checkNumber(gridValue, i):
grid[i] = gridValue
backtrack = 0
break
# Value is 9, reset gridValue
if (grid[i] == oldGridValue):
grid[i] = 0
backtrack = 1
# gridValue valid, increment to next grid position, restart
if not backtrack:
i = i + 1
# Backtrack to previous grid postition
else:
i = i - 1
# Print out puzzle solution
print "Solved Puzzle:n"
displayGrid()
# Reset grid and puzzle
for i in range(81):
grid[i] = 0
puzzle[i] = False
# Determine if gridValue is valid if placed on grid
def checkNumber(gridValue, gridPosition):
# Get current position
getRow = gridPosition / 9
getColumn = gridPosition % 9
getSubgrid = (getRow / 3) * 3 + (getColumn / 3)
# Check if gridValue is not already in row
for i in row[getRow]:
if (grid[i] == gridValue):
return False
# Check if gridValue is not already in column
for i in column[getColumn]:
if (grid[i] == gridValue):
return False
# Check if gridValue is not already in subgrid
for i in subgrid[getSubgrid]:
if (grid[i] == gridValue):
return False
# Value is valid
return True
# Create grid list initialized with 0's
grid = [0] * 81
# Create puzzle list initialized with false
puzzle = [False] * 81
# Create empty row list
row = []
# Create empty column list
column = []
# Create empty subgrid list
subgrid = []
# Fill row list
fillRows()
# Fill column list
fillColumns()
# Fill subgrid list
fillSubgrids()
# Initialize user input
userInput()

More Related Content

Similar to Andrew Daws - Final Project Report

Sudoku Solving with Computational Intelligence
Sudoku Solving with Computational IntelligenceSudoku Solving with Computational Intelligence
Sudoku Solving with Computational Intelligenceharaldhiss
 
Predictive Analytics for Everyone! Building CART Models using R - Chantal D....
Predictive Analytics for Everyone!  Building CART Models using R - Chantal D....Predictive Analytics for Everyone!  Building CART Models using R - Chantal D....
Predictive Analytics for Everyone! Building CART Models using R - Chantal D....Chantal Larose
 
Solve Sudoku using Constraint Propagation- Search and Genetic Algorithm
Solve Sudoku using  Constraint Propagation- Search and Genetic AlgorithmSolve Sudoku using  Constraint Propagation- Search and Genetic Algorithm
Solve Sudoku using Constraint Propagation- Search and Genetic AlgorithmAi Sha
 
@RISK Unchained Webinar
@RISK Unchained Webinar@RISK Unchained Webinar
@RISK Unchained WebinarAndrew Sich
 
No (Lab) Jacket Required Workshop [Kanban Global Summit 2022]
No (Lab) Jacket Required Workshop [Kanban Global Summit 2022]No (Lab) Jacket Required Workshop [Kanban Global Summit 2022]
No (Lab) Jacket Required Workshop [Kanban Global Summit 2022]Matthew Philip
 
CS 2110 Programming Competition Entry Readme
CS 2110 Programming Competition Entry ReadmeCS 2110 Programming Competition Entry Readme
CS 2110 Programming Competition Entry ReadmeChuck Moyes
 
Deep Learning for Folks Without (or With!) a Ph.D.
Deep Learning for Folks Without (or With!) a Ph.D.Deep Learning for Folks Without (or With!) a Ph.D.
Deep Learning for Folks Without (or With!) a Ph.D.Douglas Starnes
 
Advanced Agile Planning
Advanced Agile PlanningAdvanced Agile Planning
Advanced Agile PlanningMike Cohn
 
Lecture-1-Algorithms.pptx
Lecture-1-Algorithms.pptxLecture-1-Algorithms.pptx
Lecture-1-Algorithms.pptxxalahama3
 
More Reliable Delivery with Monte Carlo & Story Mapping
More Reliable Delivery with Monte Carlo & Story MappingMore Reliable Delivery with Monte Carlo & Story Mapping
More Reliable Delivery with Monte Carlo & Story MappingConal Scanlon
 
Ecs 10 programming assignment 4 loopapalooza
Ecs 10 programming assignment 4   loopapaloozaEcs 10 programming assignment 4   loopapalooza
Ecs 10 programming assignment 4 loopapaloozaJenniferBall44
 

Similar to Andrew Daws - Final Project Report (20)

Sudoku Solving with Computational Intelligence
Sudoku Solving with Computational IntelligenceSudoku Solving with Computational Intelligence
Sudoku Solving with Computational Intelligence
 
Poscat seminar 7
Poscat seminar 7Poscat seminar 7
Poscat seminar 7
 
Predictive Analytics for Everyone! Building CART Models using R - Chantal D....
Predictive Analytics for Everyone!  Building CART Models using R - Chantal D....Predictive Analytics for Everyone!  Building CART Models using R - Chantal D....
Predictive Analytics for Everyone! Building CART Models using R - Chantal D....
 
Solve Sudoku using Constraint Propagation- Search and Genetic Algorithm
Solve Sudoku using  Constraint Propagation- Search and Genetic AlgorithmSolve Sudoku using  Constraint Propagation- Search and Genetic Algorithm
Solve Sudoku using Constraint Propagation- Search and Genetic Algorithm
 
Sudokureport
SudokureportSudokureport
Sudokureport
 
Sudoku solve rmain
Sudoku solve rmainSudoku solve rmain
Sudoku solve rmain
 
Poscat seminar 11
Poscat seminar 11Poscat seminar 11
Poscat seminar 11
 
Poscat seminar 10
Poscat seminar 10Poscat seminar 10
Poscat seminar 10
 
Maze Path Finding
Maze Path FindingMaze Path Finding
Maze Path Finding
 
IBM Study material
IBM Study materialIBM Study material
IBM Study material
 
@RISK Unchained Webinar
@RISK Unchained Webinar@RISK Unchained Webinar
@RISK Unchained Webinar
 
No (Lab) Jacket Required Workshop [Kanban Global Summit 2022]
No (Lab) Jacket Required Workshop [Kanban Global Summit 2022]No (Lab) Jacket Required Workshop [Kanban Global Summit 2022]
No (Lab) Jacket Required Workshop [Kanban Global Summit 2022]
 
CS 2110 Programming Competition Entry Readme
CS 2110 Programming Competition Entry ReadmeCS 2110 Programming Competition Entry Readme
CS 2110 Programming Competition Entry Readme
 
Deep Learning for Folks Without (or With!) a Ph.D.
Deep Learning for Folks Without (or With!) a Ph.D.Deep Learning for Folks Without (or With!) a Ph.D.
Deep Learning for Folks Without (or With!) a Ph.D.
 
Advanced Agile Planning
Advanced Agile PlanningAdvanced Agile Planning
Advanced Agile Planning
 
Research Poster PowerPoint
Research Poster PowerPointResearch Poster PowerPoint
Research Poster PowerPoint
 
P1
P1P1
P1
 
Lecture-1-Algorithms.pptx
Lecture-1-Algorithms.pptxLecture-1-Algorithms.pptx
Lecture-1-Algorithms.pptx
 
More Reliable Delivery with Monte Carlo & Story Mapping
More Reliable Delivery with Monte Carlo & Story MappingMore Reliable Delivery with Monte Carlo & Story Mapping
More Reliable Delivery with Monte Carlo & Story Mapping
 
Ecs 10 programming assignment 4 loopapalooza
Ecs 10 programming assignment 4   loopapaloozaEcs 10 programming assignment 4   loopapalooza
Ecs 10 programming assignment 4 loopapalooza
 

Andrew Daws - Final Project Report

  • 1. Andrew Daws CS 455-01 Final Project Due 4/30/14 Final Project – Depth-First Sudoku Solver Objective: The purpose of this project is to write a program that will make use of some artificial intelligence technique to solve a given problem. Thus through the completion of this final project at least one problem solving method will be understood, such that it can be implemented elsewhere. Problem: For centuries Sudoku puzzles have created and attempted to be solved through simple brainstorming. For anyone who is unaware of Sudoku, it is a system of logic heavy, combinatorial number-placement puzzles in which the objective is to fill a 9x9 grid such that each of the nine 3x3 sub-grids, rows, and columns are composed of all digits from 1 to 9 so there are no duplicates. Any generated Sudoku puzzle is composed of a partially filled grid, from which a player is to try and fill the whole puzzle to completion, such that the unique solution is found. As any Sudoku players will tell you, puzzles can vary greatly in complexity, creating wide ranges of completion times. This presents the idea that the optimal method to solve Sudoku puzzles is to instead use the great power of computing which should finish puzzles considerably faster. Unfortunately the problem is still not nullified unless a suitable solving method is established and used, which can require sophisticated strategies with the massive number of possible routes that can be taken at any point, creating an unimaginable number of potential solutions. Approach: One route that can be taken is to search for the solution through one of multiple possible methods. There are several search algorithms that can be used such as Breadth-First Search (BFS) and greedy search, but in the case of Sudoku my choice is relatively simple and efficient algorithm called Depth-First Search (DFS). Depth-First Search involves the algorithm recursively traversing a search tree or graph structure as far as possible along a particular branch before backtracking to a previous untraveled option if it had not reached the target goal along a branch. Thus in the case of Sudoku the DFS algorithm will recursively set in the grid, checking that each placement is valid before changing to a new square, otherwise it will backtrack up the tree, select a new value at a previous point in the grid, and then continue back through the grid. This allows the algorithm to simplify the search tree on each incorrect iteration due to the decreasing number of potential values in each square as it further completes any particular set of rows, columns, or 3x3 sub-grids. The algorithm will finish successfully upon finding the correct value for each square that results in the unique solution in which all rows, columns, and 3x3 sub-grids are composed of all values from 1 to 9 which the algorithm will know by the time it reaches the final grid position in the puzzle.
  • 2. Evaluation Plan: To test that I created a program that properly implemented DFS, I had to run multiple test cases in which I would use multiple sample Sudoku puzzles and check that my program was able to successfully solve the Sudoku puzzles. If the program ends up outputting an incorrect solution for any test case than clearly the algorithm needs to be rechecked so it can be modified, thus hopefully accounting for the failed test case, and hopefully other potential test cases. Test Case Puzzle 1 3 2 6 9 3 5 1 1 8 6 4 8 1 2 9 7 8 6 7 8 2 2 6 9 5 8 2 3 9 5 1 3 2 3 5 4 8 1 5 1 2 7 5 2 8 6 3 4 1 9 3 2 5 9 8 1 2 6 8 6 2 3 2 4 8 3 5 7 6 2 3 1 4 6 9 7 2 5 1 2 3 4 9 7 3 1 8 4 4 3 8 9 5 7 5 9 2 7 1 5 8 2 9 3 9 4 2 1 4 2 7 1 2 8 7 9 5 1 5 8 2 6 8 3 4 2 7 3 5 1 4 6 8 7 9 5 8 4 7 1 3 2 5
  • 3. Evaluation Results: After running the sample Sudoku puzzles through the program, I was able to confirm that I had properly implemented DFS since the solution computed for each test case by the program matched with the expected results. Thus my program passed for each test case, and will likely pass for all potential test cases since the test cases had enough variation to represent a large number of possible cases. Test Case Computed Solution Expected Output Pass/Fail 1 4 8 3 9 2 1 6 5 7 9 6 7 3 4 5 8 2 1 2 5 1 8 7 6 4 9 3 5 4 8 1 3 2 9 7 6 7 2 9 5 6 4 1 3 8 1 3 6 7 9 8 2 4 5 3 7 2 6 8 9 5 1 4 8 1 4 2 5 3 7 6 9 6 9 5 4 1 7 3 8 2 4 8 3 9 2 1 6 5 7 9 6 7 3 4 5 8 2 1 2 5 1 8 7 6 4 9 3 5 4 8 1 3 2 9 7 6 7 2 9 5 6 4 1 3 8 1 3 6 7 9 8 2 4 5 3 7 2 6 8 9 5 1 4 8 1 4 2 5 3 7 6 9 6 9 5 4 1 7 3 8 2 Pass 2 1 3 7 2 5 6 8 4 9 9 2 8 3 1 4 5 6 7 4 6 5 8 9 7 3 1 2 6 7 3 5 4 2 9 8 1 8 1 9 6 7 3 2 5 4 5 4 2 1 8 9 7 3 6 2 5 6 7 3 1 4 9 8 3 9 1 4 2 8 6 7 5 7 8 4 9 6 5 1 2 3 1 3 7 2 5 6 8 4 9 9 2 8 3 1 4 5 6 7 4 6 5 8 9 7 3 1 2 6 7 3 5 4 2 9 8 1 8 1 9 6 7 3 2 5 4 5 4 2 1 8 9 7 3 6 2 5 6 7 3 1 4 9 8 3 9 1 4 2 8 6 7 5 7 8 4 9 6 5 1 2 3 Pass 3 6 9 7 1 2 8 3 4 5 4 2 8 6 3 5 1 9 7 3 1 5 4 7 9 6 8 2 5 3 1 2 4 6 9 7 8 2 8 6 3 9 7 4 5 1 9 7 4 5 8 1 2 6 3 1 4 9 8 5 2 7 3 6 7 5 2 9 6 3 8 1 4 8 6 3 7 1 4 5 2 9 6 9 7 1 2 8 3 4 5 4 2 8 6 3 5 1 9 7 3 1 5 4 7 9 6 8 2 5 3 1 2 4 6 9 7 8 2 8 6 3 9 7 4 5 1 9 7 4 5 8 1 2 6 3 1 4 9 8 5 2 7 3 6 7 5 2 9 6 3 8 1 4 8 6 3 7 1 4 5 2 9 Pass 4 2 3 5 7 6 1 4 8 9 4 1 9 3 2 8 5 7 6 8 6 7 5 4 9 2 1 3 7 4 6 1 3 5 9 2 8 5 2 1 8 9 6 7 3 4 9 8 3 4 7 2 6 5 1 3 9 4 2 8 7 1 6 5 6 5 2 9 1 3 8 4 7 1 7 8 6 5 4 3 9 2 2 3 5 7 6 1 4 8 9 4 1 9 3 2 8 5 7 6 8 6 7 5 4 9 2 1 3 7 4 6 1 3 5 9 2 8 5 2 1 8 9 6 7 3 4 9 8 3 4 7 2 6 5 1 3 9 4 2 8 7 1 6 5 6 5 2 9 1 3 8 4 7 1 7 8 6 5 4 3 9 2 Pass 5 4 6 9 1 5 8 3 7 2 7 1 2 4 6 3 8 5 9 5 3 8 2 9 7 6 4 1 9 2 7 6 3 4 5 1 8 3 8 5 7 1 9 4 2 6 1 4 6 5 8 2 7 9 3 6 5 3 9 4 1 2 8 7 2 9 4 8 7 6 1 3 5 8 7 1 3 2 5 9 6 4 4 6 9 1 5 8 3 7 2 7 1 2 4 6 3 8 5 9 5 3 8 2 9 7 6 4 1 9 2 7 6 3 4 5 1 8 3 8 5 7 1 9 4 2 6 1 4 6 5 8 2 7 9 3 6 5 3 9 4 1 2 8 7 2 9 4 8 7 6 1 3 5 8 7 1 3 2 5 9 6 4 Pass
  • 4. Challenges and Lessons Learned: The only significant challenge I encountered while attempting to create a successful DFS Sudoku solver was finding a method to check each of the 3x3 sub-grids. I had no issues with checking each of the rows nor columns since it was a relatively trivial task to accomplish, but the sub-grids on the other hand were quite a task to figure out. Eventually I established that the only reliable method to check the sub-grids was to use a similar method to what I was using for the rows and columns in that I used list sets to compare the current positions value against, but it did take awhile to figure out the math to determine which sub- grid I was currently working with which is visible in the checkNumber function of my code. Thus it seems I simply needed to approach that part of the problem a little bit differently and I learned that experimentation is the best way to get the solution I was looking for, otherwise I never would have figure out a way to check the sub-grids that would work in all cases. Only other issues I encountered was not related to the algorithm itself, but to using the python functions like raw_input. I certainly learned the hard way that I should always consult the documentation first when having issues with the functions in a language, otherwise I will just waste more time, like I did, being frustrated with something not working when it turns out I was simply using the function incorrectly. Conclusion: Sudoku is a combinatorial number-placement puzzle which can vary in complexity, creating wide ranges of completion times. Thus the best method to solve Sudoku puzzles is to instead use various computing algorithms to quickly find the solutions. For my program I chose to use an algorithm known as Depth-First Search (DFS). Using DFS I created a program that recursively went through all of the grid positions, validating possible values, and then backtracking up the search tree when an invalid value is found. Thus I allowed the algorithm to simplify the search tree as it filled with an increasing number of correct values. I was able to confirm my correct implementation of DFS through the use of multiple test cases. My program was able to successfully output the expected solutions after I found a method to test each of the 3x3 sub-grids, since I had already discovered a way to check each of the rows and columns. Through the completion of this program I was able to find new ways to look at similar programs and exercise my current methods of approaching problems. References: Daws, Andrew R. "Andrew Daws - Final Project." Youtube. N.p., n.d. Web. <http://youtu.be/cu95he8ujJk>. "50 Easy Sudoku Puzzles." Norvig. N.p., n.d. Web. <http://norvig.com/easy50.txt>. "Top 95 Hard Sudoku Puzzles." Magic Tour. N.p., n.d. Web. <http://magictour.free.fr/top95>. "Spyder Is the Scientific PYthon Development EnviRonment." Spyderlib. N.p., n.d. Web. <https://code.google.com/p/spyderlib/>. Python. N.p., n.d. Web. <https://www.python.org/>.
  • 5. Source Code: # Name: Andrew Daws # ID: 1696873 # Course: CS 455-01 # Professor: Dr. Richard Stansbury # Assignment: Final Project # Due: 30 April 2014 # Program: Depth-First Sudoku Solver # Obtains all user input and loops the program def userInput(): # Initialize variables selectContinue = 'Y' selectPuzzle = 0 # User inputs anything other than no while(not(selectContinue == 'N' or selectContinue == 'n')): # User selects a puzzle print "Please select a Sudoku Puzzle. [1-25]: ", try: selectPuzzle = int(raw_input()) print " " # Proceed with program if valid selection if selectPuzzle > 0 and selectPuzzle <= 25: # Fill grid list with the selected puzzle fillGrid(selectPuzzle) # Solve puzzle solvePuzzle() # Solve another puzzle print "Run again? [Y/N]: " selectContinue = raw_input() print " " # Notify of error if invalid selection else: print "Invalid selection, please try again." print " " # Exception handling if a non-integer is entered except ValueError: print "Invalid input, please try again." print " " # Fill the grid solving for with file input def fillGrid(selectPuzzle): # Create the file string for the selected puzzle puzzleString = "Puzzles" + str(selectPuzzle) + ".txt" # Open puzzle input file with read access input = open(puzzleString, "r")
  • 6. # Read string from input file one character at a time for inputIndex, i in enumerate(input.read()): # If value is not 0, otherwise ignore if (int)(i): # Set the index position in the puzzle as a given puzzle[inputIndex] = True # Set the index position in the grid as a new value grid[inputIndex] = (int)(i) # Close the input file to prevent a lockup input.close() # Create a list of row sets [Top to Bottom] def fillRows(): # Rows 1 - 3 row.append([ 0, 1, 2, 3, 4, 5, 6, 7, 8]) row.append([ 9, 10, 11, 12, 13, 14, 15, 16, 17]) row.append([18, 19, 20, 21, 22, 23, 24, 25, 26]) # Rows 4 - 6 row.append([27, 28, 29, 30, 31, 32, 33, 34, 35]) row.append([36, 37, 38, 39, 40, 41, 42, 43, 44]) row.append([45, 46, 47, 48, 49, 50, 51, 52, 53]) # Rows 7 - 9 row.append([54, 55, 56, 57, 58, 59, 60, 61, 62]) row.append([63, 64, 65, 66, 67, 68, 69, 70, 71]) row.append([72, 73, 74, 75, 76, 77, 78, 79, 80]) # Create list of column sets [Left to Right] def fillColumns(): # Columns 1 - 3 column.append([0, 9, 18, 27, 36, 45, 54, 63, 72]) column.append([1, 10, 19, 28, 37, 46, 55, 64, 73]) column.append([2, 11, 20, 29, 38, 47, 56, 65, 74]) # Columns 4 - 6 column.append([3, 12, 21, 30, 39, 48, 57, 66, 75]) column.append([4, 13, 22, 31, 40, 49, 58, 67, 76]) column.append([5, 14, 23, 32, 41, 50, 59, 68, 77]) # Columns 7 - 9 column.append([6, 15, 24, 33, 42, 51, 60, 69, 78]) column.append([7, 16, 25, 34, 43, 52, 61, 70, 79]) column.append([8, 17, 26, 35, 44, 53, 62, 71, 80]) # Create list of subgrid sets [Top Left to Bottom Right] def fillSubgrids(): # Subgrids 1 - 3 subgrid.append([ 0, 1, 2, 9, 10, 11, 18, 19, 20]) subgrid.append([ 3, 4, 5, 12, 13, 14, 21, 22, 23]) subgrid.append([ 6, 7, 8, 15, 16, 17, 24, 25, 26])
  • 7. # Subgrids 4 - 6 subgrid.append([27, 28, 29, 36, 37, 38, 45, 46, 47]) subgrid.append([30, 31, 32, 39, 40, 41, 48, 49, 50]) subgrid.append([33, 34, 35, 42, 43, 44, 51, 52, 53]) # Subgrids 7 - 9 subgrid.append([54, 55, 56, 63, 64, 65, 72, 73, 74]) subgrid.append([57, 58, 59, 66, 67, 68, 75, 76, 77]) subgrid.append([60, 61, 62, 69, 70, 71, 78, 79, 80]) # Displays the current grid values def displayGrid(): # Increment through rows for i in range(9): # Increment through sets of 3 on each row for j in range(3): # Print each set of 3 for k in range(3): # Store current grid value to avoid duplicate calculations gridTempValue = grid[i*9 + (j*(2+1)) + k] # Separate sets of 3 if k == 0 and j > 0: print "|", # Convert grid to string values and print if gridTempValue > 0: gridString = str(gridTempValue) print gridString, # Print empty grid values else: print " ", # Move to new print row print " " # Separate subgrids if i == 2 or i == 5: print "------+-------+------" print " " # Main function, solves sudoku puzzles def solvePuzzle(): # Print out puzzle print "Uncompleted Puzzle:n" displayGrid() # Initialize variables i = 0 backtrack = 0 # Check all grid positions
  • 8. while(i < 81): # Check if the grid position is a given in the puzzle if puzzle[i]: # Move to next grid position if not backtrack: i = i + 1 # Move to previous grid position else: i = i - 1 # No gridValue in grid position else: gridValue = grid[i] oldGridValue = grid[i] # Check if gridValue is already 9 while(gridValue < 9): # Not 9, increment if (gridValue < 9): gridValue = gridValue + 1 # If value is valid, add to grid postition if checkNumber(gridValue, i): grid[i] = gridValue backtrack = 0 break # Value is 9, reset gridValue if (grid[i] == oldGridValue): grid[i] = 0 backtrack = 1 # gridValue valid, increment to next grid position, restart if not backtrack: i = i + 1 # Backtrack to previous grid postition else: i = i - 1 # Print out puzzle solution print "Solved Puzzle:n" displayGrid() # Reset grid and puzzle for i in range(81): grid[i] = 0 puzzle[i] = False # Determine if gridValue is valid if placed on grid def checkNumber(gridValue, gridPosition): # Get current position getRow = gridPosition / 9 getColumn = gridPosition % 9 getSubgrid = (getRow / 3) * 3 + (getColumn / 3)
  • 9. # Check if gridValue is not already in row for i in row[getRow]: if (grid[i] == gridValue): return False # Check if gridValue is not already in column for i in column[getColumn]: if (grid[i] == gridValue): return False # Check if gridValue is not already in subgrid for i in subgrid[getSubgrid]: if (grid[i] == gridValue): return False # Value is valid return True # Create grid list initialized with 0's grid = [0] * 81 # Create puzzle list initialized with false puzzle = [False] * 81 # Create empty row list row = [] # Create empty column list column = [] # Create empty subgrid list subgrid = [] # Fill row list fillRows() # Fill column list fillColumns() # Fill subgrid list fillSubgrids() # Initialize user input userInput()