N-Queens Recursive Implementation in C++11











up vote
8
down vote

favorite
1












I created a recursive, optimized, N-Queens implementation in CPP. Any suggestions for improvement?



/*
* Filename : queens.cpp
* Author : Kevin F.
* License : CopyLeft, Feb 2017
* Eight Queens : Print solutions which have N non-attacking queens placed on NxN chess board
* Input : Adjust BOARD_SIZE to change the size of the N
* Output : ASCII printed board and runtime statistics
* Requires : C++11
* Compilation : g++ -std=c++11 -O3 -o queens queens.cpp
* Source : http://pastebin.com/qy483BEi
* Speed : Found: 92 solutions in: 0.002582 seconds using: 2747 recursions
* Without Opt : Found: 92 solutions in: 0.132239 seconds using: 139049 recursions
*/

#include <algorithm>
#include <iostream>
#include <set>

#define BOARD_SIZE 8

using namespace std;

struct queen_t {
uint8_t x;
uint8_t y;
};

static vector< vector<queen_t> > _boards;
static uint32_t _num_recursions;

/* Forward declare for validate_and_continue */
void recurse(vector<queen_t> *board, set<uint8_t> *avail_x,
set<uint8_t> *avail_y, uint16_t cur_iteration);

void print_board(vector<queen_t> *board){

/* Sample Output
* |12345678
* 1|xxxxxQxx
* 2|xxxQxxxx
* 3|xxxxxxQx
* 4|Qxxxxxxx
* 5|xxxxxxxQ
* 6|xQxxxxxx
* 7|xxxxQxxx
* 8|xxQxxxxx
*/

/* Generate empty board */
vector<string> rows = {" |"};
for(int i=1; i <= BOARD_SIZE; i++){
rows[0].append(to_string(i));
rows.push_back(to_string(i) + "|" + string(BOARD_SIZE, 'x'));
}

/* Fill boards with queens */
for_each(board->begin(), board->end(), [&](const queen_t queen){
rows[queen.y].replace(queen.x+1, 1, "Q"); // +1 to skip #|
});

for(int i=0; i <= BOARD_SIZE; i++){
cout << rows[i] << endl;
}
}

/*
* Slope can be calculated as rise/run = (Y2-Y1)/(X2-X1)
* A slope of 1 (perfect diagonal) implies (Y2-Y1) = (X2-X1)
*/
bool diagonal_slope(const queen_t *first_queen, const queen_t *second_queen){

/* May be sent empty queens */
if (first_queen->x == 0 || second_queen->x == 0){
return false;
}

return abs(second_queen->y - first_queen->y) == abs(second_queen->x - first_queen->x);
}

/* Return true when new queen(s) trigger a diagonal attack */
bool can_attack(const vector<queen_t> *board, const queen_t *first_queen,
const queen_t *second_queen){

for(const queen_t &queen : *board){
if (diagonal_slope(first_queen, &queen) == true ||
diagonal_slope(&queen, second_queen) == true){
return true;
}
}

return diagonal_slope(first_queen, second_queen);
}

/* If 0..2 queens added are valid, continue recursion */
void validate_and_continue(vector<queen_t> *board, set<uint8_t> *avail_x,
set<uint8_t> *avail_y, uint16_t cur_iteration,
queen_t first_queen, queen_t second_queen){

/* Early prune any entries that produce diagonal attacks */
if (can_attack(board, &first_queen, &second_queen) == true){
return;
}

/* Update availability lists and create a new board */
set<uint8_t> new_avail_x = *avail_x;
set<uint8_t> new_avail_y = *avail_y;
vector<queen_t> new_board = *board;

if (first_queen.x != 0){
new_avail_x.erase(first_queen.x);
new_avail_y.erase(first_queen.y);
new_board.push_back(first_queen);
}

if (second_queen.x != 0){
new_avail_x.erase(second_queen.x);
new_avail_y.erase(second_queen.y);
new_board.push_back(second_queen);
}

/* Recurse with new modified copy of data */
recurse(&new_board, &new_avail_x, &new_avail_y, cur_iteration + 1);
}

/* Iterate through 1..BOARD_SIZE queens based on X,Y availability lists */
void recurse(vector<queen_t> *board, set<uint8_t> *avail_x,
set<uint8_t> *avail_y, uint16_t cur_iteration) {

//cout << "CurIter: " << cur_iteration << ", queens placed: " << board->size() << endl;
_num_recursions ++;

/* Completion conditions */
uint16_t queens_left = BOARD_SIZE - board->size();
if (cur_iteration > BOARD_SIZE){
if (queens_left == 0) {
if (avail_x->size() != 0 || avail_y->size() != 0){
cout << "ERROR: Board is full, but avail lists are non empty!" << endl;
print_board(board);
exit(1);
}else{
//cout << "Adding solution, recursions so far: " << _num_recursions << endl;
_boards.push_back(*board);
}
}
return;
}

/* Current iteration is now available for X and Y positions */
avail_x->insert(cur_iteration);
avail_y->insert(cur_iteration);

uint16_t rounds_left = BOARD_SIZE - cur_iteration + 1; // including this round
uint16_t queens_to_add = 0;
if (queens_left >= (rounds_left * 2)) {
/* Optimize for perfect 2 and skip 3+ saves ~5000 recursions */
queens_to_add = ceil((double)queens_left / rounds_left);
} else if (queens_left > ((rounds_left - 1) * 2)) {
/* Avoid adding 0 queens this round saves ~500 recursions */
queens_to_add = 1;
}

/* Add 0, 1, or 2 queens this round */
for (queens_to_add; queens_to_add <= min(queens_left, (uint16_t)2); queens_to_add ++) {
switch (queens_to_add) {
case 1:
/* Possible (X,cur_iteration) pairs */
for_each(avail_x->begin(), avail_x->end(), [&](const uint8_t cur_x) {
validate_and_continue(board, avail_x, avail_y,
cur_iteration, {cur_x, (uint8_t)cur_iteration}, {});
});

/* Possible (cur_iteration,Y) pairs */
for_each(avail_y->begin(), avail_y->end(), [&](const uint8_t cur_y) {
/* Equivalent to X = cur_iteration case above */
if (cur_y == cur_iteration) {
return;
}

validate_and_continue(board, avail_x, avail_y,
cur_iteration, {(uint8_t)cur_iteration, cur_y}, {});
});
break;
case 2:
/*
* Example outcomes for cur_iteration = 2
* x = 1, y = 1... queens 1,2 and 2,1 ** valid
* x = 1, y = 2....queens 1,2 and 2,2 ** skip in Y
* x = 2, y = 1... queens 2,2 and 2,1 ** skip in X
* x = 2, y = 2... queens 2,2 and 2,2 ** skip in X
*/

for_each(avail_x->begin(), avail_x->end(), [&](const uint8_t cur_x) {
/* First queen would generate a diagonal */
if (cur_x == cur_iteration) {
return;
}

/* Possible (cur_iteration,Y) pairs */
for_each(avail_y->begin(), avail_y->end(), [&](const uint8_t cur_y) {

/* Second queen would generate a diagonal */
if (cur_y == cur_iteration) {
return;
}

validate_and_continue(board,
avail_x, avail_y, cur_iteration,
{cur_x, (uint8_t)cur_iteration},
{(uint8_t)cur_iteration, cur_y});
});
});
break;
default:
/* Place 0 queens this round, impossible >2 without collision */
validate_and_continue(board, avail_x, avail_y,
cur_iteration, {}, {});
break;
}
}
}

int main(int argc, char *argv){

cout << "Calculating solutions..." << endl;

vector<queen_t> board;
set<uint8_t> avail_x;
set<uint8_t> avail_y;
clock_t start_time = clock();

/*
* Recurse through possible outcomes that wont attack in row/column
* Validate each placed queen against diagonal attacks via slope = 1
*/
recurse(&board, &avail_x, &avail_y, 1);

cout << "Found: " << _boards.size() << " solutions in: " <<
((double_t)(clock() - start_time)/CLOCKS_PER_SEC) <<
" seconds using: " << _num_recursions << " recursions" << endl;

print_board(&(*_boards.begin()));
}


On my fast workstation (vs slower HTPC), it completes in 0.001345 seconds (for Eight queens), so speed is not the concern. Code cohesiveness, clarity, code shrink, C++11isms, commenting, naming, optimizations, etc ? Can you get a lower number of recursions while maintaining the performance?



Thanks!



I updated the code per the link in title block, but left original here for readability of answers as per codereview.stackexchange.com policy.










share|improve this question
























  • I would suggest solving for a board of size 13x13 (or larger). Using an 8x8 board to determine performance isn't good because any program will solve that in much less than 1 second. I modified your program to solve a 13x13 board and it took 24.5 seconds. I took the program from my answer here to a recent n-queens question and it took 1 second. BTW your program used 9073263 recursions to solve 13x13 and the program I linked used 4601178 (around half).
    – JS1
    Feb 22 '17 at 21:07












  • interesting. thanks for the comparison. I notice the time it takes explodes around 12-14. It will be fun to narrow down the root cause.
    – kevinf
    Feb 22 '17 at 23:39






  • 1




    Don't edit the code in your post after people have answered. If you want further review, you should post a new question, possibly linking back to this one for context. Please rollback the edit so that the answers make sense once again.
    – Toby Speight
    Feb 23 '17 at 13:38










  • @Toby Speight thx for heads up. At least the updated code compiles :)
    – kevinf
    Feb 23 '17 at 21:49















up vote
8
down vote

favorite
1












I created a recursive, optimized, N-Queens implementation in CPP. Any suggestions for improvement?



/*
* Filename : queens.cpp
* Author : Kevin F.
* License : CopyLeft, Feb 2017
* Eight Queens : Print solutions which have N non-attacking queens placed on NxN chess board
* Input : Adjust BOARD_SIZE to change the size of the N
* Output : ASCII printed board and runtime statistics
* Requires : C++11
* Compilation : g++ -std=c++11 -O3 -o queens queens.cpp
* Source : http://pastebin.com/qy483BEi
* Speed : Found: 92 solutions in: 0.002582 seconds using: 2747 recursions
* Without Opt : Found: 92 solutions in: 0.132239 seconds using: 139049 recursions
*/

#include <algorithm>
#include <iostream>
#include <set>

#define BOARD_SIZE 8

using namespace std;

struct queen_t {
uint8_t x;
uint8_t y;
};

static vector< vector<queen_t> > _boards;
static uint32_t _num_recursions;

/* Forward declare for validate_and_continue */
void recurse(vector<queen_t> *board, set<uint8_t> *avail_x,
set<uint8_t> *avail_y, uint16_t cur_iteration);

