cmats ← ##.queens N                         ⍝ The N-Queens Problem

Place  N  Queens on an N×N chessboard such that no queen is attacking any other.
This  coding  returns  all  unique solutions for the given board as a  vector of
character matrices with '⍟' representing a queen. Trivially equivalent solutions
that represent rotations and reflections are omitted.

Technical notes:

There  are  many  ways  to  approach this problem. A simple but rather expensive
method  is  to  generate all permutations (see →pmat←) of ⍳⍵ and then filter out
those that represent a solution. The approach used here is a tree search: First,
place  a queen anywhere in the first row (or "rank" in chess-speak). Next, place
a second  queen in the second rank on any square not attacked  by the first one.
Continue  placing  queens on "safe" squares in successive ranks until either all
ranks have been covered (a configuration that constitutes a solution) or until a
rank  is  reached where all squares are attacked by previously placed queens. In
the  latter  case,  "back track" to the last rank where all squares have not yet
been tried and after moving that queen to a different square, try again.

For  a  standard 8×8 chessboard, there are 96 solutions but these include 2 ref-
lections  and 4 rotations of each "distinct" solution, so in fact there are only
12 interesting solutions. Part of the challenge is to avoid generating or to re-
move these duplicates.

During the search, the current state of play is represented by two vectors, each
item  of which corresponds to a rank. The first is a simple vector of placements
already  made  and the second, a vector of vectors of remaining free squares per
rank.  For  example,  after  placing the first 4 queens, the state of play might
look like this:

    file:    0 1 2 3 4 5 6 7
            ┌───────────────┐   ⍟ placed queen.
    rank: 0 │· ⍟ · · · · · ·│   ○ free square.
          1 │· · · ⍟ · · · ·│
          2 │· · · · · · ⍟ ·│
          3 │⍟ · · · · · · ·│
          4 │· · ○ · · · · ○│
          5 │· · · · ○ ○ · ·│
          6 │· · · · ○ ○ · ·│
          7 │· · ○ · · ○ · ○│
            └───────────────┘

The  vector  of  placements is 1 3 6 0, and the vector of free squares is (2 7),
(4 5)(4 5)(2 5 7). Placing the next queen in column 2, would render the state of
play:

    file:    0 1 2 3 4 5 6 7
            ┌───────────────┐   ⍟ placed queen.
    rank: 0 │· ⍟ · · · · · ·│   ○ free square.
          1 │· · · ⍟ · · · ·│
          2 │· · · · · · ⍟ ·│
          3 │⍟ · · · · · · ·│
          4 │· · ⍟ · · · · ·│
          5 │· · · · ○ ○ · ·│
          6 │· · · · · ○ · ·│
          7 │· · · · · · · ○│
            └───────────────┘

with placement vector: 1 3 6 0 2, and free-square vector: (4 5)(5)(7).

The  search  is implemented by passing the placement vector as left argument and
free-square vector as right argument to inner function [search]: The tree search
consists  of  appending  in  turn,  each  free square from the next rank, to the
placement  vector  and  recursively calling search with a reduced vector of free
squares. Search returns if either any of the  ranks  contain  no  free  squares:
(⊂⍬)∊⍵,  or  no ranks remain: 0=⍴⍵, which constitutes a solution. Notice a small
detail:  the  result  when  there are no free squares must be suitably enclosed:
0⍴⊂⍬,  so that in the three cases where there are no solutions queens¨0 2 3, the
overall result is a vector of _matrices_ in common with all other cases.

    search←{                        ⍝ Search for all solutions.
        (⊂⍬)∊⍵:0⍴⊂⍬                 ⍝ stitched: abandon this branch.
        0=⍴⍵:rmdups ⍺               ⍝ all done: solution!
        hd tl←(⊃⍵)(1↓⍵)             ⍝ head 'n tail of remaining ranks.
        next←⍺∘,¨hd                 ⍝ possible next steps.
        rems←hd free¨⊂tl            ⍝ unchecked squares.
        ↑,/next ∇¨rems              ⍝ ... in following ranks.
    }

