mirror of
https://github.com/trekhleb/javascript-algorithms.git
synced 2025-12-08 19:06:00 +00:00
102 lines
4.7 KiB
JavaScript
102 lines
4.7 KiB
JavaScript
/**
|
|
* 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);
|
|
}
|