void print_board(vector<queen_t> *board){

/* Sample Output
* |12345678
* 1|xxxxxQxx
* 2|xxxQxxxx
* 3|xxxxxxQx
* 4|Qxxxxxxx
* 5|xxxxxxxQ
* 6|xQxxxxxx
* 7|xxxxQxxx
* 8|xxQxxxxx
*/

/* Generate empty board */
vector<string> rows = {" |"};
for(int i=1; i <= BOARD_SIZE; i++){
rows[0].append(to_string(i));
rows.push_back(to_string(i) + "|" + string(BOARD_SIZE, 'x'));
}

/* Fill boards with queens */
for_each(board->begin(), board->end(), [&](const queen_t queen){
rows[queen.y].replace(queen.x+1, 1, "Q"); // +1 to skip #|
});

for(int i=0; i <= BOARD_SIZE; i++){
cout << rows[i] << endl;
}
}

/*
* Slope can be calculated as rise/run = (Y2-Y1)/(X2-X1)
* A slope of 1 (perfect diagonal) implies (Y2-Y1) = (X2-X1)
*/
bool diagonal_slope(const queen_t *first_queen, const queen_t *second_queen){

/* May be sent empty queens */
if (first_queen->x == 0 || second_queen->x == 0){
return false;
}

return abs(second_queen->y - first_queen->y) == abs(second_queen->x - first_queen->x);
}

/* Return true when new queen(s) trigger a diagonal attack */
bool can_attack(const vector<queen_t> *board, const queen_t *first_queen,
const queen_t *second_queen){

for(const queen_t &queen : *board){
if (diagonal_slope(first_queen, &queen) == true ||
diagonal_slope(&queen, second_queen) == true){
return true;
}
}

return diagonal_slope(first_queen, second_queen);
}

/* If 0..2 queens added are valid, continue recursion */
void validate_and_continue(vector<queen_t> *board, set<uint8_t> *avail_x,
set<uint8_t> *avail_y, uint16_t cur_iteration,
queen_t first_queen, queen_t second_queen){

/* Early prune any entries that produce diagonal attacks */
if (can_attack(board, &first_queen, &second_queen) == true){
return;
}

/* Update availability lists and create a new board */
set<uint8_t> new_avail_x = *avail_x;
set<uint8_t> new_avail_y = *avail_y;
vector<queen_t> new_board = *board;

if (first_queen.x != 0){
new_avail_x.erase(first_queen.x);
new_avail_y.erase(first_queen.y);
new_board.push_back(first_queen);
}

if (second_queen.x != 0){
new_avail_x.erase(second_queen.x);
new_avail_y.erase(second_queen.y);
new_board.push_back(second_queen);
}

/* Recurse with new modified copy of data */
recurse(&new_board, &new_avail_x, &new_avail_y, cur_iteration + 1);
}

/* Iterate through 1..BOARD_SIZE queens based on X,Y availability lists */
void recurse(vector<queen_t> *board, set<uint8_t> *avail_x,
set<uint8_t> *avail_y, uint16_t cur_iteration) {

//cout << "CurIter: " << cur_iteration << ", queens placed: " << board->size() << endl;
_num_recursions ++;

/* Completion conditions */
uint16_t queens_left = BOARD_SIZE - board->size();
if (cur_iteration > BOARD_SIZE){
if (queens_left == 0) {
if (avail_x->size() != 0 || avail_y->size() != 0){
cout << "ERROR: Board is full, but avail lists are non empty!" << endl;
print_board(board);
exit(1);
}else{
//cout << "Adding solution, recursions so far: " << _num_recursions << endl;
_boards.push_back(*board);
}
}
return;
}

/* Current iteration is now available for X and Y positions */
avail_x->insert(cur_iteration);
avail_y->insert(cur_iteration);

uint16_t rounds_left = BOARD_SIZE - cur_iteration + 1; // including this round
uint16_t queens_to_add = 0;
if (queens_left >= (rounds_left * 2)) {
/* Optimize for perfect 2 and skip 3+ saves ~5000 recursions */
queens_to_add = ceil((double)queens_left / rounds_left);
} else if (queens_left > ((rounds_left - 1) * 2)) {
/* Avoid adding 0 queens this round saves ~500 recursions */
queens_to_add = 1;
}

/* Add 0, 1, or 2 queens this round */
for (queens_to_add; queens_to_add <= min(queens_left, (uint16_t)2); queens_to_add ++) {
switch (queens_to_add) {
case 1:
/* Possible (X,cur_iteration) pairs */
for_each(avail_x->begin(), avail_x->end(), [&](const uint8_t cur_x) {
validate_and_continue(board, avail_x, avail_y,
cur_iteration, {cur_x, (uint8_t)cur_iteration}, {});
});

/* Possible (cur_iteration,Y) pairs */
for_each(avail_y->begin(), avail_y->end(), [&](const uint8_t cur_y) {
/* Equivalent to X = cur_iteration case above */
if (cur_y == cur_iteration) {
return;
}

validate_and_continue(board, avail_x, avail_y,
cur_iteration, {(uint8_t)cur_iteration, cur_y}, {});
});
break;
case 2:
/*
* Example outcomes for cur_iteration = 2
* x = 1, y = 1... queens 1,2 and 2,1 ** valid
* x = 1, y = 2....queens 1,2 and 2,2 ** skip in Y
* x = 2, y = 1... queens 2,2 and 2,1 ** skip in X
* x = 2, y = 2... queens 2,2 and 2,2 ** skip in X
*/

for_each(avail_x->begin(), avail_x->end(), [&](const uint8_t cur_x) {
/* First queen would generate a diagonal */
if (cur_x == cur_iteration) {
return;
}

/* Possible (cur_iteration,Y) pairs */
for_each(avail_y->begin(), avail_y->end(), [&](const uint8_t cur_y) {

/* Second queen would generate a diagonal */
if (cur_y == cur_iteration) {
return;
}

validate_and_continue(board,
avail_x, avail_y, cur_iteration,
{cur_x, (uint8_t)cur_iteration},
{(uint8_t)cur_iteration, cur_y});
});
});
break;
default:
/* Place 0 queens this round, impossible >2 without collision */
validate_and_continue(board, avail_x, avail_y,
cur_iteration, {}, {});
break;
}
}
}

int main(int argc, char *argv){

cout << "Calculating solutions..." << endl;

vector<queen_t> board;
set<uint8_t> avail_x;
set<uint8_t> avail_y;
clock_t start_time = clock();

/*
* Recurse through possible outcomes that wont attack in row/column
* Validate each placed queen against diagonal attacks via slope = 1
*/
recurse(&board, &avail_x, &avail_y, 1);

cout << "Found: " << _boards.size() << " solutions in: " <<
((double_t)(clock() - start_time)/CLOCKS_PER_SEC) <<
" seconds using: " << _num_recursions << " recursions" << endl;

print_board(&(*_boards.begin()));
}


On my fast workstation (vs slower HTPC), it completes in 0.001345 seconds (for Eight queens), so speed is not the concern. Code cohesiveness, clarity, code shrink, C++11isms, commenting, naming, optimizations, etc ? Can you get a lower number of recursions while maintaining the performance?



Thanks!



I updated the code per the link in title block, but left original here for readability of answers as per codereview.stackexchange.com policy.










share|improve this question
























  • I would suggest solving for a board of size 13x13 (or larger). Using an 8x8 board to determine performance isn't good because any program will solve that in much less than 1 second. I modified your program to solve a 13x13 board and it took 24.5 seconds. I took the program from my answer here to a recent n-queens question and it took 1 second. BTW your program used 9073263 recursions to solve 13x13 and the program I linked used 4601178 (around half).
    – JS1
    Feb 22 '17 at 21:07












  • interesting. thanks for the comparison. I notice the time it takes explodes around 12-14. It will be fun to narrow down the root cause.
    – kevinf
    Feb 22 '17 at 23:39






  • 1




    Don't edit the code in your post after people have answered. If you want further review, you should post a new question, possibly linking back to this one for context. Please rollback the edit so that the answers make sense once again.
    – Toby Speight
    Feb 23 '17 at 13:38










  • @Toby Speight thx for heads up. At least the updated code compiles :)
    – kevinf
    Feb 23 '17 at 21:49













up vote
8
down vote

favorite
1









up vote
8
down vote

favorite
1






1





I created a recursive, optimized, N-Queens implementation in CPP. Any suggestions for improvement?



/*
* Filename : queens.cpp
* Author : Kevin F.
* License : CopyLeft, Feb 2017
* Eight Queens : Print solutions which have N non-attacking queens placed on NxN chess board
* Input : Adjust BOARD_SIZE to change the size of the N
* Output : ASCII printed board and runtime statistics
* Requires : C++11
* Compilation : g++ -std=c++11 -O3 -o queens queens.cpp
* Source : http://pastebin.com/qy483BEi
* Speed : Found: 92 solutions in: 0.002582 seconds using: 2747 recursions
* Without Opt : Found: 92 solutions in: 0.132239 seconds using: 139049 recursions
*/

#include <algorithm>
#include <iostream>
#include <set>

#define BOARD_SIZE 8

using namespace std;

struct queen_t {
uint8_t x;
uint8_t y;
};

static vector< vector<queen_t> > _boards;
static uint32_t _num_recursions;

/* Forward declare for validate_and_continue */
void recurse(vector<queen_t> *board, set<uint8_t> *avail_x,
set<uint8_t> *avail_y, uint16_t cur_iteration);

void print_board(vector<queen_t> *board){

/* Sample Output
* |12345678
* 1|xxxxxQxx
* 2|xxxQxxxx
* 3|xxxxxxQx
* 4|Qxxxxxxx
* 5|xxxxxxxQ
* 6|xQxxxxxx
* 7|xxxxQxxx
* 8|xxQxxxxx
*/

/* Generate empty board */
vector<string> rows = {" |"};
for(int i=1; i <= BOARD_SIZE; i++){
rows[0].append(to_string(i));
rows.push_back(to_string(i) + "|" + string(BOARD_SIZE, 'x'));
}

/* Fill boards with queens */
for_each(board->begin(), board->end(), [&](const queen_t queen){
rows[queen.y].replace(queen.x+1, 1, "Q"); // +1 to skip #|
});

for(int i=0; i <= BOARD_SIZE; i++){
cout << rows[i] << endl;
}
}

/*
* Slope can be calculated as rise/run = (Y2-Y1)/(X2-X1)
* A slope of 1 (perfect diagonal) implies (Y2-Y1) = (X2-X1)
*/
bool diagonal_slope(const queen_t *first_queen, const queen_t *second_queen){

/* May be sent empty queens */
if (first_queen->x == 0 || second_queen->x == 0){
return false;
}

return abs(second_queen->y - first_queen->y) == abs(second_queen->x - first_queen->x);
}

/* Return true when new queen(s) trigger a diagonal attack */
bool can_attack(const vector<queen_t> *board, const queen_t *first_queen,
const queen_t *second_queen){

for(const queen_t &queen : *board){
if (diagonal_slope(first_queen, &queen) == true ||
diagonal_slope(&queen, second_queen) == true){
return true;
}
}

return diagonal_slope(first_queen, second_queen);
}

