I spent the last couple of days thinking about and writing a program in Python to solve Sudoku puzzles. I know it has been done many times before, but I wanted to try anyway. I have been really excited about programming lately. It makes me feel like a kid again, when my dad had a TRS-80 and I was writing BASIC programs on it.
This Sudoku solver uses only a very basic method. I will add more advanced methods later. This was more about an exercise in programming than creating a robust Sudoku solver. Here it is. To get a usable copy of the code, click the little icon to the left of the printer icon, then copy and paste the code from the pop-up window.
# sudoku_solver_7.py
# version 0.1
#
# Copyright 2011 Daniel Veazey <danielveazey@gmail.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
#
#
def quadfinder(find): # to figure out which quad to check for a position while solving
global quad_position
for quadcount in range(0, 9):
if find in quad_position[quadcount]:
return quadcount
print "Welcome to Daniel Veazey's Sudoku Solver 7."
print "http://www.danielveazey.com"
print "Please fill in the available numbers on the board,"
print "starting on the top row (Row 1), going left to right."
print "If a square is blank, put a 0 in its place."
print "Separate each position with a comma and a space."
print "Each position can be only one digit, 0 through 9."
print "Example:"
print "Row 1: 5, 6, 0, 0, 4, 1, 0, 0, 8"
print "Row 2: 1, 0, 0, 0, 0, 9, 0, 7, 0"
print "etc."
print
print "Let's get started."
print
print
## ask the user to populate the board by rows
row = [0, 1, 2, 3, 4, 5, 6, 7, 8]
for x in range(0, 9):
row[x] = list(input("Row " + str(x+1) + ": "))
print
print
print "Original board:"
for x in range (0, 9):
print row[x]
## define and populate the columns by referring to the rows
col = [0, 1, 2, 3, 4, 5, 6, 7, 8]
for x in range (0, 9):
col[x] = [row[0][x], row[1][x], row[2][x], row[3][x], row[4][x], row[5][x], row[6][x], row[7][x], row[8][x]]
## define and populate quads by referring to slices of rows
quad = [0, 1, 2, 3, 4, 5, 6, 7, 8]
quad[0] = row[0][0:3] + row[1][0:3] + row[2][0:3]
quad[1] = row[0][3:6] + row[1][3:6] + row[2][3:6]
quad[2] = row[0][6:] + row[1][6:] + row[2][6:]
quad[3] = row[3][0:3] + row[4][0:3] + row[5][0:3]
quad[4] = row[3][3:6] + row[4][3:6] + row[5][3:6]
quad[5] = row[3][6:] + row[4][6:] + row[5][6:]
quad[6] = row[6][0:3] + row[7][0:3] + row[8][0:3]
quad[7] = row[6][3:6] + row[7][3:6] + row[8][3:6]
quad[8] = row[6][6:] + row[7][6:] + row[8][6:]
## populate list of single positions
position = list()
for z in range(0, 81):
for x in range(0, 9):
for y in range(0, 9):
position.append(row[x][y])
## put list of possible answers in unsolved single positions
for x in range(0, 81):
if position[x] == 0:
position[x] = [1, 2, 3, 4, 5, 6, 7, 8, 9]
## define which positions are in which quads
quad_position = [0, 1, 2, 3, 4, 5, 6, 7, 8]
quad_position[0] = [0, 1, 2, 9, 10, 11, 18, 19, 20]
quad_position[1] = [3, 4, 5, 12, 13, 14, 21, 22, 23]
quad_position[2] = [6, 7, 8, 15, 16, 17, 24, 25, 26]
quad_position[3] = [27, 28, 29, 36, 37, 38, 45, 46, 47]
quad_position[4] = [30, 31, 32, 39, 40, 41, 48, 49, 50]
quad_position[5] = [33, 34, 35, 42, 43, 44, 51, 52, 53]
quad_position[6] = [54, 55, 56, 63, 64, 65, 72, 73, 74]
quad_position[7] = [57, 58, 59, 66, 67, 68, 75, 76, 77]
quad_position[8] = [60, 61, 62, 69, 70, 71, 78, 79, 80]
################# SOLVING ######################################
## this program uses only a very simple method to solve the puzzle,
## just checking to see if a possible answer for a position
## is already in its row, column or quad. if so, the possible answer
## is eliminated from the list of possible answers.
did_change = True
while did_change == True:
did_change = False
for x in range(0, 81):
if type(position[x]) == list:
y = 0
while type(position[x]) == list and y < len(position[x]): # to only keep checking if there are more items in the list of possible answers
# check to see if the possible answer is already in a position in the row, column or quad
if position[x][y] in row[x/9] or position[x][y] in col[x%9] or position[x][y] in quad[quadfinder(x)]: # quadfinder tells us which quad to check
del position[x][y] ## delete the possible answer if it's found elsewhere
did_change = True ## to keep the loop going
if len(position[x]) == 1: ## if list of possible answers is only one item long,
position[x] = position[x][0] ## remove the nested list
row[x/9][x%9] = position[x] ## and copy the answer to the rows,
col[x%9][x/9] = position[x] ## columns,
for quad_count in range(0, 9): ## and quads
if x in quad_position[quad_count]: ## similar to function quadfinder(), but used on its own here
for z in range(0, 9):
if x == quad_position[quad_count][z]:
quad[quad_count][z] = position[x]
else: y = y + 1 # to check the next item in the list of possible answers
## additional methods will go here in the future
## find out if the puzzle was solved:
solved = True
for x in range(0, 81):
if type(position[x]) == list:
solved = False
if solved == False:
print
print
print "Here is a list of positions with either"
print "the correct answers or"
print "a list of possible correct answers:"
for x in range(0, 81):
print str(x+1) + ': ' + str(position[x])
## also print board by rows
print
print
print "This is as far as I got using the simple method Scroll up"
print "to see a list of possible answers for each position on the board."
for x in range (0, 9):
print row[x]
else:
print
print
print "I SOLVED IT!!!"
for x in range (0, 9):
print row[x]Plans for future versions
- Add more advanced methods for solving puzzles
- Add a timer to tell the user how long it took to solve the puzzle
Any more ideas? Leave a comment.





Does your Sudoku solver guard against unsolvable puzzles.
I wrote a similar solver: http://vantasyworld.com/fun/sudoku/sudokusolver.html
which failed to protect against unsolvable puzzles and caused and infinite loop.
Try yours.
My solver is far from robust. It only uses two methods and is only effective against the easiest puzzles. Thanks for the comment.