[Free]  returns  a new vector of free-squares after those in check from the next
placement have been removed:

    free←{⍵~¨⍺+(⍴⍵)↑cvex}           ⍝ Unchecked squares.

As [cvex], is invariant during the search, it is computed initially as an optim-
isation.

    cvex←(1+⍳⍵)×⊂¯1 0 1             ⍝ Checking vectors.

Given that the final solution vector must contain no duplicates, a further opti-
misation  is to start the tree search from only half of the squares in the first
rank.  Any  solution resulting from a search starting from the remaining squares
must constitute a "reflected" solution. So instead of:

    squares←⍵⍴⊂⍳⍵                   ⍝ (0 1 2 3 4 5 6 7) (0 1 2 3 4 5 6 7) ···

we have:

    squares←(⊂⍳⌈⍵÷2),1↓⍵⍴⊂⍳⍵        ⍝ (0 1 2 3)         (0 1 2 3 4 5 6 7) ···

which _halves_ the total search time.

[rmdups]  discards  "boring"  equivalent solutions by generating all 4 rotations
and  2  reflections  and  then  rejecting  the putative solution if it isn't the
most attractive member of this set:

    rmdups←{                        ⍝ Ignore duplicate solution.
        rots←{{⍒⍵}\4/⊂⍵}            ⍝ 4 rotations.
        refs←{{⍋⍵}\2/⊂⍵}            ⍝ 2 reflections.
        best←{(⊃⍋↑⍵)⊃⍵}             ⍝ best (=lowest) solution.
        all8←,↑refs¨rots ⍵          ⍝ all 8 orientations.
        (⍵≡best all8)⊃⍬(,⊂⍵)        ⍝ ignore if not best.
    }

Finally, [fmt] renders each solution as a character matrix with '·' for an empty
square  and  '⍟' for an occupied one. Each matrix is spread horizontally to make
it appear more square-shaped on the screen.

    fmt←{                           ⍝ Format solution.
        chars←'·⍟'[(↑⍵)∘.=⍳⍺]       ⍝ char array of placed queens.
        expd←1↓,↑⍺⍴⊂0 1             ⍝ expansion mask.
        ↑¨↓↓expd\chars              ⍝ vector of char matrices.
    }

Alternative Codings
-------------------
The  following,  perhaps  rather  opaque  coding, is  remarkable in that it is a
_single expression_  for  the  vector  of unique solutions to the problem in the
same  way  that  {+/⍵÷⍴⍵} is a single expression for the mean value of a numeric
vector.  It  contains  no guards; just applications of operators to operands and
functions to arguments.  The code assumes an index-origin and migration-level of
0.

      {⍵{↑¨↓↓(2|1↓⍳2×⍺)\((↑⍵)∘.=⍳⍺)⊃¨⊂'·⍟'}∪{{(⊃⍋↑⍵)⊃⍵},↑⍵}¨{{⍋⍵}\2/⊂⍵}¨∘{{⍒⍵}\4/⊂⍵}¨⍵{(⍺=,↑⍴¨⍵)/⍵},¨↑⍬{↑(⍺⍺,⍺)∇∇(1↓⍵⍵~¨⍺+(1+⍳⍴⍵⍵)×⊂¯1 0 1)/((⊃⍵⍵)~⍺+¯1 0 1),⊂⍵,⊂⍺⍺,⍺}(1↓⍵⍴⊂⌽⍳⍵)/(⌽⍳⌈⍵÷2),⊂0⍴⊂⍬}

We can prove that it works by pasting it into the session and appending an argu-
ment  (such  as 8) after remembering to set ⎕io ⎕ml←0. To examine it further, we
might  find  it  helpful  to  assign  a name and use the editor to split it up a
little:

    qn←{
        ⍵{
            ↑¨↓↓(2|1↓⍳2×⍺)\((↑⍵)∘.=⍳⍺)⊃¨⊂'·⍟'
        }∪{
            {(⊃⍋↑⍵)⊃⍵},↑⍵
        }¨{
            {⍋⍵}\2/⊂⍵
        }¨∘{
            {⍒⍵}\4/⊂⍵
        }¨⍵{
            (⍺=,↑⍴¨⍵)/⍵
        },¨↑⍬{
            ↑(⍺⍺,⍺)∇∇(1↓⍵⍵~¨⍺+(1+⍳⍴⍵⍵)×⊂¯1 0 1)/((⊃⍵⍵)~⍺+¯1 0 1),⊂⍵,⊂⍺⍺,⍺
        }(1↓⍵⍴⊂⌽⍳⍵)/(⌽⍳⌈⍵÷2),⊂0⍴⊂⍬
    }

