1
\$\begingroup\$

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?
\$\endgroup\$
3
\$\begingroup\$

Correct idiom for if not cond: raise Exception(message) is assert cond, message.

Also, don't use builtin names for something else. When I look at str.strip, I see unbound method. :-(

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.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.