CS140 -- Lab 10 (Spring 2021)


Inspiration

This problem dates back to Max Bezzel in 1848, and it has drawn the interest of many mathematicians since then.

N queens is also a nice example of using recursion to implement a depth-first backtracking algorithm (similar to Sudoku, which we will cover next week as another example) with a few tricks can avoid the entire search space. For example, for an 8x8 board there are over 4.4 ** billion ** possible arrangements (64 chose 8) but if we chose one queen per column (row) it reduces to over 16.7 million (8^8).

It also is a good example where using more efficient coding (inc. what is often called branch and bound) will substancially reduce the overall run time.

Part 1: Enumerating all n-queens boards with one queen per column

Develop a simple C++ program called "nqueens.cpp" that takes a single command line argument, which will be size of the chess board.

The n-queens problem is pretty simple. We want to find boards of size n x n with a total of n queens on it with a wrinkle: none of the queens on these boards threaten each other. This means there is no other queen in the same row, nor the same column, and there are no queens on either diagonal relative to the all of the n queens placed.

You must implement a simple C++ function called n queens with the following parameters:

  1. A 1D array/vector called Board passed by reference
  2. The current column to place a queen
  3. The board size provided to the main program

As alluded to above, we will implement the more efficient version where each element of the 1D vector is the row-position of a queen at that specific column. For example, if board[2] is 5, that means there is a queen in the third column in the 6th row (assuming we count from 0)

The simplest recursive/backtracking solution calls the nqueens function as follows from main:

nqueens (Board, 0, size);

And there are two conditions in the brute-force version to consider:

  1. The column (initially 0) is less than the board size, implying we still need to place queens. You can do by using the following recursive call:
    for (int i = 0; i < size; i++) {
    
       board[pos] = i;
       nqueens (board, pos + 1, size);
    
    }
  2. The column provided equals size. In this case you need to check to make sure the board is valid, and display it if it is indeed valid.

For example, calling your program as follows:

./nqueens 8

should output the following:

0, 4, 7, 5, 2, 6, 1, 3
0, 5, 7, 2, 6, 3, 1, 4
0, 6, 3, 5, 7, 1, 4, 2
0, 6, 4, 7, 1, 3, 5, 2
... (there are 92 total, of which 4 have queens at 0,0)


Hint: You can compute the difference between the rows of two queens (aka the values) and the difference between the columns of the queens (aka the indices of the array) to help locate other queens on the same diagonal. Draw it out to see what we mean.

Part 2: Limiting the search space

The simple recursive backtracking above will enumerate (and check) all of the possibilities. This is over 16.7 million for a board of size 8, which even with a relatively inefficient check takes under a second on a tesla lab machine. It will work less well, however, with larger boards. As a concrete example, a 10x10 board generates over 1.41 billion possibilties and checking each of these takes around 7 minutes on tesla1.

Create a second version of nqueens (called nqueens2.cpp) that will only check additonal columns if the current board is valid. For example, using my code above in the second iteration the function will happily place another queen down in the first row (0) even though the prior iteration already placed a row there. Please count the total number of boards checked and display that to standard error (so make test will still work) so you can submit this to us in the report.

Extra credit

Because this is the week that spans the spring recess, there will be no extra credit or challenges this week.

Rubric

+ 2 nqueens.cpp and nqueens2.cpp are well formatted, commented (inc. name, assignment, 
and overview), with reasonable variable names
+8 Finds all 92 solutions for n=8 based on make test
+8 Finds all 724 solutions for n=10 based on make test
+9 Implements constrained backtracking for part 2
+8 Report (report.txt) has the requested values. Namely:
- # of Boards considered with contraint(s) for n = 8
- # of Boards considered with contraint(s) for n = 10
- In your own words, what is the benefit of a recursive solution to
enumerate all of the nqueens potential solutions (2 points)
- Also in your own words, what are the theoretical and/or practical
advantages of constraining the search space to only still valid 
solutions?

If you have implemented only contrained backtracking we'll accept it for make test if you make a note of it in the report.txt **BUT ** it must output the solutions in the same order as our reference answers.

Testing your code prior to submission

To faciliate testing, you were previously asked to clone the course Github repository as follows:

git clone https://github.com/semrich/CS140-21.git cs140

For this assignment, update this clone by using the following:

git pull

Submission

Name your solution to part 1 "nqueens.cpp," the solution to part 2, "nqueens2.cpp." Name your report "report.txt."

Then, please run the following command prior to submission:

tar -cvf lab10.tar nqueens.cpp nqueens2.cpp report.txt

Note: Although submission will be faciliated by Canvas, we will compile and test on EECS lab machines!

If you develop your solution elsewhere please make sure it works on the lab computers prior to the deadline.