The left side (top in the wrapped version) of the expression is much the same as
[queens], except that the slightly shorter and quicker: '·⍟'[(↑⍵)∘.=⍳⍺] has been
replaced by the slightly purer ((↑⍵)∘.=⍳⍺)⊃¨⊂'·⍟', in order to avoid APL's argu-
ably anomalous square-bracket syntax.

The  main  difference  is  in  the  search sub-expression, which uses _reduce /_
rather than _each ¨_ to recur along ranks.

    ↑⍬{
        ↑(⍺⍺,⍺)∇∇(1↓⍵⍵~¨⍺+(1+⍳⍴⍵⍵)×⊂¯1 0 1)/((⊃⍵⍵)~⍺+¯1 0 1),⊂⍵,⊂⍺⍺,⍺
    }(1↓⍵⍴⊂⌽⍳⍵)/(⌽⍳⌈⍵÷2),⊂0⍴⊂⍬

The  operand of the  outer reduction is a function derived from the inner dyadic
operator  whose  left and right array operands are the placement and free-square
vectors respectively:

    ↑···                ⍝ disclose result of reduction.
    ·⍬{                 ⍝ left operand: placement vector.
        ···             ⍝ inner dyadic operator.
    }(1↓⍵⍴⊂⌽⍳⍵)···      ⍝ right operand: free-square vector.
    ···········/···     ⍝ reduction with above derived function.

The  argument  to  the  reduction is the vector of squares in the first rank and
emulates  queens  in representing only half of the files as an optimisation. The
final  item  is  the  vector  of  solutions so-far and is enclosed to ensure the
correct rank for the three cases where there are no solutions.

    ············(⌽⍳⌈⍵÷2),⊂0⍴⊂⍬

The inner dyadic operator then, has operands/arguments:

    Left argument:  next square in current rank.
    Left operand:   current placement vector.
    Right operand:  free-square vector.
    Right argument: vector of solutions-so-far.

This  leaves  just  the  code  _inside_ the operator, which is another reduction
using a recursive call with new values for the left and right operands:

    ↑(⍺⍺,⍺)∇∇(1↓⍵⍵~¨⍺+(1+⍳⍴⍵⍵)×⊂¯1 0 1)/((⊃⍵⍵)~⍺+¯1 0 1),⊂⍵,⊂⍺⍺,⍺

This breaks down as a reduction:

    ↑···································  ⍝ disclose result of reduction.
    ·······∇∇···························  ⍝ recursive call on operator with
    ·(⍺⍺,⍺)·····························  ⍝ operands: extended placement vector
    ·········(1↓⍵⍵~¨⍺+(1+⍳⍴⍵⍵)×⊂¯1 0 1)·  ⍝ and reduced free-square vector.
    ···································/  ⍝ ... reduction.

The  argument  to  the reduction is the vector of solutions-so-far extended with
the new path and prefixed with the vector of remaining squares in the next rank:

    ····································((⊃⍵⍵)~⍺+¯1 0 1),⊂⍵,⊂⍺⍺,⍺

The  new  path:  _⍺⍺,⍺_  does  not  constitute a solution if it's shorter than ⍵
items.  These  'short'  solutions are removed later when full-length vectors are
filtered out by the next function:

        ⍵{                  ⍝ board size.
            (⍺=,↑⍴¨⍵)/⍵     ⍝ filter out full-length solutions.
        }