/* If 0..2 queens added are valid, continue recursion */
void validate_and_continue(vector<queen_t> *board, set<uint8_t> *avail_x,
set<uint8_t> *avail_y, uint16_t cur_iteration,
queen_t first_queen, queen_t second_queen){

/* Early prune any entries that produce diagonal attacks */
if (can_attack(board, &first_queen, &second_queen) == true){
return;
}

/* Update availability lists and create a new board */
set<uint8_t> new_avail_x = *avail_x;
set<uint8_t> new_avail_y = *avail_y;
vector<queen_t> new_board = *board;

if (first_queen.x != 0){
new_avail_x.erase(first_queen.x);
new_avail_y.erase(first_queen.y);
new_board.push_back(first_queen);
}

if (second_queen.x != 0){
new_avail_x.erase(second_queen.x);
new_avail_y.erase(second_queen.y);
new_board.push_back(second_queen);
}

/* Recurse with new modified copy of data */
recurse(&new_board, &new_avail_x, &new_avail_y, cur_iteration + 1);
}

/* Iterate through 1..BOARD_SIZE queens based on X,Y availability lists */
void recurse(vector<queen_t> *board, set<uint8_t> *avail_x,
set<uint8_t> *avail_y, uint16_t cur_iteration) {

//cout << "CurIter: " << cur_iteration << ", queens placed: " << board->size() << endl;
_num_recursions ++;

/* Completion conditions */
uint16_t queens_left = BOARD_SIZE - board->size();
if (cur_iteration > BOARD_SIZE){
if (queens_left == 0) {
if (avail_x->size() != 0 || avail_y->size() != 0){
cout << "ERROR: Board is full, but avail lists are non empty!" << endl;
print_board(board);
exit(1);
}else{
//cout << "Adding solution, recursions so far: " << _num_recursions << endl;
_boards.push_back(*board);
}
}
return;
}

/* Current iteration is now available for X and Y positions */
avail_x->insert(cur_iteration);
avail_y->insert(cur_iteration);

uint16_t rounds_left = BOARD_SIZE - cur_iteration + 1; // including this round
uint16_t queens_to_add = 0;
if (queens_left >= (rounds_left * 2)) {
/* Optimize for perfect 2 and skip 3+ saves ~5000 recursions */
queens_to_add = ceil((double)queens_left / rounds_left);
} else if (queens_left > ((rounds_left - 1) * 2)) {
/* Avoid adding 0 queens this round saves ~500 recursions */
queens_to_add = 1;
}

/* Add 0, 1, or 2 queens this round */
for (queens_to_add; queens_to_add <= min(queens_left, (uint16_t)2); queens_to_add ++) {
switch (queens_to_add) {
case 1:
/* Possible (X,cur_iteration) pairs */
for_each(avail_x->begin(), avail_x->end(), [&](const uint8_t cur_x) {
validate_and_continue(board, avail_x, avail_y,
cur_iteration, {cur_x, (uint8_t)cur_iteration}, {});
});

/* Possible (cur_iteration,Y) pairs */
for_each(avail_y->begin(), avail_y->end(), [&](const uint8_t cur_y) {
/* Equivalent to X = cur_iteration case above */
if (cur_y == cur_iteration) {
return;
}

validate_and_continue(board, avail_x, avail_y,
cur_iteration, {(uint8_t)cur_iteration, cur_y}, {});
});
break;
case 2:
/*
* Example outcomes for cur_iteration = 2
* x = 1, y = 1... queens 1,2 and 2,1 ** valid
* x = 1, y = 2....queens 1,2 and 2,2 ** skip in Y
* x = 2, y = 1... queens 2,2 and 2,1 ** skip in X
* x = 2, y = 2... queens 2,2 and 2,2 ** skip in X
*/

for_each(avail_x->begin(), avail_x->end(), [&](const uint8_t cur_x) {
/* First queen would generate a diagonal */
if (cur_x == cur_iteration) {
return;
}

/* Possible (cur_iteration,Y) pairs */
for_each(avail_y->begin(), avail_y->end(), [&](const uint8_t cur_y) {

/* Second queen would generate a diagonal */
if (cur_y == cur_iteration) {
return;
}

validate_and_continue(board,
avail_x, avail_y, cur_iteration,
{cur_x, (uint8_t)cur_iteration},
{(uint8_t)cur_iteration, cur_y});
});
});
break;
default:
/* Place 0 queens this round, impossible >2 without collision */
validate_and_continue(board, avail_x, avail_y,
cur_iteration, {}, {});
break;
}
}
}

int main(int argc, char *argv){

cout << "Calculating solutions..." << endl;

vector<queen_t> board;
set<uint8_t> avail_x;
set<uint8_t> avail_y;
clock_t start_time = clock();

/*
* Recurse through possible outcomes that wont attack in row/column
* Validate each placed queen against diagonal attacks via slope = 1
*/
recurse(&board, &avail_x, &avail_y, 1);

cout << "Found: " << _boards.size() << " solutions in: " <<
((double_t)(clock() - start_time)/CLOCKS_PER_SEC) <<
" seconds using: " << _num_recursions << " recursions" << endl;

print_board(&(*_boards.begin()));
}


On my fast workstation (vs slower HTPC), it completes in 0.001345 seconds (for Eight queens), so speed is not the concern. Code cohesiveness, clarity, code shrink, C++11isms, commenting, naming, optimizations, etc ? Can you get a lower number of recursions while maintaining the performance?



Thanks!



I updated the code per the link in title block, but left original here for readability of answers as per codereview.stackexchange.com policy.










share|improve this question















I created a recursive, optimized, N-Queens implementation in CPP. Any suggestions for improvement?



/*
* Filename : queens.cpp
* Author : Kevin F.
* License : CopyLeft, Feb 2017
* Eight Queens : Print solutions which have N non-attacking queens placed on NxN chess board
* Input : Adjust BOARD_SIZE to change the size of the N
* Output : ASCII printed board and runtime statistics
* Requires : C++11
* Compilation : g++ -std=c++11 -O3 -o queens queens.cpp
* Source : http://pastebin.com/qy483BEi
* Speed : Found: 92 solutions in: 0.002582 seconds using: 2747 recursions
* Without Opt : Found: 92 solutions in: 0.132239 seconds using: 139049 recursions
*/

#include <algorithm>
#include <iostream>
#include <set>

#define BOARD_SIZE 8

using namespace std;

struct queen_t {
uint8_t x;
uint8_t y;
};

static vector< vector<queen_t> > _boards;
static uint32_t _num_recursions;

/* Forward declare for validate_and_continue */
void recurse(vector<queen_t> *board, set<uint8_t> *avail_x,
set<uint8_t> *avail_y, uint16_t cur_iteration);

void print_board(vector<queen_t> *board){

/* Sample Output
* |12345678
* 1|xxxxxQxx
* 2|xxxQxxxx
* 3|xxxxxxQx
* 4|Qxxxxxxx
* 5|xxxxxxxQ
* 6|xQxxxxxx
* 7|xxxxQxxx
* 8|xxQxxxxx
*/

/* Generate empty board */
vector<string> rows = {" |"};
for(int i=1; i <= BOARD_SIZE; i++){
rows[0].append(to_string(i));
rows.push_back(to_string(i) + "|" + string(BOARD_SIZE, 'x'));
}

/* Fill boards with queens */
for_each(board->begin(), board->end(), [&](const queen_t queen){
rows[queen.y].replace(queen.x+1, 1, "Q"); // +1 to skip #|
});

for(int i=0; i <= BOARD_SIZE; i++){
cout << rows[i] << endl;
}
}

/*
* Slope can be calculated as rise/run = (Y2-Y1)/(X2-X1)
* A slope of 1 (perfect diagonal) implies (Y2-Y1) = (X2-X1)
*/
bool diagonal_slope(const queen_t *first_queen, const queen_t *second_queen){

/* May be sent empty queens */
if (first_queen->x == 0 || second_queen->x == 0){
return false;
}

return abs(second_queen->y - first_queen->y) == abs(second_queen->x - first_queen->x);
}

/* Return true when new queen(s) trigger a diagonal attack */
bool can_attack(const vector<queen_t> *board, const queen_t *first_queen,
const queen_t *second_queen){

for(const queen_t &queen : *board){
if (diagonal_slope(first_queen, &queen) == true ||
diagonal_slope(&queen, second_queen) == true){
return true;
}
}

return diagonal_slope(first_queen, second_queen);
}

/* If 0..2 queens added are valid, continue recursion */
void validate_and_continue(vector<queen_t> *board, set<uint8_t> *avail_x,
set<uint8_t> *avail_y, uint16_t cur_iteration,
queen_t first_queen, queen_t second_queen){

/* Early prune any entries that produce diagonal attacks */
if (can_attack(board, &first_queen, &second_queen) == true){
return;
}

/* Update availability lists and create a new board */
set<uint8_t> new_avail_x = *avail_x;
set<uint8_t> new_avail_y = *avail_y;
vector<queen_t> new_board = *board;

if (first_queen.x != 0){
new_avail_x.erase(first_queen.x);
new_avail_y.erase(first_queen.y);
new_board.push_back(first_queen);
}

if (second_queen.x != 0){
new_avail_x.erase(second_queen.x);
new_avail_y.erase(second_queen.y);
new_board.push_back(second_queen);
}

/* Recurse with new modified copy of data */
recurse(&new_board, &new_avail_x, &new_avail_y, cur_iteration + 1);
}

