# Hints to make Sudoku solver more Pythonic (II)

I tried to implement as many changes suggested as possible as well as expanding the code base according to the suggested link. The original question can be found here. It does solve 4x4 and 9x9 Sodukus.

What should I do to make it more Pythonic?

This is what I came up with:

from numpy import *
import sys

TEXT = '.123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
NUMBER = arange(len(TEXT)+1)

with open(name) as fid:
s = int(sqrt(len(str)))

# check input
if s != int(sqrt(s))**2:
raise Exception("Input needs to be quadratic")

if len(str.strip(TEXT[0:s + 1])) != 0:
raise Exception("Do not use anything beside %s with this base" % TEXT[0:s + 1])
return (str,s)

## Set the value in field and adjust the possibility tensor
def set(col,row,wert,feld,pos,s):
if feld[col,row] != 0:
raise Exception

pos[col,row,:] = 0
pos[:,row,wert] = 0
pos[col,:,wert] = 0

ss = int(sqrt(s))

col_start = col // ss * ss
row_start = row // ss * ss
pos[col_start:col_start + ss,row_start:row_start + ss,wert] = 0

feld[col,row] = wert + 1

## Read in the stuff and create field
feld = zeros((s,s),dtype=uint8)
pos = ones((s,s,s),dtype=bool_)

for ind in range(s**2):
if str[ind] is not '0' and str[ind] is not '.':
set(ind // s,ind % s,NUMBER[TEXT.index(str[ind])] - 1,feld,pos,s)
return (feld,pos,s)

## Write the output into a file
def write(name,feld,s):
with open(name,'w') as fid:
for col in range(s):
for row in range(s):
fid.write(TEXT[feld[col,row]])
fid.write('\n')

## Are we there yet?
def done(feld,s):
return count_nonzero(feld) == s**2

## Start the Analytical Process returns 0 if solved
def anal(feld,pos,s):
neu = 1
while neu:
neu = 0

# Scan all dimensions of pos
ind = where(sum(pos,axis = 2) == 1)
for x,y in zip(ind,ind):
set(x,y,nonzero(pos[x,y,:]),feld,pos,s)

ind = where(sum(pos,axis = 1) == 1)
for x,y in zip(ind,ind):
set(x,nonzero(pos[x,:,y]),y,feld,pos,s)

ind = where(sum(pos,axis = 0) == 1)
for x,y in zip(ind,ind):
set(nonzero(pos[:,x,y]),x,y,feld,pos,s)

# get the box
for x in range(s):
for y in range(s):
col_start = x // s * s
row_start = x % s * s

if sum(pos[col_start:col_start + s,row_start:row_start + s,y]) == 1:
ind = nonzero(pos[col_start:col_start + s,row_start:row_start + s,y]);
neu = set(col_start + ind,row_start + ind,y,feld,pos,s)

## A brute force method to calculate the missing values returns 0 if solved
def brute(feld,pos,s):
# get a starting point with minimum possibilities
n_pos = sum(pos,axis=2)
n_pos[n_pos == 0] = 255
ind = unravel_index(n_pos.argmin(), n_pos.shape)

# trial and error
for wert in nonzero(pos[ind,ind,:]):
try_feld = copy(feld)
try_pos = copy(pos)

try:
set(ind,ind,wert,try_feld,try_pos,s)
anal(try_feld,try_pos,s)

if not done(try_feld,s):
try_feld, try_pos = brute(try_feld,try_pos,s)

if done(try_feld,s):
break
except:
pass
return (try_feld, try_pos)

## main method
if __name__ == "__main__":
if len(sys.argv) != 3:
raise Exception("Give path to input and output file")

anal(feld,pos,s)

if not done(feld,s):
feld,pos = brute(feld,pos,s)

write(sys.argv,feld,s)


• It's just great that I can use Exception to control the program flow. Very nice
• I hope did the file I/O now in a Pythonic way.
• I read that vectorization is good in Python. Should I try to vectorize the code even further?

Correct idiom for if not cond: raise Exception(message) is assert cond, message.
str[ind] is not '0' and str[ind] is not '.' is str[ind] not in '0.'. And don't use is when you mean ==.
Don't ever use bare except:. It catches much more than you think. Either define customized Exceptions subclass, or use assert as I said above.