threatless_queens.py

#!/usr/bin/env python
#
# Find positions of queens on chess-board so they will not threaten each other
#
# Author:  Yotam Medini  [email protected] -- Created: 2006/June/18
#

import sys
import string


def next_permutation(a):
    n = len(a)
    # Find last increasing pair
    i = n - 2
    while i >= 0 and a[i] >= a[i+1]:
        i -= 1
    if i < 0:
        a = None  # a is decreasing, no more permutations
    else:
        # Find last greater than a[i].
        # There must be one! since a[i] < a[i+1].
        j = n - 1
        while a[i] >= a[j]:
            j -= 1
        # and swap
        t = a[i];  a[i] = a[j];  a[j] = t

        # Mirror the tail.
        tail = a[i + 1:]
        tail.reverse();  # Python is strong
        a = a[: i + 1] + tail
    return a


# Check if (x0,y0) and  (x1,y1)  squares  on same '/' diagonal
def same_diagonal_SW2NE(x0, y0, x1, y1):
    same = (x0 - y0) == (x1 - y1)
    return same


# Check if (x0,y0) and  (x1,y1)  squares  on same '\' diagonal
def same_diagonal_SE2NW(x0, y0, x1, y1):
    same = (x0 + y0) == (x1 + y1)
    return same


# Check if (x0,y0) and  (x1,y1)  squares  on common diagonal
def same_diagonal(x0, y0, x1, y1):
    same = (same_diagonal_SW2NE(x0, y0, x1, y1) or
            same_diagonal_SE2NW(x0, y0, x1, y1))
    return same


# Assume  a  holds a permutation of chess rows.
# If a = [a0, a1, a2, ..., a{n-1}]  then the squares are:
# (0,a0),  (1,a1), (2,a2) ..., (n-1,a{n-1})
# The function checks if there are no pair of squares that
# queens would threaten each other.
def is_queen_solution(a):
    no_threat = True
    size = len(a)
    xShva = 0 # First queen
    while no_threat and xShva < size:
        yShva = a[xShva]
        xEster = xShva + 1  # Second queen
        while no_threat and xEster < size:
            yEster = a[xEster]
            no_threat = not same_diagonal(xShva, yShva, xEster, yEster)
            xEster += 1
        xShva += 1
    return no_threat


# Assume rows is a sequence of rows for each column.
# Return string of chess squares
# For example:  if  rows = [0, 2, 1] v then we will return  "a1 b3 c2"
def rows2chess_str(rows):
    abc = string.ascii_lowercase;  # "abc...z"  but I am lazy
    nlc = len(abc);                # This is 26 duh! but let Python count...
    s = ""
    size = len(rows)
    if size < nlc:  #  8 < 26   So the default case will be nice
        for c in range(0, size):
            colrow = "%s%d" % (abc[c], rows[c] + 1)
            s += colrow + ' '  #  add square
    else:
        # We give up letter notation
        for c in range(0, size):
            s += "(%d,%d) " % (c, rows[c])
    s = s[:-1]  # Remove last unwanted space
    return s


# main
size = 8; # default
if len(sys.argv) == 2:
    size = int(sys.argv[1])
n_solutions = 0
permutation = range(0, size);  # 1st increasing permutation - I like Python
while permutation != None:
    if is_queen_solution(permutation):
        chess_squares = rows2chess_str(permutation)
        sys.stdout.write("[%3d]:  %s\n" % (n_solutions, chess_squares))
        n_solutions += 1
    permutation = next_permutation(permutation)
sys.stdout.write("Total: %d solutions\n" % n_solutions)
sys.exit(0)

Generated by GNU enscript 1.6.4.