Roger Hui  suggests  this  coding. Transliterated from J, it finds all solutions
including reflections and rotations:

    queens←{⍵{((+/b)⌿⍵),⍺|(,b)/⍳×/⍴b←~↑(⊂⍳⍺)∊¨(↓⍵)+[0]¨⊂(c-⍳c←1↓⍴⍵)∘.ׯ1 0 1}⍣⍵⍳1 0}

See: http://jsoftware.com/jwiki/Essays/N_Queens_Problem


This coding, which includes reflections and rotations, uses depth-first travers-
al operator →trav←:

    qtrav←{                             ⍝ N-queens using trav DFS.
        subs←{(⊂⍵),¨(⍳⍴⊃⍺)~atk ⍵}       ⍝ sub-trees of tree-node ⍵.
        accm←{⍺,((⍴⍵)=⍴⊃⍺)/⊂⍵}          ⍝ accumlation of N-placement.
        atk←{↑(⊂⍵)+¯1 0 1×⊂⌽⍳⍴⍵}        ⍝ columns under attack in row 1+⍵.
        fmt←{⍵∘.=⍳⍴⍵}                   ⍝ formatted as bool matrix.
        fmt¨(↓0 ⍵⍴0)accm trav subs ⍬    ⍝ formatted solutions.
    }

See also this YouTube video: https://www.youtube.com/watch?v=DsZdfnlh_d0

Examples:

    1 disp∘queens¨0 1 2 3 4 5 6         ⍝ solutions for small boards.
┌───┬───┬─────┬───────┬─────────┬─────────────────────┬─────────────┐
│┌⊖┐│┌→┐│┌⊖──┐│┌⊖────┐│┌→──────┐│┌→────────┬─────────┐│┌→──────────┐│
││ ⌽││⍟↓││   ⌽││     ⌽││· ⍟ · ·│││⍟ · · · ·│· ⍟ · · ·│││· ⍟ · · · ·││
│└⊖┘│└→┘│└──→┘│└────→┘││· · · ⍟│││· · ⍟ · ·│· · · · ⍟│││· · · ⍟ · ·││
│   │   │     │       ││⍟ · · ·│││· · · · ⍟│· · ⍟ · ·│││· · · · · ⍟││
│   │   │     │       ││· · ⍟ ·↓││· ⍟ · · ·│⍟ · · · ·│││⍟ · · · · ·││
│   │   │     │       │└──────→┘││· · · ⍟ ·↓· · · ⍟ ·↓││· · ⍟ · · ·││
│   │   │     │       │         │└────────→┴────────→┘││· · · · ⍟ ·↓│
│   │   │     │       │         │                     │└──────────→┘│
└───┴───┴─────┴───────┴─────────┴─────────────────────┴─────────────┘

    queens 7
┌─────────────┬─────────────┬─────────────┬─────────────┬─────────────┬─────────────┐
│⍟ · · · · · ·│⍟ · · · · · ·│· ⍟ · · · · ·│· ⍟ · · · · ·│· ⍟ · · · · ·│· ⍟ · · · · ·│
│· · ⍟ · · · ·│· · · ⍟ · · ·│· · · ⍟ · · ·│· · · · ⍟ · ·│· · · · ⍟ · ·│· · · · · ⍟ ·│
│· · · · ⍟ · ·│· · · · · · ⍟│⍟ · · · · · ·│⍟ · · · · · ·│· · · · · · ⍟│· · ⍟ · · · ·│
│· · · · · · ⍟│· · ⍟ · · · ·│· · · · · · ⍟│· · · ⍟ · · ·│· · · ⍟ · · ·│· · · · · · ⍟│
│· ⍟ · · · · ·│· · · · · ⍟ ·│· · · · ⍟ · ·│· · · · · · ⍟│⍟ · · · · · ·│· · · ⍟ · · ·│
│· · · ⍟ · · ·│· ⍟ · · · · ·│· · ⍟ · · · ·│· · ⍟ · · · ·│· · ⍟ · · · ·│⍟ · · · · · ·│
│· · · · · ⍟ ·│· · · · ⍟ · ·│· · · · · ⍟ ·│· · · · · ⍟ ·│· · · · · ⍟ ·│· · · · ⍟ · ·│
└─────────────┴─────────────┴─────────────┴─────────────┴─────────────┴─────────────┘

    3 4⍴ queens 8
