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)
## Read the file in
def read(name):
with open(name) as fid:
str = fid.read().replace('\n',"")
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
def read_and_construct(name):
str,s = read(name)
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[0],ind[1]):
set(x,y,nonzero(pos[x,y,:])[0][0],feld,pos,s)
ind = where(sum(pos,axis = 1) == 1)
for x,y in zip(ind[0],ind[1]):
set(x,nonzero(pos[x,:,y])[0][0],y,feld,pos,s)
ind = where(sum(pos,axis = 0) == 1)
for x,y in zip(ind[0],ind[1]):
set(nonzero(pos[:,x,y])[0][0],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[0][0],row_start + ind[1][0],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[0],ind[1],:])[0]:
try_feld = copy(feld)
try_pos = copy(pos)
try:
set(ind[0],ind[1],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")
feld, pos, s = read_and_construct(sys.argv[1])
anal(feld,pos,s)
if not done(feld,s):
feld,pos = brute(feld,pos,s)
write(sys.argv[2],feld,s)
Comments on the code:
- 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?