My effort, messy and inefficient, but seems to work. It starts off by creating a list containing the numbers 1-9 in all the empty squares, It then repeatedly scans through these elements, removing all numbers that are already in the same row, column or 3x3 square. If this doesn't get a result (it does for some puzzles), it sets the 1st undetermined element to each of the possible values and tries to solve. The count < 650 test creates a lot of pointless looping, i need to make a better way to test if anything was changed in the last iteration. Criticism/comments/suggestions welcome.
#! /usr/bin/env python
import copy
def swap_column_rows(inlist):
result = []
for i in range(len(inlist)):
result.append([])
for j in range(len(inlist)):
result[i].append(inlist[j][i])
return result
def swap_squares_rows(inlist):
"""Input must be len(9)"""
result = []
for a in range(len(inlist)):
result.append([])
for i in range(0, len(inlist) / 3):
for j in range(0, len(inlist) / 3):
result[a].append(inlist[
( a / 3 * 3) + i][(a % 3) * 3 + j])
return result
def check_sudoku(sudoku_list):
for row in sudoku_list: #check is square
if len(row) != len(sudoku_list):
return False
for square_list in (sudoku_list, swap_column_rows(sudoku_list),
swap_squares_rows(sudoku_list)):
for row in square_list:
numbers = range(1, len(row) + 1)
for element in row:
if element in numbers:
numbers.remove(element)
else:
return False
if numbers != []:
return False
return True
def single_line_to_list(s):
result = []
for i in range(9):
result.append([])
for j in range(9):
char = s[9*i+j]
if "1" <= char <= "9":
result[i].append(int(char))
else:
result[i].append([])
return result
def fill_empty_elements(inlist):
"""takes a square list of lists of len(9) and replaces elements that are not
integers from 1 - 9 with range(1,10)"""
for i in inlist:
k = 0
for j in i:
if type(j) is not int and len(j) == 0:
i[k] = range(1,10)
k += 1
def get_coords(x, y):
"""returns a list of coordinates in the same row, column and square as
input"""
result = []
lx = range(9)
lx.remove(x)
for i in lx:
result.append((i, y))
ly = range(9)
ly.remove(y)
for i in ly:
result.append((x, i))
squarex = range(x / 3 * 3, x / 3 * 3 + 3)
squarex.remove(x)
squarey = range(y / 3 * 3, y / 3 * 3 + 3)
squarey.remove(y)
for i in squarex:
for j in squarey:
result.append((i, j))
return result
coord_dict = {}
for i in range(9):
for j in range(9):
coord_dict[(i,j)] = get_coords(i,j)
def is_not_possible(n, x, y, inlist):
"""returns True if n already exists in the same row, column or square"""
coords = coord_dict[(x,y)]
for elem in coords:
if type(inlist[elem[1]][elem[0]]) is int:
if inlist[elem[1]][elem[0]] == n:
return True
return False
def is_solved(inlist):
solved = True
for i in inlist:
for j in i:
if type(j) is not int:
solved = False
return solved
def solve_sudoku_wrap(inlist):
result = copy.deepcopy(inlist)
fill_empty_elements(result)
return solve_sudoku(result)
def solve_sudoku(inlist):
result = copy.deepcopy(inlist)
count = 0
while not is_solved(result) and count < 650:
y = 0
for i in result:
x = 0
for j in i:
if type(j) is not int:
if len(j) == 1:
i[x] = j[0]
else:
for num in j:
if is_not_possible(num,x,y,result):
j.remove(num)
x += 1
y += 1
count += 1
if count >= 650:
for i in result:
for j in i:
if type(j) is not int:
tmp = j
tmp_index = i.index(j)
for p in tmp:
i[tmp_index] = p
attempt = solve_sudoku(result)
if check_sudoku(attempt):
return attempt
return result
return result
answered
17 Mar '12, 03:44
Matthew Atki...
1.9k●3●9●30
There is a way to simulate a variable in a list, but it's not pretty. Use a list (and store one value in it). However, the variable won't be named in the usual way, and it will make it a little awkward to use. Later on, they should introduce objects (or dictionaries), and you'll kinda get what you want in a neater way.