/* Iterate through 1..BOARD_SIZE queens based on X,Y availability lists */
void recurse(vector<queen_t> *board, set<uint8_t> *avail_x,
set<uint8_t> *avail_y, uint16_t cur_iteration) {

//cout << "CurIter: " << cur_iteration << ", queens placed: " << board->size() << endl;
_num_recursions ++;

/* Completion conditions */
uint16_t queens_left = BOARD_SIZE - board->size();
if (cur_iteration > BOARD_SIZE){
if (queens_left == 0) {
if (avail_x->size() != 0 || avail_y->size() != 0){
cout << "ERROR: Board is full, but avail lists are non empty!" << endl;
print_board(board);
exit(1);
}else{
//cout << "Adding solution, recursions so far: " << _num_recursions << endl;
_boards.push_back(*board);
}
}
return;
}

/* Current iteration is now available for X and Y positions */
avail_x->insert(cur_iteration);
avail_y->insert(cur_iteration);

uint16_t rounds_left = BOARD_SIZE - cur_iteration + 1; // including this round
uint16_t queens_to_add = 0;
if (queens_left >= (rounds_left * 2)) {
/* Optimize for perfect 2 and skip 3+ saves ~5000 recursions */
queens_to_add = ceil((double)queens_left / rounds_left);
} else if (queens_left > ((rounds_left - 1) * 2)) {
/* Avoid adding 0 queens this round saves ~500 recursions */
queens_to_add = 1;
}

/* Add 0, 1, or 2 queens this round */
for (queens_to_add; queens_to_add <= min(queens_left, (uint16_t)2); queens_to_add ++) {
switch (queens_to_add) {
case 1:
/* Possible (X,cur_iteration) pairs */
for_each(avail_x->begin(), avail_x->end(), [&](const uint8_t cur_x) {
validate_and_continue(board, avail_x, avail_y,
cur_iteration, {cur_x, (uint8_t)cur_iteration}, {});
});

/* Possible (cur_iteration,Y) pairs */
for_each(avail_y->begin(), avail_y->end(), [&](const uint8_t cur_y) {
/* Equivalent to X = cur_iteration case above */
if (cur_y == cur_iteration) {
return;
}

validate_and_continue(board, avail_x, avail_y,
cur_iteration, {(uint8_t)cur_iteration, cur_y}, {});
});
break;
case 2:
/*
* Example outcomes for cur_iteration = 2
* x = 1, y = 1... queens 1,2 and 2,1 ** valid
* x = 1, y = 2....queens 1,2 and 2,2 ** skip in Y
* x = 2, y = 1... queens 2,2 and 2,1 ** skip in X
* x = 2, y = 2... queens 2,2 and 2,2 ** skip in X
*/

for_each(avail_x->begin(), avail_x->end(), [&](const uint8_t cur_x) {
/* First queen would generate a diagonal */
if (cur_x == cur_iteration) {
return;
}

/* Possible (cur_iteration,Y) pairs */
for_each(avail_y->begin(), avail_y->end(), [&](const uint8_t cur_y) {

/* Second queen would generate a diagonal */
if (cur_y == cur_iteration) {
return;
}

validate_and_continue(board,
avail_x, avail_y, cur_iteration,
{cur_x, (uint8_t)cur_iteration},
{(uint8_t)cur_iteration, cur_y});
});
});
break;
default:
/* Place 0 queens this round, impossible >2 without collision */
validate_and_continue(board, avail_x, avail_y,
cur_iteration, {}, {});
break;
}
}
}

int main(int argc, char *argv){

cout << "Calculating solutions..." << endl;

vector<queen_t> board;
set<uint8_t> avail_x;
set<uint8_t> avail_y;
clock_t start_time = clock();

/*
* Recurse through possible outcomes that wont attack in row/column
* Validate each placed queen against diagonal attacks via slope = 1
*/
recurse(&board, &avail_x, &avail_y, 1);

cout << "Found: " << _boards.size() << " solutions in: " <<
((double_t)(clock() - start_time)/CLOCKS_PER_SEC) <<
" seconds using: " << _num_recursions << " recursions" << endl;

print_board(&(*_boards.begin()));
}


On my fast workstation (vs slower HTPC), it completes in 0.001345 seconds (for Eight queens), so speed is not the concern. Code cohesiveness, clarity, code shrink, C++11isms, commenting, naming, optimizations, etc ? Can you get a lower number of recursions while maintaining the performance?



Thanks!



I updated the code per the link in title block, but left original here for readability of answers as per codereview.stackexchange.com policy.







c++ c++11 recursion chess n-queens






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Jul 22 at 23:19

























asked Feb 22 '17 at 20:32









kevinf

1437




1437












  • I would suggest solving for a board of size 13x13 (or larger). Using an 8x8 board to determine performance isn't good because any program will solve that in much less than 1 second. I modified your program to solve a 13x13 board and it took 24.5 seconds. I took the program from my answer here to a recent n-queens question and it took 1 second. BTW your program used 9073263 recursions to solve 13x13 and the program I linked used 4601178 (around half).
    – JS1
    Feb 22 '17 at 21:07












  • interesting. thanks for the comparison. I notice the time it takes explodes around 12-14. It will be fun to narrow down the root cause.
    – kevinf
    Feb 22 '17 at 23:39






  • 1




    Don't edit the code in your post after people have answered. If you want further review, you should post a new question, possibly linking back to this one for context. Please rollback the edit so that the answers make sense once again.
    – Toby Speight
    Feb 23 '17 at 13:38










  • @Toby Speight thx for heads up. At least the updated code compiles :)
    – kevinf
    Feb 23 '17 at 21:49


















  • I would suggest solving for a board of size 13x13 (or larger). Using an 8x8 board to determine performance isn't good because any program will solve that in much less than 1 second. I modified your program to solve a 13x13 board and it took 24.5 seconds. I took the program from my answer here to a recent n-queens question and it took 1 second. BTW your program used 9073263 recursions to solve 13x13 and the program I linked used 4601178 (around half).
    – JS1
    Feb 22 '17 at 21:07












  • interesting. thanks for the comparison. I notice the time it takes explodes around 12-14. It will be fun to narrow down the root cause.
    – kevinf
    Feb 22 '17 at 23:39






  • 1




    Don't edit the code in your post after people have answered. If you want further review, you should post a new question, possibly linking back to this one for context. Please rollback the edit so that the answers make sense once again.
    – Toby Speight
    Feb 23 '17 at 13:38










  • @Toby Speight thx for heads up. At least the updated code compiles :)
    – kevinf
    Feb 23 '17 at 21:49
















I would suggest solving for a board of size 13x13 (or larger). Using an 8x8 board to determine performance isn't good because any program will solve that in much less than 1 second. I modified your program to solve a 13x13 board and it took 24.5 seconds. I took the program from my answer here to a recent n-queens question and it took 1 second. BTW your program used 9073263 recursions to solve 13x13 and the program I linked used 4601178 (around half).
– JS1
Feb 22 '17 at 21:07






I would suggest solving for a board of size 13x13 (or larger). Using an 8x8 board to determine performance isn't good because any program will solve that in much less than 1 second. I modified your program to solve a 13x13 board and it took 24.5 seconds. I took the program from my answer here to a recent n-queens question and it took 1 second. BTW your program used 9073263 recursions to solve 13x13 and the program I linked used 4601178 (around half).
– JS1
Feb 22 '17 at 21:07














interesting. thanks for the comparison. I notice the time it takes explodes around 12-14. It will be fun to narrow down the root cause.
– kevinf
Feb 22 '17 at 23:39




interesting. thanks for the comparison. I notice the time it takes explodes around 12-14. It will be fun to narrow down the root cause.
– kevinf
Feb 22 '17 at 23:39




1




1




Don't edit the code in your post after people have answered. If you want further review, you should post a new question, possibly linking back to this one for context. Please rollback the edit so that the answers make sense once again.
– Toby Speight
Feb 23 '17 at 13:38




Don't edit the code in your post after people have answered. If you want further review, you should post a new question, possibly linking back to this one for context. Please rollback the edit so that the answers make sense once again.
– Toby Speight
Feb 23 '17 at 13:38












@Toby Speight thx for heads up. At least the updated code compiles :)
– kevinf
Feb 23 '17 at 21:49




@Toby Speight thx for heads up. At least the updated code compiles :)
– kevinf
Feb 23 '17 at 21:49










3 Answers
3






active

oldest

votes

















up vote
5
down vote



accepted










Prefer references



Throughout your code you use vector* or queen_t*. Not once do you check whether they are nullptr. Prefer references instead of pointers, e.g.



bool diagonal_slope(const queen_t &first_queen, const queen_t &second_queen){

if (first_queen.x == 0 || second_queen.x == 0){
return false;
}

return std::abs(second_queen.y - first_queen.y) == abs(second_queen.x - first_queen.x);
}


In validate_and_continue, you can simply copy your std::sets.



Don't use an underscore on global scope names



An underscore at the start of an identifier at global scope is reserved and yields undefined behaviour.



Also, using namespace std; is considered bad practice.



Prefer range-based for-loops over for_each



Your for_each loop is not really easy too the eye:



        for_each(avail_x->begin(), avail_x->end(), [&](const uint8_t cur_x) {
validate_and_continue(board, avail_x, avail_y,
cur_iteration, {cur_x, (uint8_t)cur_iteration}, {});
});


The range-based for loop which does the same doesn't need a lambda:



        for(auto cur_x : avail_x) {
validate_and_continue(board, avail_x, avail_y,
cur_iteration, {cur_x, (uint8_t)cur_iteration}, {});
}


Prefer const(expr) <integral type> over #define



You currently define BOARD_SIZE as a macro:



#define BOARD_SIZE 8


This will replace BOARD_SIZE by 8 in your code, which is still better than a stray magic number. However, it can hinder debugging in some cases and doesn't provide a type to 8. Prefer a constexpr or a const, see here and here:



const uint16_t BOARD_SIZE = 8;


Other remarks



For loop parts can be empty



If you don't need to initialize something, just leave the initialization expression empty, or fill it with something useful:



// Do not
for (queens_to_add; queens_to_add <= min(queens_left, (uint16_t)2); queens_to_add ++)

// Do
for (; queens_to_add <= min(queens_left, (uint16_t)2); queens_to_add ++)


Although note that you might call min in every iteration.



Documentation



If you want to follow a documentation quasi-standard, use doxygen. Also make sure to write down how your algorithm works, and why you use the variables you do. For example, why do you use avail_x and avail_y?



Global variables



Try to get rid of global variables. They make testing your functions a lot harder. If you want to know the number of recursions, either tag them along, or have recurse and validate_and_continue return the number of recursions.



Add missing includes



You're currently missing <vector> and <ctime>, as well as some others.



Better representation



For each row in a board, you only need to know the column of a queen in that board, e.g.



std::vector<unsigned> board = {1, 3, 0, 2};

// .Q..
// ...Q
// Q...
// ..Q.


Therefore, a std::vector<unsigned> is enough to store the location of the queens. The y-position is the index of your vector.






share|improve this answer























  • Prefer range-based for-loops over for_each ... I disagree.
    – knivil
    Feb 22 '17 at 21:23






  • 1




    @knivil and that's why? You need for_each only if you don't want to traverse the whole range (begin() to end()), or if you need the functor. You also don't need a lambda/functor. So what's your reason not to prefer range-based for-loops in this case?
    – Zeta
    Feb 22 '17 at 21:30










  • For me, they are equal. Non of them offer a clear quality advantage, e.g. readability.
    – knivil
    Feb 22 '17 at 21:47










  • I read this, stackoverflow.com/questions/2047414/… prior to my implementation, and indeed favoured range-based for ability to return early, but figured for_each was better practice? Seems like its personal preference
    – kevinf
    Feb 22 '17 at 23:41


















up vote
5
down vote













Here are some things that may help you improve your code.



Don't abuse using namespace std



Putting using namespace std within your program is generally a bad habit that you'd do well to avoid.



Use the appropriate #includes



In order to compile and link, this code requires the following lines:



#include <cmath>
#include <vector>
#include <string>


For the program to be complete, these should be listed, too.



Fix the bug



OK, technically speaking, it's not actually a bug, but it's a useless statement and confusing to human readers. In particular, I'm referring to this line:



