Solve the eight queens puzzle.
You can extend the problem to solve the puzzle with a board of side NxN. For the number of solutions for small values of N, see oeis.org
// Much shorter than the version below; // uses C++11 threads to parallelize the computation; also uses backtracking // Outputs all solutions for any table size #include <vector> #include <iostream> #include <iomanip> #include <thread> #include <future> // Print table. 'pos' is a vector of positions – the index in pos is the row, // and the number at that index is the column where the queen is placed. static void print(const std::vector<int> &pos) { // print table header for (int i = 0; i < pos.size(); i++) { std::cout << std::setw(3) << char('a' + i); } std::cout << '\n'; for (int row = 0; row < pos.size(); row++) { int col = pos[row]; std::cout << row + 1 << std::setw(3 * col + 3) << " # "; std::cout << '\n'; } std::cout << "\n\n"; } static bool threatens(int row_a, int col_a, int row_b, int col_b) { return row_a == row_b // same row or col_a == col_b // same column or std::abs(row_a - row_b) == std::abs(col_a - col_b); // diagonal } // the i-th queen is in the i-th row // we only check rows up to end_idx // so that the same function can be used for backtracking and checking the final solution static bool good(const std::vector<int> &pos, int end_idx) { for (int row_a = 0; row_a < end_idx; row_a++) { for (int row_b = row_a + 1; row_b < end_idx; row_b++) { int col_a = pos[row_a]; int col_b = pos[row_b]; if (threatens(row_a, col_a, row_b, col_b)) { return false; } } } return true; } static std::mutex print_count_mutex; // mutex protecting 'n_sols' static int n_sols = 0; // number of solutions // recursive DFS backtracking solver static void n_queens(std::vector<int> &pos, int index) { // if we have placed a queen in each row (i. e. we are at a leaf of the search tree), check solution and return if (index >= pos.size()) { if (good(pos, index)) { std::lock_guard<std::mutex> lock(print_count_mutex); print(pos); n_sols++; } return; } // backtracking step if (not good(pos, index)) { return; } // optimization: the first level of the search tree is parallelized if (index == 0) { std::vector<std::future<void>> fts; for (int col = 0; col < pos.size(); col++) { pos[index] = col; auto ft = std::async(std::launch::async, [=]{ auto cpos(pos); n_queens(cpos, index + 1); }); fts.push_back(std::move(ft)); } for (const auto &ft : fts) { ft.wait(); } } else { // deeper levels are not for (int col = 0; col < pos.size(); col++) { pos[index] = col; n_queens(pos, index + 1); } } } int main() { std::vector<int> start(12); // 12: table size n_queens(start, 0); std::cout << n_sols << " solutions found.\n"; return 0; }
- Output:
Output for N = 4:
a b c d 1 # 2 # 3 # 4 # a b c d 1 # 2 # 3 # 4 #
// A straight-forward brute-force C++ version with formatted output, // eschewing obfuscation and C-isms, producing ALL solutions, which // works on any OS with a text terminal. // // Two basic optimizations are applied: // // It uses backtracking to only construct potentially valid solutions. // // It only computes half the solutions by brute -- once we get the // queen halfway across the top row, any remaining solutions must be // reflections of the ones already computed. // // This is a bare-bones example, without any progress feedback or output // formatting controls, which a more complete program might provide. // // Beware that computing anything larger than N=14 might take a while. // (Time gets exponentially worse the higher the number.) // Copyright 2014 Michael Thomas Greer // Distributed under the Boost Software License, Version 1.0. // http://www.boost.org/LICENSE_1_0.txt #include <algorithm> #include <ciso646> #include <iomanip> #include <iostream> #include <set> #include <sstream> #include <stdexcept> #include <string> #include <vector> // /////////////////////////////////////////////////////////////////////////// struct queens /////////////////////////////////////////////////////////////////////////// // { // TYPES ------------------------------------------------------------------- // A row or column index. (May be signed or unsigned.) // typedef signed char index_type; // A 'solution' is a row --> column lookup of queens on the board. // // It has lexicographical order and can be transformed with a variety of // reflections, which, when properly combined, produce all possible // orientations of a solution. // struct solution_type: std::vector <index_type> { typedef std::vector <index_type> base_type; // constructors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . solution_type( std::size_t N ): base_type( N, -1 ) { } solution_type( const solution_type& s ): base_type( s ) { } // compare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . bool operator < ( const solution_type& s ) const { auto mm = std::mismatch( begin(), end(), s.begin() ); return (mm.first != end()) and (*mm.first < *mm.second); } // transformations . . . . . . . . . . . . . . . . . . . . . . . . . . . . void vflip() { std::reverse( begin(), end() ); } void hflip() { for (auto& x : *this) x = size() - 1 - x; } void transpose() { solution_type result( size() ); for (index_type y = 0; (std::size_t)y < size(); y++) result[ (*this)[ y ] ] = y; swap( result ); } }; // MEMBER VALUES ----------------------------------------------------------- const int N; std::set <solution_type> solutions; // SOLVER ------------------------------------------------------------------ queens( int N = 8 ): N( (N < 0) ? 0 : N ) { // Row by row we create a potentially valid solution. // If a queen can be placed in a valid spot by the time // we get to the last row, then we've found a solution. solution_type solution( N ); index_type row = 0; while (true) { // Advance the queen along the row ++solution[ row ]; // (If we get past halfway through the first row, we're done.) if ((row == 0) and (solution[ 0 ] > N/2)) break; if (solution[ row ] < N) { // If the queen is in a good spot... if (ok( solution, row, solution[ row ] )) { // ...and we're on the last row if (row == N-1) { // Add the solution we found plus all it's reflections solution_type s = solution; solutions.insert( s ); s.vflip(); solutions.insert( s ); s.hflip(); solutions.insert( s ); s.vflip(); solutions.insert( s ); s.transpose(); solutions.insert( s ); s.vflip(); solutions.insert( s ); s.hflip(); solutions.insert( s ); s.vflip(); solutions.insert( s ); } // otherwise begin marching a queen along the next row else solution[ ++row ] = -1; } // When we get to the end of a row's columns then // we need to backup a row and continue from there. } else --row; } } // HELPER ------------------------------------------------------------------ // This routine helps the solver by identifying column locations // that do not conflict with queens already placed in prior rows. bool ok( const solution_type& columns, index_type row, index_type column ) { for (index_type r = 0; r < row; r++) { index_type c = columns[ r ]; index_type delta_row = row - r; index_type delta_col = (c < column) ? (column - c) : (c - column); if ((c == column) or (delta_row == delta_col)) return false; } return true; } // OUTPUT A SINGLE SOLUTION ------------------------------------------------ // // Formatted as (for example): // // d1 b2 g3 c4 f5 h6 e7 a8 // Q - - - - - - - // - - - - Q - - - // - - - - - - - Q // - - - - - Q - - // - - Q - - - - - // - - - - - - Q - // - Q - - - - - - // - - - Q - - - - // friend std::ostream& operator << ( std::ostream& outs, const queens::solution_type& solution ) { static const char* squares[] = { "- ", "Q " }; index_type N = solution.size(); // Display the queen positions for (auto n = N; n--; ) outs << (char)('a' + solution[ n ]) << (N - n) << " "; // Display the board for (auto queen : solution) { outs << "\n"; for (index_type col = 0; col < N; col++) outs << squares[ col == queen ]; } return outs; } // OUTPUT ALL SOLUTIONS ---------------------------------------------------- // // Display "no solutions" or "N solutions" followed by // each individual solution, separated by blank lines. friend std::ostream& operator << ( std::ostream& outs, const queens& q ) { if (q.solutions.empty()) outs << "no"; else outs << q.solutions.size(); outs << " solutions"; std::size_t n = 1; for (auto solution : q.solutions) { outs << "\n\n#" << n++ << "\n" << solution; } return outs; } }; /* /////////////////////////////////////////////////////////////////////////// string_to <type> ( x ) /////////////////////////////////////////////////////////////////////////// */ template <typename T> T string_to( const std::string& s ) { T result; std::istringstream ss( s ); ss >> result; if (!ss.eof()) throw std::runtime_error( "to_string(): invalid conversion" ); return result; } template <typename T, T default_value> T string_to( const std::string& s ) { try { return string_to <T> ( s ); } catch (...) { return default_value; } } /* /////////////////////////////////////////////////////////////////////////// main program /////////////////////////////////////////////////////////////////////////// */ int usage( const std::string& name ) { std::cerr << "usage:\n " << name << " 8\n\n" "" "Solve the N-Queens problem, brute-force,\n" "and show all solutions for an 8x8 board.\n\n" "" "(Specify a value other than 8 for the board size you want.)\n"; return 1; } int main( int argc, char** argv ) { signed N = (argc < 2) ? 8 : (argc > 2) ? 0 : string_to <signed, 0> ( argv[ 1 ] ); if (N <= 0) return usage( argv[ 0 ] ); std::cout << queens( N ) << "\n"; }
- Output:
for N=4:
2 solutions #1 c1 a2 d3 b4 - Q - - - - - Q Q - - - - - Q - #2 b1 d2 a3 c4 - - Q - Q - - - - - - Q - Q - -
The Windows-only, obfuscated versions previously found on this page:
#include <windows.h> #include <iostream> #include <string> //-------------------------------------------------------------------------------------------------- using namespace std; //-------------------------------------------------------------------------------------------------- class point { public: int x, y; point(){ x = y = 0; } void set( int a, int b ){ x = a; y = b; } }; //-------------------------------------------------------------------------------------------------- class nQueens { public: void solve( int c ) { _count = c; int len = ( c + 1 ) * ( c + 1 ); _queens = new bool[len]; memset( _queens, 0, len ); _cl = new bool[c]; memset( _cl, 0, c ); _ln = new bool[c]; memset( _ln, 0, c ); point pt; pt.set( rand() % c, rand() % c ); putQueens( pt, c ); displayBoard(); delete [] _queens; delete [] _ln; delete [] _cl; } private: void displayBoard() { system( "cls" ); string t = "+---+", q = "| Q |", s = "| |"; COORD c = { 0, 0 }; HANDLE h = GetStdHandle( STD_OUTPUT_HANDLE ); for( int y = 0, cy = 0; y < _count; y++ ) { int yy = y * _count; for( int x = 0; x < _count; x++ ) { SetConsoleCursorPosition( h, c ); cout << t; c.Y++; SetConsoleCursorPosition( h, c ); if( _queens[x + yy] ) cout << q; else cout << s; c.Y++; SetConsoleCursorPosition( h, c ); cout << t; c.Y = cy; c.X += 4; } cy += 2; c.X = 0; c.Y = cy; } } bool checkD( int x, int y, int a, int b ) { if( x < 0 || y < 0 || x >= _count || y >= _count ) return true; if( _queens[x + y * _count] ) return false; if( checkD( x + a, y + b, a, b ) ) return true; return false; } bool check( int x, int y ) { if( _ln[y] || _cl[x] ) return false; if( !checkD( x, y, -1, -1 ) ) return false; if( !checkD( x, y, 1, -1 ) ) return false; if( !checkD( x, y, -1, 1 ) ) return false; if( !checkD( x, y, 1, 1 ) ) return false; return true; } bool putQueens( point pt, int cnt ) { int it = _count; while( it ) { if( !cnt ) return true; if( check( pt.x, pt.y ) ) { _queens[pt.x + pt.y * _count] = _cl[pt.x] = _ln[pt.y] = true; point tmp = pt; if( ++tmp.x >= _count ) tmp.x = 0; if( ++tmp.y >= _count ) tmp.y = 0; if( putQueens( tmp, cnt - 1 ) ) return true; _queens[pt.x + pt.y * _count] = _cl[pt.x] = _ln[pt.y] = false; } if( ++pt.x >= _count ) pt.x = 0; it--; } return false; } int _count; bool* _queens, *_ln, *_cl; }; //-------------------------------------------------------------------------------------------------- int main( int argc, char* argv[] ) { nQueens n; int nq; while( true ) { system( "cls" ); cout << "Enter board size bigger than 3 (0 - 3 to QUIT): "; cin >> nq; if( nq < 4 ) return 0; n.solve( nq ); cout << endl << endl; system( "pause" ); } return 0; } //--------------------------------------------------------------------------------------------------
- Output:
+---+---+---+---+---+ +---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+---+---+---+ | Q | | | | | | | | | Q | | | | | | | | | | | | | Q | | | | +---+---+---+---+---+ +---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+---+---+---+ | | | Q | | | | | Q | | | | | | | | | | | | | | | | | Q | | +---+---+---+---+---+ +---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+---+---+---+ | | | | | Q | | | | | | | | | Q | | | | | Q | | | | | | | | +---+---+---+---+---+ +---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+---+---+---+ | | Q | | | | | | | | | | Q | | | | | Q | | | | | | | | | | +---+---+---+---+---+ +---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+---+---+---+ | | | | Q | | | Q | | | | | | | | | | | | | Q | | | | | | | +---+---+---+---+---+ +---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+---+---+---+ | | | Q | | | | | | | | | | | | | | | | | Q | +---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+---+---+---+ | | | | | Q | | | | | Q | | | | | | | | | | | +---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+---+---+---+ | | | | | | | Q | | | | | Q | | | | | | | | | +---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+---+---+---+ | | | | | | Q | | | | | | +---+---+---+---+---+---+---+---+---+---+---+ | | | | | | | | | Q | | | +---+---+---+---+---+---+---+---+---+---+---+ | | | | | | | Q | | | | | +---+---+---+---+---+---+---+---+---+---+---+
Version using Heuristics – explained here: Solution_construction
#include <windows.h> #include <iostream> #include <string> #include <vector> #include <algorithm> //-------------------------------------------------------------------------------------------------- using namespace std; //-------------------------------------------------------------------------------------------------- typedef unsigned int uint; //-------------------------------------------------------------------------------------------------- class nQueens_Heuristic { public: void solve( uint n ) { makeList( n ); drawBoard( n ); } private: void drawBoard( uint n ) { system( "cls" ); string t = "+---+", q = "| Q |", s = "| |"; COORD c = { 0, 0 }; HANDLE h = GetStdHandle( STD_OUTPUT_HANDLE ); uint w = 0; for( uint y = 0, cy = 0; y < n; y++ ) { for( uint x = 0; x < n; x++ ) { SetConsoleCursorPosition( h, c ); cout << t; c.Y++; SetConsoleCursorPosition( h, c ); if( x + 1 == solution[w] ) cout << q; else cout << s; c.Y++; SetConsoleCursorPosition( h, c ); cout << t; c.Y = cy; c.X += 4; } cy += 2; c.X = 0; c.Y = cy; w++; } solution.clear(); odd.clear(); evn.clear(); } void makeList( uint n ) { uint r = n % 6; for( uint x = 1; x <= n; x++ ) { if( x & 1 ) odd.push_back( x ); else evn.push_back( x ); } if( r == 2 ) { swap( odd[0], odd[1] ); odd.erase( find( odd.begin(), odd.end(), 5 ) ); odd.push_back( 5 ); } else if( r == 3 ) { odd.erase( odd.begin() ); odd.erase( odd.begin() ); odd.push_back( 1 ); odd.push_back( 3 ); evn.erase( evn.begin() ); evn.push_back( 2 ); } vector<uint>::iterator it = evn.begin(); while( it != evn.end() ) { solution.push_back( ( *it ) ); it++; } it = odd.begin(); while( it != odd.end() ) { solution.push_back( ( *it ) ); it++; } } vector<uint> odd, evn, solution; }; //-------------------------------------------------------------------------------------------------- int main( int argc, char* argv[] ) { uint n; nQueens_Heuristic nQH; while( true ) { cout << "Enter board size bigger than 3 (0 - 3 to QUIT): "; cin >> n; if( n < 4 ) return 0; nQH.solve( n ); cout << endl << endl; } return 0; } //--------------------------------------------------------------------------------------------------
Content is available under GNU Free Documentation License 1.2.