/* Several Solutions to the Queens Problem  Dr Libor Spacek, 21th May 2008

   (allqueens n) returns all solutions but is slow
   (queens n) and (tailqueens n) return one different solution each
   (thequeens n) does no search and is very fast even for large boards

Examples:

    >allqueens 8; // returns all 92 solutions, as a list of lists
    >queens 8;    // gives solution number 52 in the allqueens' list,
    >tailqueens 8; // gives solution no. 89, which is a reflection of no. 52
    >map succ (thequeens 8); // gives solution no. 56 */

// row j in current column not attacked by any queens in preceding columns?
safe _ _ [] = 1;
safe id::int j::int (j2::int:l) =  // id is the column positions difference
        if (j==j2) || (id==j2-j) || (id==j-j2) then 0 else safe (1+id) j l;

allqueens n::int = list (searchall n n []) // returns all possible solutions
  with
        searchall n::int 0 p = p;
   searchall n::int i::int p =
                tuple [searchall n (i-1) (j:p) | j = 1..n; safe 1 j p]
  end;

// the solution is only the rows permutation, without the ordered columns (1..n)
// full 2D board coordinates can be reconstructed with zip (1..n) (queens n);
nullary failed;
queens n::int = list (search n n n [])
  with
   search _ 0 _ p = ();                         // last i, solved
   search _ _ 0 _ = failed;     // failed, run out of alternative js
   search n::int i::int j::int p =
     if (failed === solution) then search n i (j-1) p else j,solution
     when solution = search n (i-1) n (j:p); end if safe 1 j p;
     = search n i (j-1) p              // also try another j when unsafe
  end;
// this concise backtracking tailrecursive version throws a single solution
tailqueens n::int = catch id (srch n n n [])
  with srch _ 0 _ p = throw p;
                 srch _ _ 0 _ = failed;
       srch n::int i::int j::int p = if safe 1 j p then
         ( if failed === (srch n (i-1) n (j:p)) then srch n i (j-1) p else () )
       else srch n i (j-1) p
  end;

/*
thequeens encodes my no search solution, which is to my knowledge the simplest
known algorithm for this problem.
There always exists one fundamental centre-symmetrical solution of this form,
representing an orbit of just 4 reflected solutions, instead of the usual 8.
These few lines of code are self-contained (not calling any square checking).
The solutions had been tested exhaustively for board sizes 0 to 5000 and also
individually for board size 50000x50000.

Row numbering in 'thequeens' is changed for simplicity to 'C style'  0..n-1
Solution using 2D board coordinates (1..n)x(1..n) can be easily reconstructed
with: (fullboard (thequeens n)).
*/

fullboard simple = zip (1..(#simple)) (map succ simple);

nullary nosolution; // returned for n=2 and n=3 when there are no solutions

thequeens n::int = case n of
        1 = [0];  // trivial solution to one square board
        2 | 3 = nosolution;
        n::int = map (newsquare n) (0..(n-1))   // rule for even sized boards n>3
                with newsquare n::int x::int
                        = (start+2*x) mod n if x < halfn; // right start square is crucial
                        = (start2+2*(x-halfn)) mod n // centre reflections fill the 2nd half
                end
                when
        halfn::int = n div 2;  // local variable halfn
        start::int = if (n mod 3) then (halfn-1) else 1;//(n mod 3) is special
        start2::int = n-((start + 2*(halfn-1)) mod n)-1 // start reflections
                end if (n mod 2) == 0;                   // even sized boards finished
    = 0:(map succ (thequeens (n-1))) // corner start 0: solves odd size boards!
end; // end of case and thequeens


// The rest are test utilities for the queens problem:
// checks one queens solution either in 0..7 encoding or in 1..8 encoding.
// returns 1 for a correct result, including "nosolution" for sizes 2 and 3.
// returns 0 if a queen attack exists anywhere in the presented 'solution':
checkqs [] = 1;
checkqs (s::int:l) = if safe 1 s l then checkqs l else 0;
checkqs (nosolution) = 1;

// conducts an exhaustive test of solutions for boards of all listed sizes.
// examples of use: >queenstest (1..1000); >queenstest (5000,4999..4990);
queenstest [] = 1;
queenstest (h:l) = if checkqs (thequeens h) then queenstest l else 0;