┌───────────────┬───────────────┬───────────────┬───────────────┐
│⍟ · · · · · · ·│⍟ · · · · · · ·│· ⍟ · · · · · ·│· ⍟ · · · · · ·│
│· · · · ⍟ · · ·│· · · · · ⍟ · ·│· · · ⍟ · · · ·│· · · · ⍟ · · ·│
│· · · · · · · ⍟│· · · · · · · ⍟│· · · · · ⍟ · ·│· · · · · · ⍟ ·│
│· · · · · ⍟ · ·│· · ⍟ · · · · ·│· · · · · · · ⍟│⍟ · · · · · · ·│
│· · ⍟ · · · · ·│· · · · · · ⍟ ·│· · ⍟ · · · · ·│· · ⍟ · · · · ·│
│· · · · · · ⍟ ·│· · · ⍟ · · · ·│⍟ · · · · · · ·│· · · · · · · ⍟│
│· ⍟ · · · · · ·│· ⍟ · · · · · ·│· · · · · · ⍟ ·│· · · · · ⍟ · ·│
│· · · ⍟ · · · ·│· · · · ⍟ · · ·│· · · · ⍟ · · ·│· · · ⍟ · · · ·│
├───────────────┼───────────────┼───────────────┼───────────────┤
│· ⍟ · · · · · ·│· ⍟ · · · · · ·│· ⍟ · · · · · ·│· ⍟ · · · · · ·│
│· · · · ⍟ · · ·│· · · · · ⍟ · ·│· · · · · ⍟ · ·│· · · · · · ⍟ ·│
│· · · · · · ⍟ ·│⍟ · · · · · · ·│· · · · · · · ⍟│· · ⍟ · · · · ·│
│· · · ⍟ · · · ·│· · · · · · ⍟ ·│· · ⍟ · · · · ·│· · · · · ⍟ · ·│
│⍟ · · · · · · ·│· · · ⍟ · · · ·│⍟ · · · · · · ·│· · · · · · · ⍟│
│· · · · · · · ⍟│· · · · · · · ⍟│· · · ⍟ · · · ·│· · · · ⍟ · · ·│
│· · · · · ⍟ · ·│· · ⍟ · · · · ·│· · · · · · ⍟ ·│⍟ · · · · · · ·│
│· · ⍟ · · · · ·│· · · · ⍟ · · ·│· · · · ⍟ · · ·│· · · ⍟ · · · ·│
├───────────────┼───────────────┼───────────────┼───────────────┤
│· ⍟ · · · · · ·│· · ⍟ · · · · ·│· · ⍟ · · · · ·│· · ⍟ · · · · ·│
│· · · · · · ⍟ ·│· · · · ⍟ · · ·│· · · · ⍟ · · ·│· · · · · ⍟ · ·│
│· · · · ⍟ · · ·│· ⍟ · · · · · ·│· · · · · · · ⍟│· ⍟ · · · · · ·│
│· · · · · · · ⍟│· · · · · · · ⍟│· · · ⍟ · · · ·│· · · · ⍟ · · ·│
│⍟ · · · · · · ·│⍟ · · · · · · ·│⍟ · · · · · · ·│· · · · · · · ⍟│
│· · · ⍟ · · · ·│· · · · · · ⍟ ·│· · · · · · ⍟ ·│⍟ · · · · · · ·│
│· · · · · ⍟ · ·│· · · ⍟ · · · ·│· ⍟ · · · · · ·│· · · · · · ⍟ ·│
│· · ⍟ · · · · ·│· · · · · ⍟ · ·│· · · · · ⍟ · ·│· · · ⍟ · · · ·│
└───────────────┴───────────────┴───────────────┴───────────────┘

    {⊃⍴queens ⍵}¨0 to 16            ⍝ Number of solutions for various boards.
0 1 0 0 1 2 1 6 12 46 92 341 1787 9233 45752 285053 1846955

See also: kt sudoku pmat queensX trav X

Back to: contents

Back to: Workspaces