for (queens_to_add; queens_to_add <= min(queens_left, (uint16_t)2); queens_to_add ++) {


The first clause queens_to_add doesn't actually do anything. It caused me to wonder if it was intended to say queens_to_add=0 or something like that, but on deeper inspection, the value has already been set. I'd recommend leaving that clause empty instead.



Omit unused variables



Because argc and argv are unused, you could use the alternative form of main:



int main ()


Don't use std::endl if you don't really need it



The difference betweeen std::endl and 'n' is that 'n' just emits a newline character, while std::endl actually flushes the stream. This can be time-consuming in a program with a lot of I/O and is rarely actually needed. It's best to only use std::endl when you have some good reason to flush the stream and it's not very often needed for simple programs such as this one. Avoiding the habit of using std::endl when 'n' will do will pay dividends in the future as you write more complex programs with more I/O and where performance needs to be maximized.



Don't use leading underscores in names



Anything with a leading underscore is a reserved name in C++ (and in C). See this question for details.



Prefer references to raw pointers



In a number of places within the code, parameters are passed as raw pointers such as



void print_board(std::vector<queen_t> *board);


What you really want is a reference. The difference is that a reference cannot be nullptr and must actually point to an object, which is exactly the guarantee your code relies on anyway.



Use appropriate C++ idioms



The last line of main looks like this:



print_board(&(*_boards.begin()));


Instead of doing funny stuff like that with an iterator, why not just use front()? Combined with the suggestion above (to pass references), that would change that line instead to this:



print_board(_boards.front());


Use const where practical



In your print_board() routine, the passed board is never altered, which is just as it should be. You should indicate that fact by declaring it like this:



void print_board(const std::vector<queen_t> &board);


Note that this also incorporates the above two suggestions.



Prefer const or constexpr variables to #define



The BOARD_SIZE value should be declared as



constexpr std::size_t BOARD_SIZE{8};


The difference is that when constants are declared this way, they have a bit of additional type safety.



Eliminate global variables where possible



The _boards variable is global but should really be in main. If you move it to main, you can simply add an additional reference argument to recurse and to validate_and_continue.



Create a C++ object



Rather than passing the board vector around, it would seem to make more sense to me to have it be an actual object with most of the functions being member functions of that class. Itwould not only encapsulate the data and algorithms more neatly, but it would also make the code easier for humans to read and understand.



Use better naming



The can_attack() function is well named because it's easy to guess (correctly) what it does in this context. The recurse function, however, doesn't really say anything useful about what the function's purpose is; just a hint as to how it might function. A user of the function won't necessarily care what's inside, so the name should be more descriptive.



Return something useful from functions



Instead of void, why not return the recursion depth from recurse and pass the current depth as a parameter? That way you could eliminate another global variable.



Improving the algorithm



Reflections and translations of any given solution are also solutions. One could easily incorporate the generation of those within the program and eliminate some of the recursions.






share|improve this answer























  • Odd, I went through my program and removed all the extraneous headers... and it compiled fine without them. My machines are up-to-date Ubuntu 16.04, it seemed like the missing headers were being pulled from algorithm,etc. Thanks for the heads up :)
    – kevinf
    Feb 22 '17 at 23:33






  • 2




    The problem with that approach is that some standard headers might include other standard headers. So that means that it compiles just find with that particular version of library and compiler, but it isn't portable and isn't reliable. So the best approach is, for each standard library call or template you use, include the documented #include per the standard.
    – Edward
    Feb 22 '17 at 23:38










  • unistd comes to mind... number of utilities at my company that were missing that header once we upgraded to Ubuntu 14.04...
    – kevinf
    Feb 22 '17 at 23:45


















up vote
-1
down vote













Including the appropriate header files to make this program compile, e.g. <vector>, <string> or <time.h>. Use clock functions from C++11. Do not use globals. Represent a board with a bitset where a set bit represents a position of a queen.






share|improve this answer

















  • 1




    these sound like pretty actionable advice items. the only thing missing now is a justification... why? why should OP do this?
    – Vogel612
    Feb 22 '17 at 22:40










  • 1.) Because the example does not compile on my machine. 2.) Because it is the prefered way (consistency) in C++11 (see question tags). 3.) Because globals are bad design. 4.) It is cool.
    – knivil
    Feb 22 '17 at 22:51












  • A downvote because I do not spoon-feed every detail?
    – knivil
    Feb 23 '17 at 9:28










  • Don't know why. Apart from the bitset, your points are valid. (didn't downvote you).
    – Zeta
    Feb 23 '17 at 23:37










  • I downvoted you but I don't know why - probably clicked by mistake
    – Nik Kyriakides
    Nov 15 '17 at 17:14













Your Answer





StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");

StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f156050%2fn-queens-recursive-implementation-in-c11%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























3 Answers
3






active

oldest

votes








3 Answers
3






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
5
down vote



accepted










Prefer references



Throughout your code you use vector* or queen_t*. Not once do you check whether they are nullptr. Prefer references instead of pointers, e.g.



bool diagonal_slope(const queen_t &first_queen, const queen_t &second_queen){

if (first_queen.x == 0 || second_queen.x == 0){
return false;
}

return std::abs(second_queen.y - first_queen.y) == abs(second_queen.x - first_queen.x);
}


In validate_and_continue, you can simply copy your std::sets.



Don't use an underscore on global scope names



An underscore at the start of an identifier at global scope is reserved and yields undefined behaviour.



Also, using namespace std; is considered bad practice.



Prefer range-based for-loops over for_each



Your for_each loop is not really easy too the eye:



        for_each(avail_x->begin(), avail_x->end(), [&](const uint8_t cur_x) {
validate_and_continue(board, avail_x, avail_y,
cur_iteration, {cur_x, (uint8_t)cur_iteration}, {});
});


The range-based for loop which does the same doesn't need a lambda:



        for(auto cur_x : avail_x) {
validate_and_continue(board, avail_x, avail_y,
cur_iteration, {cur_x, (uint8_t)cur_iteration}, {});
}


Prefer const(expr) <integral type> over #define



You currently define BOARD_SIZE as a macro:



#define BOARD_SIZE 8


This will replace BOARD_SIZE by 8 in your code, which is still better than a stray magic number. However, it can hinder debugging in some cases and doesn't provide a type to 8. Prefer a constexpr or a const, see here and here:



const uint16_t BOARD_SIZE = 8;


Other remarks



For loop parts can be empty



If you don't need to initialize something, just leave the initialization expression empty, or fill it with something useful:



// Do not
for (queens_to_add; queens_to_add <= min(queens_left, (uint16_t)2); queens_to_add ++)

// Do
for (; queens_to_add <= min(queens_left, (uint16_t)2); queens_to_add ++)


Although note that you might call min in every iteration.



Documentation



If you want to follow a documentation quasi-standard, use doxygen. Also make sure to write down how your algorithm works, and why you use the variables you do. For example, why do you use avail_x and avail_y?



Global variables



Try to get rid of global variables. They make testing your functions a lot harder. If you want to know the number of recursions, either tag them along, or have recurse and validate_and_continue return the number of recursions.



Add missing includes



You're currently missing <vector> and <ctime>, as well as some others.



Better representation



For each row in a board, you only need to know the column of a queen in that board, e.g.



std::vector<unsigned> board = {1, 3, 0, 2};

// .Q..
// ...Q
// Q...
// ..Q.


Therefore, a std::vector<unsigned> is enough to store the location of the queens. The y-position is the index of your vector.






share|improve this answer























  • Prefer range-based for-loops over for_each ... I disagree.
    – knivil
    Feb 22 '17 at 21:23






  • 1




    @knivil and that's why? You need for_each only if you don't want to traverse the whole range (begin() to end()), or if you need the functor. You also don't need a lambda/functor. So what's your reason not to prefer range-based for-loops in this case?
    – Zeta
    Feb 22 '17 at 21:30










  • For me, they are equal. Non of them offer a clear quality advantage, e.g. readability.
    – knivil
    Feb 22 '17 at 21:47










  • I read this, stackoverflow.com/questions/2047414/… prior to my implementation, and indeed favoured range-based for ability to return early, but figured for_each was better practice? Seems like its personal preference
    – kevinf
    Feb 22 '17 at 23:41















up vote
5
down vote



accepted










Prefer references



Throughout your code you use vector* or queen_t*. Not once do you check whether they are nullptr. Prefer references instead of pointers, e.g.



bool diagonal_slope(const queen_t &first_queen, const queen_t &second_queen){

if (first_queen.x == 0 || second_queen.x == 0){
return false;
}

return std::abs(second_queen.y - first_queen.y) == abs(second_queen.x - first_queen.x);
}


In validate_and_continue, you can simply copy your std::sets.



Don't use an underscore on global scope names



An underscore at the start of an identifier at global scope is reserved and yields undefined behaviour.



Also, using namespace std; is considered bad practice.



Prefer range-based for-loops over for_each



Your for_each loop is not really easy too the eye:



        for_each(avail_x->begin(), avail_x->end(), [&](const uint8_t cur_x) {
validate_and_continue(board, avail_x, avail_y,
cur_iteration, {cur_x, (uint8_t)cur_iteration}, {});
});


The range-based for loop which does the same doesn't need a lambda:



        for(auto cur_x : avail_x) {
validate_and_continue(board, avail_x, avail_y,
cur_iteration, {cur_x, (uint8_t)cur_iteration}, {});
}


Prefer const(expr) <integral type> over #define



You currently define BOARD_SIZE as a macro:



#define BOARD_SIZE 8


This will replace BOARD_SIZE by 8 in your code, which is still better than a stray magic number. However, it can hinder debugging in some cases and doesn't provide a type to 8. Prefer a constexpr or a const, see here and here:



const uint16_t BOARD_SIZE = 8;


Other remarks



For loop parts can be empty



If you don't need to initialize something, just leave the initialization expression empty, or fill it with something useful:



// Do not
for (queens_to_add; queens_to_add <= min(queens_left, (uint16_t)2); queens_to_add ++)

// Do
for (; queens_to_add <= min(queens_left, (uint16_t)2); queens_to_add ++)


Although note that you might call min in every iteration.



Documentation



If you want to follow a documentation quasi-standard, use doxygen. Also make sure to write down how your algorithm works, and why you use the variables you do. For example, why do you use avail_x and avail_y?



Global variables



Try to get rid of global variables. They make testing your functions a lot harder. If you want to know the number of recursions, either tag them along, or have recurse and validate_and_continue return the number of recursions.



Add missing includes



You're currently missing <vector> and <ctime>, as well as some others.



Better representation



For each row in a board, you only need to know the column of a queen in that board, e.g.



std::vector<unsigned> board = {1, 3, 0, 2};

// .Q..
// ...Q
// Q...
// ..Q.


Therefore, a std::vector<unsigned> is enough to store the location of the queens. The y-position is the index of your vector.






