Eight Queens Puzzle by Magnus Lie Hetland

Anton Vredegoor anton at vredegoor.doge.nl
Wed Aug 20 06:51:30 EDT 2003


bokr at oz.net (Bengt Richter) wrote:

>Obviously (after seeing your code and on thinking ;-) a board has two sides with
>four rotational positions each, for a total of 8 == 1 identity plus 7 T's ;-/ )

There is also something with this puzzle that makes me think of the
"getting a submatrix of all true" thread. A solution there was a
combination of the columns, here a solution is a permutation of
range(len(columns)). Below I'm giving a possible way to solve the
puzzle using this observation, but maybe someone can see a way to
improve it.

Anton

#permqueens.py
from sets import Set

def unique(n):
    """ nqueens solver filtered for solutions that
        are rotations or reflections """
    seen = Set()
    for sol in nqueens(n):
        if sol not in seen:
            m = mirrors(sol)
            seen |= Set(m)
            yield min(m)

def asqueens(sol):
    """ ascii-art representation of a solution """
    R,res = range(len(sol)),""
    Q,n = zip(R,sol[::-1]),len(sol)
    for i in R:
        for j in R:
            res  += '+Q'[(n-j-1,n-i-1) in Q]+ ' '
        res += '\n'
    return res

def nqueens(n):
    """ iterative nqueens solver using permutations """
    QC,R = queencovers(n),range(n)
    for i in xrange(fac(n)):
        p = indexedperm(i,R)
        if checksol(p,QC):   yield tuple(p)

def mirrors(sol):
    """ a list of mirror images of a solution """
    def flip(x): return x[::-1]
    def transpose(x):
        xx = list(x)
        for i,j in enumerate(x):  xx[j] = i
        return tuple(xx)
    f,t = flip(sol),transpose(sol)
    ft,tf = flip(t),transpose(f)
    ftf,tft = flip(tf),transpose(ft)
    ftft = flip(tft)
    return [sol,f,t,ft,tf,ftf,tft,ftft]

def queencovers(n):
    """ a dictionary of the positions that are covered by
        queens on all board positions on an nxn board """
    board,queens = [divmod(i,n) for i in range(n*n)],{}
    for pos in board:  queens[pos] = Set(cover(pos,board))
    return queens

def fac(n): 
    """ faculty of n """
    return reduce(lambda x,y:x*y,range(2,n+1),1L)

def indexedperm(m,L):
    """ permutations of list L by index """
    n,res,T = len(L),L[:],L[:]
    for i in range(n):
        m,d = divmod(m,n-i)
        res[i] = T.pop(d)
    return res

def checksol(sol,QC):
    """ a solution is a permutation of a range, to convert it to
        a list of queen positions it is zipped with a range, a 
        solution is true if no queen threatens the other queens """
    n = len(sol)
    #first a little optimization, queens cannot be adjacent
    for i in range(n-1):
        if abs(sol[i]-sol[i+1]) == 1:  return False
    queens = Set(zip(range(n),sol))
    #check if no queen threatens another queen
    for queen in queens:
        if queens & QC[queen]: return False
    return True
    
def cover((i,j),board):
    """ positions that are covered by queen(i,j),
        without (i,j) itself """
    def keep((x,y)): 
        if (i,j) == (x,y) : return False
        return i==x or j==y or abs(x-i)==abs(y-j)
    return filter(keep,board)

def test():
    """ test the unique nqueens solver """
    n = 8
    for i,sol in enumerate(unique(n)):
            print i, sol
            print asqueens(sol)

if __name__=='__main__':
    test()






More information about the Python-list mailing list