In [16]:
# Encoding a (partial) solution to n-queens.
# qsofar: an array of (up to) 8 elements, one for
# each column. The value indicates the "row" of the
# queen on this column.
def print_qsofar(qsofar, n=8):
    rows = [['.']*n for _ in range(n)]
    for (pcol,prow) in enumerate(qsofar):
        rows[prow][pcol] = 'Q'
    for row in rows:
        print(''.join(row))
print_qsofar([4,6,1])
........
..Q.....
........
........
Q.......
........
.Q......
........
In [11]:
def queen_safe(row, qsofar):
    """ returns True if it's safe to place the next queen in `row`, 
        in the next column `len(qsofar)`, given the current placement
        of queens in qsofar"""
    col = len(qsofar)
    for (pcol,prow) in enumerate(qsofar):
        # Check vertical attack
        pass # Nothing to check, col is already a new column
        # Check horizontal attack
        if row == prow: return False
        # Check diagonal attack
        # 1,1    conflicts  2,2
        if abs(col - pcol) == abs(row - prow): return False
    # No conflict found
    return True
queen_safe(7, [0,2,4,1])
Out[11]:
True
In [13]:
def nqueens(qsofar, n=4):
    col = len(qsofar)
    if col == n: return qsofar
    for row in range(n):
        if queen_safe(row, qsofar):
            print(f'Placing queen at {row} on to {qsofar}')
            print_qsofar(qsofar+[row], n)
            sol = nqueens(qsofar + [row])
            if sol is not None: return sol
    return None
nqueens([])
Placing queen at 0 on to []
Q...
....
....
....
Placing queen at 2 on to [0]
Q...
....
.Q..
....
Placing queen at 3 on to [0]
Q...
....
....
.Q..
Placing queen at 1 on to [0, 3]
Q...
..Q.
....
.Q..
Placing queen at 1 on to []
....
Q...
....
....
Placing queen at 3 on to [1]
....
Q...
....
.Q..
Placing queen at 0 on to [1, 3]
..Q.
Q...
....
.Q..
Placing queen at 2 on to [1, 3, 0]
..Q.
Q...
...Q
.Q..
Out[13]:
[1, 3, 0, 2]