share|improve this answer























  • Prefer range-based for-loops over for_each ... I disagree.
    – knivil
    Feb 22 '17 at 21:23






  • 1




    @knivil and that's why? You need for_each only if you don't want to traverse the whole range (begin() to end()), or if you need the functor. You also don't need a lambda/functor. So what's your reason not to prefer range-based for-loops in this case?
    – Zeta
    Feb 22 '17 at 21:30










  • For me, they are equal. Non of them offer a clear quality advantage, e.g. readability.
    – knivil
    Feb 22 '17 at 21:47










  • I read this, stackoverflow.com/questions/2047414/… prior to my implementation, and indeed favoured range-based for ability to return early, but figured for_each was better practice? Seems like its personal preference
    – kevinf
    Feb 22 '17 at 23:41













up vote
5
down vote



accepted







up vote
5
down vote



accepted






Prefer references



Throughout your code you use vector* or queen_t*. Not once do you check whether they are nullptr. Prefer references instead of pointers, e.g.



bool diagonal_slope(const queen_t &first_queen, const queen_t &second_queen){

if (first_queen.x == 0 || second_queen.x == 0){
return false;
}

return std::abs(second_queen.y - first_queen.y) == abs(second_queen.x - first_queen.x);
}


In validate_and_continue, you can simply copy your std::sets.



Don't use an underscore on global scope names



An underscore at the start of an identifier at global scope is reserved and yields undefined behaviour.



Also, using namespace std; is considered bad practice.



Prefer range-based for-loops over for_each



Your for_each loop is not really easy too the eye:



        for_each(avail_x->begin(), avail_x->end(), [&](const uint8_t cur_x) {
validate_and_continue(board, avail_x, avail_y,
cur_iteration, {cur_x, (uint8_t)cur_iteration}, {});
});


The range-based for loop which does the same doesn't need a lambda:



        for(auto cur_x : avail_x) {
validate_and_continue(board, avail_x, avail_y,
cur_iteration, {cur_x, (uint8_t)cur_iteration}, {});
}


Prefer const(expr) <integral type> over #define



You currently define BOARD_SIZE as a macro:



#define BOARD_SIZE 8


This will replace BOARD_SIZE by 8 in your code, which is still better than a stray magic number. However, it can hinder debugging in some cases and doesn't provide a type to 8. Prefer a constexpr or a const, see here and here:



const uint16_t BOARD_SIZE = 8;


Other remarks



For loop parts can be empty



If you don't need to initialize something, just leave the initialization expression empty, or fill it with something useful:



// Do not
for (queens_to_add; queens_to_add <= min(queens_left, (uint16_t)2); queens_to_add ++)

// Do
for (; queens_to_add <= min(queens_left, (uint16_t)2); queens_to_add ++)


Although note that you might call min in every iteration.



Documentation



If you want to follow a documentation quasi-standard, use doxygen. Also make sure to write down how your algorithm works, and why you use the variables you do. For example, why do you use avail_x and avail_y?



Global variables



Try to get rid of global variables. They make testing your functions a lot harder. If you want to know the number of recursions, either tag them along, or have recurse and validate_and_continue return the number of recursions.



Add missing includes



You're currently missing <vector> and <ctime>, as well as some others.



Better representation



For each row in a board, you only need to know the column of a queen in that board, e.g.



std::vector<unsigned> board = {1, 3, 0, 2};

// .Q..
// ...Q
// Q...
// ..Q.


Therefore, a std::vector<unsigned> is enough to store the location of the queens. The y-position is the index of your vector.






share|improve this answer














Prefer references



Throughout your code you use vector* or queen_t*. Not once do you check whether they are nullptr. Prefer references instead of pointers, e.g.



bool diagonal_slope(const queen_t &first_queen, const queen_t &second_queen){

if (first_queen.x == 0 || second_queen.x == 0){
return false;
}

return std::abs(second_queen.y - first_queen.y) == abs(second_queen.x - first_queen.x);
}


In validate_and_continue, you can simply copy your std::sets.



Don't use an underscore on global scope names



An underscore at the start of an identifier at global scope is reserved and yields undefined behaviour.



Also, using namespace std; is considered bad practice.



Prefer range-based for-loops over for_each



Your for_each loop is not really easy too the eye:



        for_each(avail_x->begin(), avail_x->end(), [&](const uint8_t cur_x) {
validate_and_continue(board, avail_x, avail_y,
cur_iteration, {cur_x, (uint8_t)cur_iteration}, {});
});


The range-based for loop which does the same doesn't need a lambda:



        for(auto cur_x : avail_x) {
validate_and_continue(board, avail_x, avail_y,
cur_iteration, {cur_x, (uint8_t)cur_iteration}, {});
}


Prefer const(expr) <integral type> over #define



You currently define BOARD_SIZE as a macro:



#define BOARD_SIZE 8


This will replace BOARD_SIZE by 8 in your code, which is still better than a stray magic number. However, it can hinder debugging in some cases and doesn't provide a type to 8. Prefer a constexpr or a const, see here and here:



const uint16_t BOARD_SIZE = 8;


Other remarks



For loop parts can be empty



If you don't need to initialize something, just leave the initialization expression empty, or fill it with something useful:



// Do not
for (queens_to_add; queens_to_add <= min(queens_left, (uint16_t)2); queens_to_add ++)

// Do
for (; queens_to_add <= min(queens_left, (uint16_t)2); queens_to_add ++)


Although note that you might call min in every iteration.



Documentation



If you want to follow a documentation quasi-standard, use doxygen. Also make sure to write down how your algorithm works, and why you use the variables you do. For example, why do you use avail_x and avail_y?



Global variables



Try to get rid of global variables. They make testing your functions a lot harder. If you want to know the number of recursions, either tag them along, or have recurse and validate_and_continue return the number of recursions.



Add missing includes



You're currently missing <vector> and <ctime>, as well as some others.



Better representation



For each row in a board, you only need to know the column of a queen in that board, e.g.



std::vector<unsigned> board = {1, 3, 0, 2};

// .Q..
// ...Q
// Q...
// ..Q.


Therefore, a std::vector<unsigned> is enough to store the location of the queens. The y-position is the index of your vector.







share|improve this answer














share|improve this answer



share|improve this answer








edited 48 mins ago









albert

1271




1271










answered Feb 22 '17 at 21:20









Zeta

14.8k23371




14.8k23371












  • Prefer range-based for-loops over for_each ... I disagree.
    – knivil
    Feb 22 '17 at 21:23






  • 1




    @knivil and that's why? You need for_each only if you don't want to traverse the whole range (begin() to end()), or if you need the functor. You also don't need a lambda/functor. So what's your reason not to prefer range-based for-loops in this case?
    – Zeta
    Feb 22 '17 at 21:30










  • For me, they are equal. Non of them offer a clear quality advantage, e.g. readability.
    – knivil
    Feb 22 '17 at 21:47










  • I read this, stackoverflow.com/questions/2047414/… prior to my implementation, and indeed favoured range-based for ability to return early, but figured for_each was better practice? Seems like its personal preference
    – kevinf
    Feb 22 '17 at 23:41


















  • Prefer range-based for-loops over for_each ... I disagree.
    – knivil
    Feb 22 '17 at 21:23






  • 1




    @knivil and that's why? You need for_each only if you don't want to traverse the whole range (begin() to end()), or if you need the functor. You also don't need a lambda/functor. So what's your reason not to prefer range-based for-loops in this case?
    – Zeta
    Feb 22 '17 at 21:30










  • For me, they are equal. Non of them offer a clear quality advantage, e.g. readability.
    – knivil
    Feb 22 '17 at 21:47










  • I read this, stackoverflow.com/questions/2047414/… prior to my implementation, and indeed favoured range-based for ability to return early, but figured for_each was better practice? Seems like its personal preference
    – kevinf
    Feb 22 '17 at 23:41
















Prefer range-based for-loops over for_each ... I disagree.
– knivil
Feb 22 '17 at 21:23




Prefer range-based for-loops over for_each ... I disagree.
– knivil
Feb 22 '17 at 21:23




1




1




@knivil and that's why? You need for_each only if you don't want to traverse the whole range (begin() to end()), or if you need the functor. You also don't need a lambda/functor. So what's your reason not to prefer range-based for-loops in this case?
– Zeta
Feb 22 '17 at 21:30




@knivil and that's why? You need for_each only if you don't want to traverse the whole range (begin() to end()), or if you need the functor. You also don't need a lambda/functor. So what's your reason not to prefer range-based for-loops in this case?
– Zeta
Feb 22 '17 at 21:30












For me, they are equal. Non of them offer a clear quality advantage, e.g. readability.
– knivil
Feb 22 '17 at 21:47




For me, they are equal. Non of them offer a clear quality advantage, e.g. readability.
– knivil
Feb 22 '17 at 21:47












I read this, stackoverflow.com/questions/2047414/… prior to my implementation, and indeed favoured range-based for ability to return early, but figured for_each was better practice? Seems like its personal preference
– kevinf
Feb 22 '17 at 23:41




I read this, stackoverflow.com/questions/2047414/… prior to my implementation, and indeed favoured range-based for ability to return early, but figured for_each was better practice? Seems like its personal preference
– kevinf
Feb 22 '17 at 23:41












up vote
5
down vote













Here are some things that may help you improve your code.



Don't abuse using namespace std



Putting using namespace std within your program is generally a bad habit that you'd do well to avoid.



Use the appropriate #includes



In order to compile and link, this code requires the following lines:



#include <cmath>
#include <vector>
#include <string>


For the program to be complete, these should be listed, too.



Fix the bug



OK, technically speaking, it's not actually a bug, but it's a useless statement and confusing to human readers. In particular, I'm referring to this line:



