Background

In the classic Eight Queens Problem, the goal is to place eight queens on a chessboard so that none of them is attacking any others. In any solution, each of the eight ranks (i.e., rows) and each of the eight files (i.e., columns) must have exactly one queen and each diagonal must have at most one queen. As illustrated to the right, in chess terminology, ranks are numbered one through eight and files are "numbered" a through h. For convenience, we will use numbers rather than letters to identify files. Also, as is customary in computer science, we will use numbering that begins at zero (rather than one). Distancing ourselves even further from traditional chess terminology, we will use the more familiar terms row and column.
4 | - - Q - -
3 | - - - - Q
2 | - Q - - -
1 | - - - Q -
0 | Q - - - -
  +----------
    0 1 2 3 4

To make matters more interesting, we generalize the Eight Queens Problem to become the N-Queens Problem. That is, we parameterize the problem so that the size of the board is an input rather than being fixed at eight. One solution to the 5-Queens Problem appears to the left.



Your Task

Provided to you is EightQueensBoard, a class whose instances represent chessboards (of any size) containing mutually non-attacking queens. An application that uses it, EightQueensApp, needs to be completed. Specifically, it has two methods, both called printSolutions(), that are stubbed. Completion of either one is sufficient; completing both would be a bonus. Each one is intended to be recursive, meaning that it should include one or more calls to itself. One of the methods receives a parameter that corresponds to a column number; the other receives a row number in addition to a column number. We will focus on the one-argument version for the time being.

Notice that the main() method includes the call printSolutions(0). The result of that call should be that every board configuration corresponding to a solution to the problem has been displayed. For debugging purposes, you probably want your initial version of the method to display not only the solutions but also every board configuration that is reached during the search for solutions. Indeed, displaying a narrative of the actions taken is a good idea. Here is an example of what a run of the application might look like.

Here is pseudocode that expresses the recursive logic of the one-argument version of printSolutions(). The variable N corresponds to the size of the board (i.e., the number of rows/columns).

//pre: Each of columns 0 through col-1 has a queen, constituting a 
//     partial solution.  Columns col through N-1 have no queen.
printSolutions(int col):

  if col == N then 
     display the board, as it is a (complete) solution
  else
     do for each row in range 0..N-1:
        if a queen can be (legally) placed onto (row,col)
           (a) place a queen there 
           (b) make a recursive call to find all (complete) solutions 
               that extend the current partial solution.
           (c) Remove the queen from (row,col)
        fi
     od
  fi

As indicated, the method's precondition is that the state of the board (which in the Java code is represented by a global variable board) is such that it is a (potential, at least) partial solution in which a queen appears in each column numbered less than the incoming parameter, col, while no queens are in columns numbered col or higher. For each square in the current column (i.e., col) in which a queen can be placed legally, the method does so (thereby extending the partial solution to include a queen in column col) and then calls itself recursively to find all solutions that extend it further (by placing queens in columns numbered col+1 and higher).

As for the two-argument version of printSolutions(), the intent is for it to recurse not only upon the column numbers (as does the one-argument version) but also upon the row numbers. Specifically, the iteration over the row numbers that is accomplished via a loop in the one-argument version is accomplished in the two-argument version using recursion. Here is pseudocode:

//pre: Each of columns 0 through col-1 has a queen, constituting a 
//     partial solution.  (Columns col through N-1 have no queen.)
//     All solutions that extend the partial solution represented by
//     the queens in columns 0 through col-1 by placing a queen in
//     (r,col), for r < row, already have been found.
printSolutions(int col, int row):

  if col == N then 
     display the board, as it is a (complete) solution
  else if row == N then
     do nothing; all solutions that extend the partial solution
     given by the queens in columns 0..col-1 have been found
  else  // 0 <= row < N and 0 <= col < N
     if a queen can be (legally) placed onto (row,col)
        (a) place a queen there 
        (b) make a recursive call to find all (complete) solutions 
            that extend the current partial solution (by placing
            queens into remaining columns).
        (c) Remove the queen from (row,col)
     fi
     make a recursive call that finds all (complete) solutions that
     extend the current partial solution (by placing a queen in 
     a higher-numbered row in the current column).
  fi

Of course, some of the decisions made in designing these methods were arbitrary. For example, one could have designed the methods in such a way that the roles played by rows and columns were reversed. Or one could have designed them to place queens into the columns starting at the highest-numbered column and going towards zero, rather than the other way around. If you prefer to change either method in such a way, feel free to do so.