forked from trekhleb/javascript-algorithms
-
Notifications
You must be signed in to change notification settings - Fork 0
/
nQueensBitwise.js
101 lines (93 loc) · 4.69 KB
/
nQueensBitwise.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
/**
* Checks all possible board configurations.
*
* @param {number} boardSize - Size of the squared chess board.
* @param {number} leftDiagonal - Sequence of N bits that show whether the corresponding location
* on the current row is "available" (no other queens are threatening from left diagonal).
* @param {number} column - Sequence of N bits that show whether the corresponding location
* on the current row is "available" (no other queens are threatening from columns).
* @param {number} rightDiagonal - Sequence of N bits that show whether the corresponding location
* on the current row is "available" (no other queens are threatening from right diagonal).
* @param {number} solutionsCount - Keeps track of the number of valid solutions.
* @return {number} - Number of possible solutions.
*/
function nQueensBitwiseRecursive(
boardSize,
leftDiagonal = 0,
column = 0,
rightDiagonal = 0,
solutionsCount = 0,
) {
// Keeps track of the number of valid solutions.
let currentSolutionsCount = solutionsCount;
// Helps to identify valid solutions.
// isDone simply has a bit sequence with 1 for every entry up to the Nth. For example,
// when N=5, done will equal 11111. The "isDone" variable simply allows us to not worry about any
// bits beyond the Nth.
const isDone = (2 ** boardSize) - 1;
// All columns are occupied (i.e. 0b1111 for boardSize = 4), so the solution must be complete.
// Since the algorithm never places a queen illegally (ie. when it can attack or be attacked),
// we know that if all the columns have been filled, we must have a valid solution.
if (column === isDone) {
return currentSolutionsCount + 1;
}
// Gets a bit sequence with "1"s wherever there is an open "slot".
// All that's happening here is we're taking col, ld, and rd, and if any of the columns are
// "under attack", we mark that column as 0 in poss, basically meaning "we can't put a queen in
// this column". Thus all bits position in poss that are '1's are available for placing
// queen there.
let availablePositions = ~(leftDiagonal | rightDiagonal | column);
// Loops as long as there is a valid place to put another queen.
// For N=4 the isDone=0b1111. Then if availablePositions=0b0000 (which would mean that all places
// are under threatening) we must stop trying to place a queen.
while (availablePositions & isDone) {
// firstAvailablePosition just stores the first non-zero bit (ie. the first available location).
// So if firstAvailablePosition was 0010, it would mean the 3rd column of the current row.
// And that would be the position will be placing our next queen.
//
// For example:
// availablePositions = 0b01100
// firstAvailablePosition = 100
const firstAvailablePosition = availablePositions & -availablePositions;
// This line just marks that position in the current row as being "taken" by flipping that
// column in availablePositions to zero. This way, when the while loop continues, we'll know
// not to try that location again.
//
// For example:
// availablePositions = 0b0100
// firstAvailablePosition = 0b10
// 0b0110 - 0b10 = 0b0100
availablePositions -= firstAvailablePosition;
/*
* The operators >> 1 and 1 << simply move all the bits in a bit sequence one digit to the
* right or left, respectively. So calling (rd|bit)<<1 simply says: combine rd and bit with
* an OR operation, then move everything in the result to the left by one digit.
*
* More specifically, if rd is 0001 (meaning that the top-right-to-bottom-left diagonal through
* column 4 of the current row is occupied), and bit is 0100 (meaning that we are planning to
* place a queen in column 2 of the current row), (rd|bit) results in 0101 (meaning that after
* we place a queen in column 2 of the current row, the second and the fourth
* top-right-to-bottom-left diagonals will be occupied).
*
* Now, if add in the << operator, we get (rd|bit)<<1, which takes the 0101 we worked out in
* our previous bullet point, and moves everything to the left by one. The result, therefore,
* is 1010.
*/
currentSolutionsCount += nQueensBitwiseRecursive(
boardSize,
(leftDiagonal | firstAvailablePosition) >> 1,
column | firstAvailablePosition,
(rightDiagonal | firstAvailablePosition) << 1,
solutionsCount,
);
}
return currentSolutionsCount;
}
/**
* @param {number} boardSize - Size of the squared chess board.
* @return {number} - Number of possible solutions.
* @see http://gregtrowbridge.com/a-bitwise-solution-to-the-n-queens-problem-in-javascript/
*/
export default function nQueensBitwise(boardSize) {
return nQueensBitwiseRecursive(boardSize);
}