for (queens_to_add; queens_to_add <= min(queens_left, (uint16_t)2); queens_to_add ++) {


The first clause queens_to_add doesn't actually do anything. It caused me to wonder if it was intended to say queens_to_add=0 or something like that, but on deeper inspection, the value has already been set. I'd recommend leaving that clause empty instead.



Omit unused variables



Because argc and argv are unused, you could use the alternative form of main:



int main ()


Don't use std::endl if you don't really need it



The difference betweeen std::endl and 'n' is that 'n' just emits a newline character, while std::endl actually flushes the stream. This can be time-consuming in a program with a lot of I/O and is rarely actually needed. It's best to only use std::endl when you have some good reason to flush the stream and it's not very often needed for simple programs such as this one. Avoiding the habit of using std::endl when 'n' will do will pay dividends in the future as you write more complex programs with more I/O and where performance needs to be maximized.



Don't use leading underscores in names



Anything with a leading underscore is a reserved name in C++ (and in C). See this question for details.



Prefer references to raw pointers



In a number of places within the code, parameters are passed as raw pointers such as



void print_board(std::vector<queen_t> *board);


What you really want is a reference. The difference is that a reference cannot be nullptr and must actually point to an object, which is exactly the guarantee your code relies on anyway.



Use appropriate C++ idioms



The last line of main looks like this:



print_board(&(*_boards.begin()));


Instead of doing funny stuff like that with an iterator, why not just use front()? Combined with the suggestion above (to pass references), that would change that line instead to this:



print_board(_boards.front());


Use const where practical



In your print_board() routine, the passed board is never altered, which is just as it should be. You should indicate that fact by declaring it like this:



void print_board(const std::vector<queen_t> &board);


Note that this also incorporates the above two suggestions.



Prefer const or constexpr variables to #define



The BOARD_SIZE value should be declared as



constexpr std::size_t BOARD_SIZE{8};


The difference is that when constants are declared this way, they have a bit of additional type safety.



Eliminate global variables where possible



The _boards variable is global but should really be in main. If you move it to main, you can simply add an additional reference argument to recurse and to validate_and_continue.



Create a C++ object



Rather than passing the board vector around, it would seem to make more sense to me to have it be an actual object with most of the functions being member functions of that class. Itwould not only encapsulate the data and algorithms more neatly, but it would also make the code easier for humans to read and understand.



Use better naming



The can_attack() function is well named because it's easy to guess (correctly) what it does in this context. The recurse function, however, doesn't really say anything useful about what the function's purpose is; just a hint as to how it might function. A user of the function won't necessarily care what's inside, so the name should be more descriptive.



Return something useful from functions



Instead of void, why not return the recursion depth from recurse and pass the current depth as a parameter? That way you could eliminate another global variable.



Improving the algorithm



Reflections and translations of any given solution are also solutions. One could easily incorporate the generation of those within the program and eliminate some of the recursions.






share|improve this answer























  • Odd, I went through my program and removed all the extraneous headers... and it compiled fine without them. My machines are up-to-date Ubuntu 16.04, it seemed like the missing headers were being pulled from algorithm,etc. Thanks for the heads up :)
    – kevinf
    Feb 22 '17 at 23:33






  • 2




    The problem with that approach is that some standard headers might include other standard headers. So that means that it compiles just find with that particular version of library and compiler, but it isn't portable and isn't reliable. So the best approach is, for each standard library call or template you use, include the documented #include per the standard.
    – Edward
    Feb 22 '17 at 23:38










  • unistd comes to mind... number of utilities at my company that were missing that header once we upgraded to Ubuntu 14.04...
    – kevinf
    Feb 22 '17 at 23:45















up vote
5
down vote













Here are some things that may help you improve your code.



Don't abuse using namespace std



Putting using namespace std within your program is generally a bad habit that you'd do well to avoid.



Use the appropriate #includes



In order to compile and link, this code requires the following lines:



#include <cmath>
#include <vector>
#include <string>


For the program to be complete, these should be listed, too.



Fix the bug



OK, technically speaking, it's not actually a bug, but it's a useless statement and confusing to human readers. In particular, I'm referring to this line:



for (queens_to_add; queens_to_add <= min(queens_left, (uint16_t)2); queens_to_add ++) {


The first clause queens_to_add doesn't actually do anything. It caused me to wonder if it was intended to say queens_to_add=0 or something like that, but on deeper inspection, the value has already been set. I'd recommend leaving that clause empty instead.



Omit unused variables



Because argc and argv are unused, you could use the alternative form of main:



int main ()


Don't use std::endl if you don't really need it



The difference betweeen std::endl and 'n' is that 'n' just emits a newline character, while std::endl actually flushes the stream. This can be time-consuming in a program with a lot of I/O and is rarely actually needed. It's best to only use std::endl when you have some good reason to flush the stream and it's not very often needed for simple programs such as this one. Avoiding the habit of using std::endl when 'n' will do will pay dividends in the future as you write more complex programs with more I/O and where performance needs to be maximized.



Don't use leading underscores in names



Anything with a leading underscore is a reserved name in C++ (and in C). See this question for details.



Prefer references to raw pointers



In a number of places within the code, parameters are passed as raw pointers such as



void print_board(std::vector<queen_t> *board);


What you really want is a reference. The difference is that a reference cannot be nullptr and must actually point to an object, which is exactly the guarantee your code relies on anyway.



Use appropriate C++ idioms



The last line of main looks like this:



print_board(&(*_boards.begin()));


Instead of doing funny stuff like that with an iterator, why not just use front()? Combined with the suggestion above (to pass references), that would change that line instead to this:



print_board(_boards.front());


Use const where practical



In your print_board() routine, the passed board is never altered, which is just as it should be. You should indicate that fact by declaring it like this:



void print_board(const std::vector<queen_t> &board);


Note that this also incorporates the above two suggestions.



Prefer const or constexpr variables to #define



The BOARD_SIZE value should be declared as



constexpr std::size_t BOARD_SIZE{8};


The difference is that when constants are declared this way, they have a bit of additional type safety.



Eliminate global variables where possible



The _boards variable is global but should really be in main. If you move it to main, you can simply add an additional reference argument to recurse and to validate_and_continue.



Create a C++ object



Rather than passing the board vector around, it would seem to make more sense to me to have it be an actual object with most of the functions being member functions of that class. Itwould not only encapsulate the data and algorithms more neatly, but it would also make the code easier for humans to read and understand.



Use better naming



The can_attack() function is well named because it's easy to guess (correctly) what it does in this context. The recurse function, however, doesn't really say anything useful about what the function's purpose is; just a hint as to how it might function. A user of the function won't necessarily care what's inside, so the name should be more descriptive.



Return something useful from functions



Instead of void, why not return the recursion depth from recurse and pass the current depth as a parameter? That way you could eliminate another global variable.



Improving the algorithm



Reflections and translations of any given solution are also solutions. One could easily incorporate the generation of those within the program and eliminate some of the recursions.






share|improve this answer























  • Odd, I went through my program and removed all the extraneous headers... and it compiled fine without them. My machines are up-to-date Ubuntu 16.04, it seemed like the missing headers were being pulled from algorithm,etc. Thanks for the heads up :)
    – kevinf
    Feb 22 '17 at 23:33






  • 2




    The problem with that approach is that some standard headers might include other standard headers. So that means that it compiles just find with that particular version of library and compiler, but it isn't portable and isn't reliable. So the best approach is, for each standard library call or template you use, include the documented #include per the standard.
    – Edward
    Feb 22 '17 at 23:38










  • unistd comes to mind... number of utilities at my company that were missing that header once we upgraded to Ubuntu 14.04...
    – kevinf
    Feb 22 '17 at 23:45













up vote
5
down vote










up vote
5
down vote









Here are some things that may help you improve your code.



Don't abuse using namespace std



Putting using namespace std within your program is generally a bad habit that you'd do well to avoid.



Use the appropriate #includes



In order to compile and link, this code requires the following lines:



#include <cmath>
#include <vector>
#include <string>


For the program to be complete, these should be listed, too.



Fix the bug



OK, technically speaking, it's not actually a bug, but it's a useless statement and confusing to human readers. In particular, I'm referring to this line:



for (queens_to_add; queens_to_add <= min(queens_left, (uint16_t)2); queens_to_add ++) {


The first clause queens_to_add doesn't actually do anything. It caused me to wonder if it was intended to say queens_to_add=0 or something like that, but on deeper inspection, the value has already been set. I'd recommend leaving that clause empty instead.



Omit unused variables



Because argc and argv are unused, you could use the alternative form of main:



int main ()


Don't use std::endl if you don't really need it



The difference betweeen std::endl and 'n' is that 'n' just emits a newline character, while std::endl actually flushes the stream. This can be time-consuming in a program with a lot of I/O and is rarely actually needed. It's best to only use std::endl when you have some good reason to flush the stream and it's not very often needed for simple programs such as this one. Avoiding the habit of using std::endl when 'n' will do will pay dividends in the future as you write more complex programs with more I/O and where performance needs to be maximized.



Don't use leading underscores in names



Anything with a leading underscore is a reserved name in C++ (and in C). See this question for details.



Prefer references to raw pointers



In a number of places within the code, parameters are passed as raw pointers such as



void print_board(std::vector<queen_t> *board);


What you really want is a reference. The difference is that a reference cannot be nullptr and must actually point to an object, which is exactly the guarantee your code relies on anyway.



Use appropriate C++ idioms



The last line of main looks like this:



print_board(&(*_boards.begin()));


Instead of doing funny stuff like that with an iterator, why not just use front()? Combined with the suggestion above (to pass references), that would change that line instead to this:



print_board(_boards.front());


Use const where practical



In your print_board() routine, the passed board is never altered, which is just as it should be. You should indicate that fact by declaring it like this:



void print_board(const std::vector<queen_t> &board);


Note that this also incorporates the above two suggestions.



Prefer const or constexpr variables to #define



The BOARD_SIZE value should be declared as



constexpr std::size_t BOARD_SIZE{8};


The difference is that when constants are declared this way, they have a bit of additional type safety.



Eliminate global variables where possible



The _boards variable is global but should really be in main. If you move it to main, you can simply add an additional reference argument to recurse and to validate_and_continue.



Create a C++ object



Rather than passing the board vector around, it would seem to make more sense to me to have it be an actual object with most of the functions being member functions of that class. Itwould not only encapsulate the data and algorithms more neatly, but it would also make the code easier for humans to read and understand.



Use better naming



The can_attack() function is well named because it's easy to guess (correctly) what it does in this context. The recurse function, however, doesn't really say anything useful about what the function's purpose is; just a hint as to how it might function. A user of the function won't necessarily care what's inside, so the name should be more descriptive.



Return something useful from functions



Instead of void, why not return the recursion depth from recurse and pass the current depth as a parameter? That way you could eliminate another global variable.



Improving the algorithm



Reflections and translations of any given solution are also solutions. One could easily incorporate the generation of those within the program and eliminate some of the recursions.






share|improve this answer














Here are some things that may help you improve your code.



Don't abuse using namespace std



Putting using namespace std within your program is generally a bad habit that you'd do well to avoid.



Use the appropriate #includes



In order to compile and link, this code requires the following lines:



#include <cmath>
#include <vector>
#include <string>


For the program to be complete, these should be listed, too.



Fix the bug



OK, technically speaking, it's not actually a bug, but it's a useless statement and confusing to human readers. In particular, I'm referring to this line:



for (queens_to_add; queens_to_add <= min(queens_left, (uint16_t)2); queens_to_add ++) {


The first clause queens_to_add doesn't actually do anything. It caused me to wonder if it was intended to say queens_to_add=0 or something like that, but on deeper inspection, the value has already been set. I'd recommend leaving that clause empty instead.



Omit unused variables



Because argc and argv are unused, you could use the alternative form of main:



int main ()


Don't use std::endl if you don't really need it



The difference betweeen std::endl and 'n' is that 'n' just emits a newline character, while std::endl actually flushes the stream. This can be time-consuming in a program with a lot of I/O and is rarely actually needed. It's best to only use std::endl when you have some good reason to flush the stream and it's not very often needed for simple programs such as this one. Avoiding the habit of using std::endl when 'n' will do will pay dividends in the future as you write more complex programs with more I/O and where performance needs to be maximized.



Don't use leading underscores in names



Anything with a leading underscore is a reserved name in C++ (and in C). See this question for details.



Prefer references to raw pointers



In a number of places within the code, parameters are passed as raw pointers such as



void print_board(std::vector<queen_t> *board);


What you really want is a reference. The difference is that a reference cannot be nullptr and must actually point to an object, which is exactly the guarantee your code relies on anyway.



Use appropriate C++ idioms



The last line of main looks like this:



print_board(&(*_boards.begin()));


Instead of doing funny stuff like that with an iterator, why not just use front()? Combined with the suggestion above (to pass references), that would change that line instead to this:



print_board(_boards.front());


Use const where practical



In your print_board() routine, the passed board is never altered, which is just as it should be. You should indicate that fact by declaring it like this:



void print_board(const std::vector<queen_t> &board);


Note that this also incorporates the above two suggestions.



Prefer const or constexpr variables to #define



The BOARD_SIZE value should be declared as



constexpr std::size_t BOARD_SIZE{8};


The difference is that when constants are declared this way, they have a bit of additional type safety.



Eliminate global variables where possible



The _boards variable is global but should really be in main. If you move it to main, you can simply add an additional reference argument to recurse and to validate_and_continue.



Create a C++ object



Rather than passing the board vector around, it would seem to make more sense to me to have it be an actual object with most of the functions being member functions of that class. Itwould not only encapsulate the data and algorithms more neatly, but it would also make the code easier for humans to read and understand.



Use better naming



The can_attack() function is well named because it's easy to guess (correctly) what it does in this context. The recurse function, however, doesn't really say anything useful about what the function's purpose is; just a hint as to how it might function. A user of the function won't necessarily care what's inside, so the name should be more descriptive.



Return something useful from functions



Instead of void, why not return the recursion depth from recurse and pass the current depth as a parameter? That way you could eliminate another global variable.



Improving the algorithm



Reflections and translations of any given solution are also solutions. One could easily incorporate the generation of those within the program and eliminate some of the recursions.







share|improve this answer














share|improve this answer



share|improve this answer








edited May 23 '17 at 12:40









Community

1




1










answered Feb 22 '17 at 21:45









Edward

45.6k377207




45.6k377207












  • Odd, I went through my program and removed all the extraneous headers... and it compiled fine without them. My machines are up-to-date Ubuntu 16.04, it seemed like the missing headers were being pulled from algorithm,etc. Thanks for the heads up :)
    – kevinf
    Feb 22 '17 at 23:33






  • 2




    The problem with that approach is that some standard headers might include other standard headers. So that means that it compiles just find with that particular version of library and compiler, but it isn't portable and isn't reliable. So the best approach is, for each standard library call or template you use, include the documented #include per the standard.
    – Edward
    Feb 22 '17 at 23:38










  • unistd comes to mind... number of utilities at my company that were missing that header once we upgraded to Ubuntu 14.04...
    – kevinf
    Feb 22 '17 at 23:45


















  • Odd, I went through my program and removed all the extraneous headers... and it compiled fine without them. My machines are up-to-date Ubuntu 16.04, it seemed like the missing headers were being pulled from algorithm,etc. Thanks for the heads up :)
    – kevinf
    Feb 22 '17 at 23:33






  • 2




    The problem with that approach is that some standard headers might include other standard headers. So that means that it compiles just find with that particular version of library and compiler, but it isn't portable and isn't reliable. So the best approach is, for each standard library call or template you use, include the documented #include per the standard.
    – Edward
    Feb 22 '17 at 23:38










  • unistd comes to mind... number of utilities at my company that were missing that header once we upgraded to Ubuntu 14.04...
    – kevinf
    Feb 22 '17 at 23:45
















