Error in writing a backtracking solver for sudoku - python

so recently I was attempting to write a backtracking method to solve a sudoku puzzle (2d list), and I came upon some errors I can't seem to figure out debugging.
My code:
def areLegalValues(values: list):
if len(values) in [1,4,9,16,25]:
for value in values:
if value <= len(values):
if value != 0 and values.count(value)>1:
return False
else:
return False
return True
return False
def isLegalRow(board: list, row: list):
compareList = [ ]
for r in range(len(board)):
if r == row:
for c in range(len(board[0])):
#print(board[r][c])
compareList.append(board[r][c])
return areLegalValues(compareList)
def isLegalCol(board: list, col: list):
compareList = [ ]
for r in range(len(board)):
for c in range(len(board[0])):
if c == col:
compareList.append(board[r][c])
#print(compareList)
return areLegalValues(compareList)
def isLegalBlock(board: list, block: list):
compareList = [ ]
N = int((len(board))**(1/2))
blockRowNumber = int(block//N)
blockColNumber = int(block%N)
for row in range(blockRowNumber*N, blockRowNumber*N+N):
#print(row)
for col in range(len(board[0])):
if col in range(blockColNumber*N, blockColNumber*N+N):
#print(board[row][col])
compareList.append(board[row][col])
# print(compareList)
return areLegalValues(compareList)
def isLegalSudoku(board: list):
boardLength = len(board)
for row in range(len(board)):
if isLegalRow(board,row) != True:
return False
for col in range(len(board[0])):
if isLegalCol(board,col) != True:
return False
for block in range(boardLength):
if isLegalBlock(board, block) != True:
return False
return True
def solveSudoku(board, row=0):
if row == len(board):
return board
else:
for col in range(len(board[row])):
if board[row][col] == 0:
for number in range(1, len(board)+1):
board[row][col] == number
if isLegalSudoku(board) == True:
solution = solveSudoku(board, row+1)
if solution != None:
return solution
board[row][col] == 0
return None
Test Code:
def testSolveSudoku():
print('Testing solveSudoku()...', end='')
Board = [
[1, 0, 3, 0],
[4, 3, 0, 1],
[2, 0, 1, 0],
[0, 0, 4, 2]
]
Sol = [
[1, 2, 3, 4],
[4, 3, 2, 1],
[2, 4, 1, 3],
[3, 1, 4, 2]
]
print(solveSudoku(Board))
assert(solveSudoku(Board) == Sol)
print('Passed!')
*As the names might suggest, isLegalValues takes in a list and returns if that list contains legal values, aka, the list cannot have duplicates of non zero numbers, 0's can work. isLegalRow and isLegalCol iterate through the sudoku board and build up a list with values of the certain row/column and plug them into isLegalValues. isLegalBlock finds the blocks in the board, and takes in a block number and plugs it into isLegalValues. (Through extensive testing it seems like these all work)
I currently am running into the problem in which, for some reason, my code finish solving the puzzle but manages to finish running.
example:
[1, 2, 3, 0],
[4, 3, 2, 1],
[2, 4, 1, 0],
[3, 0, 4, 2]
should be changed to
[1, 2, 3, 4],
[4, 3, 2, 1],
[2, 4, 1, 3],
[3, 1, 4, 2]
But I keep returning the code 2 lines above. (the one with 0's)
It seems that part of the code is working, as all the values that were replaced are valid, but it seems the backtracking has issues. I was wondering if someone could point out what is going wrong and how might I go about fixing it?
Thanks

Basically, I was making the error in which I was constantly skipping values in the same column with this code, and also was never attempting to solve the last column.
Solution:
def solveSudoku(board: list):
"""takes in a sudoku board and solves the board through use of backtracking
and returns the dectructively changed board"""
checkZeroes = True
for row in range(len(board)):
for col in range(len(board[0])):
if board[row][col] == 0:
checkZeroes = False
if checkZeroes == True:
return board
else:
for row in range(len(board)):
for col in range(len(board[0])):
if board[row][col] == 0:
for number in range(1,len(board)+1):
board[row][col] = number
if isLegalSudoku(board) == True:
solution = solveSudoku(board)
if solution != None:
return solution
board[row][col] = 0
return None
This accomplishes the task, albeit a bit slow, by first checking if the whole board has 0's (base case), after that, it loops through all values in the board and if the value is a 0, it tests all possible (legal) numbers. Then it checks if the whole board is legal or not, and continues on. If there is an error, it backtracks. If there is no solution, None is returned.

Related

finding possible entries in Sudoku empty cells

I have been packaging a script to calculate all possible entries in the empty cells of a sudoku game. While the algorithm to screen the vertical column and the horizontal row works, it seems that my script is not able to screen the relevant box where the empty cell is located.
The code that I am using is the following:
def possible(y,x,n):
global grid
for i in range(0,9):
if grid[y][i] == n:
return False
for i in range(0,9):
if grid[i][x] == n:
return False
x0 = (x//3)*3
y0 = (y//3)*3
for i in range(0,3):
for j in range(0,3):
if (grid[y0+i][x0+j] == n):
#print((x0+j),end=' ')
#print((y0+i),end=' ')
return False
list.append(y+1)
list.append(x+1)
list.append(n)
return True
It seems that there is some problem with the append procedure.....
Any assistance is welcome
My general comments:
Seems like you are trying to append to a list which might or might
not be defined outside of the possible() function (it's not in the supplied code).
However, as it is
not defined within the scope of that function, you generally can't
access it from the inside. Related read.)
Also you should change a variable name as
list is a built-in type of Python and it is not
recommended to use builtin types as variable
names unless you absolutely need to do so for some reason.
Generally it is not a best practice to use global variables.
My suggestion would be to move the gathering of possible
numbers outside of this function. Example:
def possible(grid: list[list[int]], num: int, pos: tuple[int, int]) -> bool:
# Check row
if num in grid[pos[0]]:
return False
# Check column
if num in [item[pos[1]] for item in grid]:
return False
# Check box
box_x = pos[1] // 3
box_y = pos[0] // 3
for i in range(box_y * 3, box_y * 3 + 3):
if num in grid[i][box_x * 3: box_x * 3 + 3] \
and (i, grid[i].index(num)) != pos:
return False
return True
Then run this 'cleaner' function in a for loop or list comprehension to collect possible numbers for a given position on the Sudoku grid.
For example (for cycle):
possible_values = []
for i in range(1,10):
if possible(grid, i, (x, y)):
possible_values.append(i)
Or this (list comprehension):
possible_values = [n for n in range(1,10) if possible(grid, n, (0, 2))]
In the Sudoku game world one call the "possible entries" of the empty cells, candidates or pencil-marks.
Here is how we can identify the candidates of the empty cells.
grid = [
[0, 0, 0, 6, 0, 8, 9, 1, 0],
[6, 0, 2, 0, 9, 0, 3, 4, 0],
[1, 9, 8, 3, 0, 0, 0, 6, 7],
[0, 5, 9, 0, 0, 0, 4, 2, 3],
[4, 0, 0, 8, 0, 3, 0, 0, 1],
[7, 1, 3, 0, 2, 0, 8, 0, 0],
[9, 6, 0, 5, 3, 7, 2, 8, 0],
[2, 0, 0, 4, 1, 0, 0, 3, 0],
[3, 4, 0, 2, 8, 0, 1, 7, 9],
]
candidates=bytearray(729)
def identifyCandidates():
for cell in range(81):
row,col=divmod(cell,9)
if grid[row][col] == 0 :
for kan in range(1,10):
if not clueInSector(cell, kan):
candidates[9*cell+(kan - 1)]=1
def clueInSector(cell, clue):
cellrow,cellcol = divmod(cell,9)
for col in range(9):
if (col != cellcol and grid[cellrow][col] == clue) :
return True
for row in range(9):
if (row != cellrow and grid[row][cellcol] == clue) :
return True
rowB = 3 * ((cellrow) // 3)
colB = 3 * ((cellcol) // 3)
for row in range (rowB,rowB + 3) :
for col in range(colB,colB +3) :
if (col != cellcol and row != cellrow and grid[row][col] == clue) :
return True
return False
Print of the 13 first cells:
cell:0 candidates: 5.
cell:1 candidates: 3.7.
cell:2 candidates: 4.5.7.
cell:3 no candidate.
cell:4 candidates: 4.5.7.
cell:5 no candidate.
cell:6 no candidate.
cell:7 no candidate.
cell:8 candidates: 2.5.
cell:9 no candidate.
cell:10 candidates: 7.
cell:11 no candidate.
cell:12 candidates: 1.7.

remove zeros between values of a list

I have a list of list and I want to remove zero values that are between numbers in each list. All my lists inside my list have same lenght.
For example:
List1=[[0,1,0,2,3,0,0],[0,5,6,0,0,9,0]]
desired output:
list2=[[0,1,2,3,0,0],[0,5,6,9,0]]
I was thinking about using indices to identify the first non zero value and last non zero value, but then I don't know how I can remove zeros between them.
You have the right idea, I think, with finding the first and last indices of nonzeroes and removing zeroes between them. Here's a function that does that:
def remove_enclosed_zeroes(lst):
try:
first_nonzero = next(
i
for (i, e) in enumerate(lst)
if e != 0
)
last_nonzero = next(
len(lst) - i - 1
for (i, e) in enumerate(reversed(lst))
if e != 0
)
except StopIteration:
return lst[:]
return lst[:first_nonzero] + \
[e for e in lst[first_nonzero:last_nonzero] if e != 0] + \
lst[last_nonzero:]
list1 = [[0,1,0,2,3,0,0],[0,5,6,0,0,9,0]]
list2 = [remove_enclosed_zeroes(sublist) for sublist in list1]
# [[0, 1, 2, 3, 0, 0], [0, 5, 6, 9, 0]]
Inspired by #python_user I thought about this a bit more and came up with this simpler solution:
def remove_internal_zeros(lst):
return [v for i, v in enumerate(lst) if v or not any(lst[i+1:]) or not any(lst[:i])]
This works by passing any value from the original list which is either
not zero (v); or
zero and not preceded by a non-zero value (not any(lst[:i])); or
zero and not followed by a non-zero value (not any(lst[i+1:]))
It can also be written as a list comprehension:
list2 = [[v for i, v in enumerate(lst) if v or not any(lst[:i]) or not any(lst[i+1:])] for lst in list1]
Original Answer
Here's another brute force approach, this pops all the zeros off either end of the list into start and end lists, then filters the balance of the list for non-zero values:
def remove_internal_zeros(l):
start_zeros = []
# get starting zeros
v = l.pop(0)
while v == 0 and len(l) > 0:
start_zeros.append(0)
v = l.pop(0)
if len(l) == 0:
return start_zeros + [v]
l = [v] + l
# get ending zeros
end_zeros = []
v = l.pop()
while v == 0 and len(l) > 0:
end_zeros.append(0)
v = l.pop()
# filter balance of list
if len(l) == 0:
return start_zeros + [v] + end_zeros
return start_zeros + list(filter(bool, l)) + [v] + end_zeros
print(remove_internal_zeros([0,1,0,2,3,0,0]))
print(remove_internal_zeros([0,5,6,0,0,9,0]))
print(remove_internal_zeros([0,0]))
print(remove_internal_zeros([0,5,0]))
Output:
[0, 1, 2, 3, 0, 0]
[0, 5, 6, 9, 0]
[0, 0]
[0, 5, 0]
I think this has to be done with brute force.
new = []
for sub in List1:
# Find last non-zero.
for j in range(len(sub)):
if sub[-1-j]:
lastnonzero = len(sub)-j
break
print(j)
newsub = []
firstnonzero = False
for i,j in enumerate(sub):
if j:
firstnonzero = True
newsub.append(j)
elif i >= lastnonzero or not firstnonzero:
newsub.append(j)
new.append(newsub)
print(new)
Please try this, remove all 0 between numbers in each list.:
list1=[[0,1,0,2,3,0,0],[0,5,6,0,0,9,0]]
rowIndex=len(list1) # count of rows
colIndex=len(list1[0]) # count of columns
for i in range(0, rowIndex):
noZeroFirstIndex = 1
noZeroLastIndex = colIndex - 2
for j in range(1, colIndex - 1):
if(list1[i][j] != 0):
noZeroFirstIndex = j
break
for j in range(colIndex -2, 0, -1):
if(list1[i][j] != 0):
noZeroLastIndex = j
break
for j in range(noZeroLastIndex, noZeroFirstIndex, -1):
if(list1[i][j] == 0 ):
del list1[i][j]
print(list1)
Result:
[[0, 1, 2, 3, 0, 0], [0, 5, 6, 9, 0]]
I wrote a pretty straight-forward approach. Try this.
def removeInnerZeroes(list):
listHold=[]
listNew = []
firstNonZeroFound = False
for item in list:
if item==0:
if firstNonZeroFound:
listHold.append(item)
else:
listNew.append(item)
else:
firstNonZeroFound=True
listHold.clear()
listNew.append(item)
listNew.extend(listHold)
return listNew
complexList = [[0,1,0,2,3,0,0],[0,5,6,0,0,9,0]]
print(complexList)
complexListNew = []
for listi in complexList:
complexListNew.append(removeInnerZeroes(listi))
print(complexListNew)
One way to do it is to treat each sublist as 3 sections:
Zeros at the front, if any
Zeros at the end, if any
Numbers in the middle from which zeros are to be purged
itertools.takewhile is handy for the front and end bits.
from itertools import takewhile
List1=[[0,1,0,2,3,0,0],[0,5,6,0,0,9,0]]
def purge_middle_zeros(numbers):
is_zero = lambda x: x==0
leading_zeros = list(takewhile(is_zero, numbers))
n_lead = len(leading_zeros)
trailing_zeros = list(takewhile(is_zero, reversed(numbers[n_lead:])))
n_trail = len(trailing_zeros)
mid_numbers = numbers[n_lead:-n_trail] if n_trail else numbers[n_lead:]
mid_non_zeros = [x for x in mid_numbers if x]
return leading_zeros + mid_non_zeros + trailing_zeros
list2 = [purge_middle_zeros(sub_list) for sub_list in List1]
list2
[[0, 1, 2, 3, 0, 0], [0, 5, 6, 9, 0]]
Other notes:
the lambda function is_zero tells takewhile what the criteria are for continuing, in this case "keep taking while it's a zero"
for the mid_non_zeros section the list comprehension [x for.... ] takes all the numbers except for the zeros (the if x at the end applies the filter)
slicing notation to pick out the middle of the list, numbers[from_start:-from_end] with the negative -from_end meaning 'except for this many elements at the end'. The case where there are no trailing zeros requires a different slice expression, i.e. numbers[from_start:]
I wrote the following code out of curiosity, that is somewhat intuitive and mimics a "looking item-by-item" approach.
def remove_zeros_inbetween(list_):
new_list = list_.copy()
for j, l in enumerate(list_): # loop through the inner lists
checking = False
start = end = None
i = 0
deleted = 0
while i < len(l): # loop through the values of an inner list
if l[i] == 0: # ignore
i += 1
continue
if l[i] != 0 and not checking: # non-zero value found
checking = True # start checking for zeros
start = i
elif l[i] != 0 and checking: # if got here and checking, the finish checking
checking = False
end = i
if start and end: # if both values have been set, i.e, different to None
# delete values in-between
new_list[j] = new_list[j][:(start+1-deleted)] + new_list[j][(end-deleted):]
deleted += end - start - 1
if l[i] != 0: # for the case of two non-zero values
start = i
checking = True
else:
i = end # ignore everything up to end
end = None # restart check
i += 1
return new_list
>>> remove_zeros_inbetween([[0, 1, 0, 2, 3, 0, 5], [0, 5, 6, 0, 0, 9, 4]])
[[0, 1, 2, 3, 5], [0, 5, 6, 9, 4]]
>>> remove_zeros_inbetween([[0, 0], [0, 3, 0], [0]]))
[[0, 0], [0, 3, 0], [0]]
>>> remove_zeros_inbetween([[0, 0, 0, 0]]))
[[0, 0, 0, 0]]
You start by replacing 0 by "0" - which is not necessary. Secondly your filter call does not save the resulting list; try:
list1[i] = list(filter(lambda a: a !=0, list1[1:-1])) # changed indexing , I suppose this could work

Recursively Generate Every Tic-Tac-Toe game in Python

I am working on a project where I generate every possible tic-tac-toe array. As a proof of concept, I am working on code to fill an array with 9 subarrays. Each subarray will have two values, the first one being 0 or 1 (for x and o respectively), and the second one being from 1 to 9 (representing when it was placed).
An example of an array I would like to get out would look like:
[[0, 0], [1, 1], [0, 2], [1, 3], [0, 4], [1, 5], [0, 6], [1, 7], [0, 8]]
I have already written code, using 9 for loops, each one nested in the one above, which gives me the desired results (every possible array, and each one unique). But I am trying to write code, using recursion, and avoiding writing tons of nested loops.
When I run the code below, it is only able to generate the array above, and cannot create other combinations. My code is below:
print("running...")
allGames = []
checkCurrentGame = [5, 5, 5, 5, 5, 5, 5, 5, 5]
stepsDown = 0
def cleanGame(move, currentGame):
for j in range(9):
if (currentGame[j][1] >= move):
currentGame[j] = [5, 0]
def completeMove(moveNumber, currentGame):
global stepsDown
stepsDown = stepsDown + 1
for i in range(9):
cleanGame(moveNumber, currentGame)
if (currentGame[i][0] == 5):
currentGame[i][0] = i % 2
currentGame[i][1] = moveNumber
allGames.append(currentGame)
break
if (stepsDown < 9):
generateGame(currentGame)
def generateGame(currentGame):
for i in range(9):
completeMove(i, currentGame)
generateGame([[5, 0], [5, 0], [5, 0], [5, 0], [5, 0], [5, 0], [5, 0], [5, 0], [5, 0]])
for x in range(len(allGames)):
print(allGames[x])
If I understand your question correctly this should do, however this is not recursion -
import itertools
[zip(p, range(0, 9)) for p in itertools.product([0, 1], repeat=9)]
The code first generates a board (9 0's or 1's) -
itertools.product([0, 1], repeat=9)
and then add the index data to it.
I recommend having a look in itertools
This is fun. Here's a method that plays the game, branching off each possible move (or at any rate, that's the intention).
Although my preference is usually to concat and return a pure result, to allow for an early exit and inspection, this recursion accumulates in a global variable.
import copy
import json
import time
s = set()
rs = []
def f():
"""
111
111000
111000000
1001001
10010010
100100100
1010100
100010001
"""
wins = [0007,0070,0700,0111,0222,0444,0124,0421]
def winner(board):
# check xs or os individual board for a win
for i in wins:
if not (i ^ (board & i)):
return True
return False
# xs board, os board, move-number, game
def g(xs, os, move, result):
# allow for early exit
global rs
if (len(rs) > 20000):
return
# base case, win or draw
if winner(xs) or winner(os) or move == 9:
#print "{0:b}".format(xs)
#print "{0:b}".format(os)
#print (result, xs, os)
#print
# confirm we're not duplicating results
enc = json.dumps(result)
if enc in s:
print "Duplicate!"
print result
s.add(enc)
# accumulate results
rs.append((result, xs, os))
return
board = xs | os
for i in xrange(9):
# candidate move
m = 1 << i
# valid move
if not (m & board):
_result = copy.deepcopy(result)
# 'O' plays on odd numbered moves
if move & 1:
_result[i] = [1, move]
g(xs, os | m, move + 1, _result)
# 'X' plays on even numbered moves
else:
_result[i] = [0, move]
g(xs | m, os, move + 1, _result)
# start the recursion
g(0, 0, 0, [[-1, -1]] * 9)
start_time = time.time()
f()
print("--- %s seconds ---" % (time.time() - start_time))
Output:
"""
rs[2002]
=> ([[0, 0], [1, 1], [-1, -1],
[-1, -1], [1, 5], [0, 2],
[0, 4], [1, 3], [-1, -1]], 97, 146)
x--
---
---
xo-
---
---
xo-
--x
---
xo-
--x
-o-
xo-
--x
xo-
xo-
-ox
xo-
"""
I recommend you watch this video on youtube. The professor teaches really nicely about recursion. I think it will benefit for you more, then getting a working code. He uses sudoku as an example, but both games are just 2D arrays.
If you watched the video, you will know how to modify this example to better suit your needs.
A pseudo code:
def place(who, when, where, table):
if (None, None) not in table: # the table is full
print(table)
return
if table[where] not (None, None): # this cell is already marked
return
table[where] (who, when) # can place our mark on the cell
# make the recursive calls
for i in range(9):
place(who.opponent, when+1, i, table)
for cell in range(9):
empty = [(None, None) for _ in range(9)]
place(firstplayer, 0, cell, empty)

Check if values in list exceed threshold a certain amount of times and return index of first exceedance

I am searching for a clean and pythonic way of checking if the contents of a list are greater than a given number (first threshold) for a certain number of times (second threshold). If both statements are true, I want to return the index of the first value which exceeds the given threshold.
Example:
# Set first and second threshold
thr1 = 4
thr2 = 5
# Example 1: Both thresholds exceeded, looking for index (3)
list1 = [1, 1, 1, 5, 1, 6, 7, 3, 6, 8]
# Example 2: Only threshold 1 is exceeded, no index return needed
list2 = [1, 1, 6, 1, 1, 1, 2, 1, 1, 1]
I don't know if it's considered pythonic to abuse the fact that booleans are ints but I like doing like this
def check(l, thr1, thr2):
c = [n > thr1 for n in l]
if sum(c) >= thr2:
return c.index(1)
Try this:
def check_list(testlist)
overages = [x for x in testlist if x > thr1]
if len(overages) >= thr2:
return testlist.index(overages[0])
# This return is not needed. Removing it will not change
# the outcome of the function.
return None
This uses the fact that you can use if statements in list comprehensions to ignore non-important values.
As mentioned by Chris_Rands in the comments, the return None is unnecessary. Removing it will not change the result of the function.
If you are looking for a one-liner (or almost)
a = filter(lambda z: z is not None, map(lambda (i, elem) : i if elem>=thr1 else None, enumerate(list1)))
print a[0] if len(a) >= thr2 else false
A naive and straightforward approach would be to iterate over the list counting the number of items greater than the first threshold and returning the index of the first match if the count exceeds the second threshold:
def answer(l, thr1, thr2):
count = 0
first_index = None
for index, item in enumerate(l):
if item > thr1:
count += 1
if not first_index:
first_index = index
if count >= thr2: # TODO: check if ">" is required instead
return first_index
thr1 = 4
thr2 = 5
list1 = [1, 1, 1, 5, 1, 6, 7, 3, 6, 8]
list2 = [1, 1, 6, 1, 1, 1, 2, 1, 1, 1]
print(answer(list1, thr1, thr2)) # prints 3
print(answer(list2, thr1, thr2)) # prints None
This is probably not quite pythonic though, but this solution has couple of advantages - we keep the index of the first match only and have an early exit out of the loop if we hit the second threshold.
In other words, we have O(k) in the best case and O(n) in the worst case, where k is the number of items before reaching the second threshold; n is the total number of items in the input list.
I don't know if I'd call it clean or pythonic, but this should work
def get_index(list1, thr1, thr2):
cnt = 0
first_element = 0
for i in list1:
if i > thr1:
cnt += 1
if first_element == 0:
first_element = i
if cnt > thr2:
return list1.index(first_element)
else:
return "criteria not met"
thr1 = 4
thr2 = 5
list1 = [1, 1, 1, 5, 1, 6, 7, 3, 6, 8]
list2 = [1, 1, 6, 1, 1, 1, 2, 1, 1, 1]
def func(lst)
res = [ i for i,j in enumerate(lst) if j > thr1]
return len(res)>=thr2 and res[0]
Output:
func(list1)
3
func(list2)
false

How to check each row/column/region in a Sudoku Python

I continue my Sudoku but i need your help. I'm blocked here :
def grid_index(grid, value):
for i, row in enumerate(grid):
for j, cell in enumerate(row):
if cell == value:
return i, j
return -1, -1
def can_fill_cell(sudoku, coords):
row = sudoku[0]
for cell in row:
if cell == value_to_input:
return False
column = [row[0] for row in sudoku]
for cell in column:
if cell == value_to_input:
return False
square = [sudoku[x][y] for x in range(3) for y in range(3)]
for cell in square:
if cell == value_to_input:
return False
return True
def solve_next_unsolved(sudoku):
"""
for x in range(9):
for y in range(9):
coords = x,y
"""
coords = (2,2)
if can_fill_cell(sudoku, coords):
sudoku[coords[0]][coords[1]] = value_to_input
print(sudoku)
So, in grid_index, i get all cell coordinates.
In my can_fill_cell function i said : "If a value is equal to the value to input, break the for and get out the function to return False and not input the value"
The problem i have right now, it's i don't know how to verify all row, column and region...
Do you have any idea please ?
Thx,
Ravaniss
EDIT : Just for information, i get my sudoku like this :
value_to_input = "1"
def import_sudoku():
with open("sudoku.txt") as f:
lines = f.read()
sudoku = [[character for character in line if not character == " "] for line in lines.split("\n")]
return sudoku
sudoku = import_sudoku()
print(sudoku)
Si, right now i can verify :
.2.
.2.
...
that's not print the result, so it's false but i can do this :
...
1..
...
.2.
22.
...
for example. what i need to verify. How can i verify all of this and make my sudoku work ?
Thx for your help !
So it sounds like your sudoku is a list of lists, e.g.
[ [1, 2, 3, 4, 5, 6, 7, 8, 9],
[4, 5, 6, 7, 8, 9, 1, 2, 3],
[7, 8, 9, 1, 2, 3, 4, 5, 6],
... ]
You'll need to verify rows and columns separately, but those are the easy ones.
for row in sudoku:
verify(row)
for col in zip(*sudoku):
verify(col)
# zip(*iterable) is a very useful idiom for flipping rows to columns
The harder one is the 3x3s. Since this is a known quantity (9x9 sudoku), it's probably easier to just enumerate it rather than calculate it yourself.
grids = [ [s[0][0], s[0][1], s[0][2],
s[1][0], s[1][1], s[1][2],
s[2][0], s[2][1], s[2][2]],
... ]
Though this is certainly time consuming, it's also straightforward which counts for a lot. You could use itertools.product to simplify this instead:
grids = [ [s[y][x] for x, y in itertools.product(range(i, i+3), repeat=2)]
for i in range(1, 8, 3) ]
Either way you have a new set of numbers to check.
for grid in grids:
verify(grid)
verify in this case is probably easiest to implement just by checking for duplicates
def verify(group):
return len(group) == len(set(group))
Though you could verify it against the literal if you like
def verify(group):
return sorted(group) == [1,2,3,4,5,6,7,8,9]

Categories