Odd, I went through my program and removed all the extraneous headers... and it compiled fine without them. My machines are up-to-date Ubuntu 16.04, it seemed like the missing headers were being pulled from algorithm,etc. Thanks for the heads up :)
– kevinf
Feb 22 '17 at 23:33




Odd, I went through my program and removed all the extraneous headers... and it compiled fine without them. My machines are up-to-date Ubuntu 16.04, it seemed like the missing headers were being pulled from algorithm,etc. Thanks for the heads up :)
– kevinf
Feb 22 '17 at 23:33




2




2




The problem with that approach is that some standard headers might include other standard headers. So that means that it compiles just find with that particular version of library and compiler, but it isn't portable and isn't reliable. So the best approach is, for each standard library call or template you use, include the documented #include per the standard.
– Edward
Feb 22 '17 at 23:38




The problem with that approach is that some standard headers might include other standard headers. So that means that it compiles just find with that particular version of library and compiler, but it isn't portable and isn't reliable. So the best approach is, for each standard library call or template you use, include the documented #include per the standard.
– Edward
Feb 22 '17 at 23:38












unistd comes to mind... number of utilities at my company that were missing that header once we upgraded to Ubuntu 14.04...
– kevinf
Feb 22 '17 at 23:45




unistd comes to mind... number of utilities at my company that were missing that header once we upgraded to Ubuntu 14.04...
– kevinf
Feb 22 '17 at 23:45










up vote
-1
down vote













Including the appropriate header files to make this program compile, e.g. <vector>, <string> or <time.h>. Use clock functions from C++11. Do not use globals. Represent a board with a bitset where a set bit represents a position of a queen.






share|improve this answer

















  • 1




    these sound like pretty actionable advice items. the only thing missing now is a justification... why? why should OP do this?
    – Vogel612
    Feb 22 '17 at 22:40










  • 1.) Because the example does not compile on my machine. 2.) Because it is the prefered way (consistency) in C++11 (see question tags). 3.) Because globals are bad design. 4.) It is cool.
    – knivil
    Feb 22 '17 at 22:51












  • A downvote because I do not spoon-feed every detail?
    – knivil
    Feb 23 '17 at 9:28










  • Don't know why. Apart from the bitset, your points are valid. (didn't downvote you).
    – Zeta
    Feb 23 '17 at 23:37










  • I downvoted you but I don't know why - probably clicked by mistake
    – Nik Kyriakides
    Nov 15 '17 at 17:14

















up vote
-1
down vote













Including the appropriate header files to make this program compile, e.g. <vector>, <string> or <time.h>. Use clock functions from C++11. Do not use globals. Represent a board with a bitset where a set bit represents a position of a queen.






share|improve this answer

















  • 1




    these sound like pretty actionable advice items. the only thing missing now is a justification... why? why should OP do this?
    – Vogel612
    Feb 22 '17 at 22:40










  • 1.) Because the example does not compile on my machine. 2.) Because it is the prefered way (consistency) in C++11 (see question tags). 3.) Because globals are bad design. 4.) It is cool.
    – knivil
    Feb 22 '17 at 22:51












  • A downvote because I do not spoon-feed every detail?
    – knivil
    Feb 23 '17 at 9:28










  • Don't know why. Apart from the bitset, your points are valid. (didn't downvote you).
    – Zeta
    Feb 23 '17 at 23:37










  • I downvoted you but I don't know why - probably clicked by mistake
    – Nik Kyriakides
    Nov 15 '17 at 17:14















up vote
-1
down vote










up vote
-1
down vote









Including the appropriate header files to make this program compile, e.g. <vector>, <string> or <time.h>. Use clock functions from C++11. Do not use globals. Represent a board with a bitset where a set bit represents a position of a queen.






share|improve this answer












Including the appropriate header files to make this program compile, e.g. <vector>, <string> or <time.h>. Use clock functions from C++11. Do not use globals. Represent a board with a bitset where a set bit represents a position of a queen.







share|improve this answer












share|improve this answer



share|improve this answer










answered Feb 22 '17 at 21:20









knivil

991




991








  • 1




    these sound like pretty actionable advice items. the only thing missing now is a justification... why? why should OP do this?
    – Vogel612
    Feb 22 '17 at 22:40










  • 1.) Because the example does not compile on my machine. 2.) Because it is the prefered way (consistency) in C++11 (see question tags). 3.) Because globals are bad design. 4.) It is cool.
    – knivil
    Feb 22 '17 at 22:51












  • A downvote because I do not spoon-feed every detail?
    – knivil
    Feb 23 '17 at 9:28










  • Don't know why. Apart from the bitset, your points are valid. (didn't downvote you).
    – Zeta
    Feb 23 '17 at 23:37










  • I downvoted you but I don't know why - probably clicked by mistake
    – Nik Kyriakides
    Nov 15 '17 at 17:14
















  • 1




    these sound like pretty actionable advice items. the only thing missing now is a justification... why? why should OP do this?
    – Vogel612
    Feb 22 '17 at 22:40










  • 1.) Because the example does not compile on my machine. 2.) Because it is the prefered way (consistency) in C++11 (see question tags). 3.) Because globals are bad design. 4.) It is cool.
    – knivil
    Feb 22 '17 at 22:51












  • A downvote because I do not spoon-feed every detail?
    – knivil
    Feb 23 '17 at 9:28










  • Don't know why. Apart from the bitset, your points are valid. (didn't downvote you).
    – Zeta
    Feb 23 '17 at 23:37










  • I downvoted you but I don't know why - probably clicked by mistake
    – Nik Kyriakides
    Nov 15 '17 at 17:14










1




1




these sound like pretty actionable advice items. the only thing missing now is a justification... why? why should OP do this?
– Vogel612
Feb 22 '17 at 22:40




these sound like pretty actionable advice items. the only thing missing now is a justification... why? why should OP do this?
– Vogel612
Feb 22 '17 at 22:40












1.) Because the example does not compile on my machine. 2.) Because it is the prefered way (consistency) in C++11 (see question tags). 3.) Because globals are bad design. 4.) It is cool.
– knivil
Feb 22 '17 at 22:51






1.) Because the example does not compile on my machine. 2.) Because it is the prefered way (consistency) in C++11 (see question tags). 3.) Because globals are bad design. 4.) It is cool.
– knivil
Feb 22 '17 at 22:51














A downvote because I do not spoon-feed every detail?
– knivil
Feb 23 '17 at 9:28




A downvote because I do not spoon-feed every detail?
– knivil
Feb 23 '17 at 9:28












Don't know why. Apart from the bitset, your points are valid. (didn't downvote you).
– Zeta
Feb 23 '17 at 23:37




Don't know why. Apart from the bitset, your points are valid. (didn't downvote you).
– Zeta
Feb 23 '17 at 23:37












I downvoted you but I don't know why - probably clicked by mistake
– Nik Kyriakides
Nov 15 '17 at 17:14






I downvoted you but I don't know why - probably clicked by mistake
– Nik Kyriakides
Nov 15 '17 at 17:14




















draft saved

draft discarded




















































Thanks for contributing an answer to Code Review Stack Exchange!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


Use MathJax to format equations. MathJax reference.


To learn more, see our tips on writing great answers.





Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


Please pay close attention to the following guidance:


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f156050%2fn-queens-recursive-implementation-in-c11%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Quarter-circle Tiles

build a pushdown automaton that recognizes the reverse language of a given pushdown automaton?

Mont Emei