2019-03-21 06:22:35 +00:00
---
id: 5a23c84252665b21eecc7ed5
title: Knight's tour
challengeType: 5
2019-08-05 16:17:33 +00:00
forumTopicId: 302297
2021-01-13 02:31:00 +00:00
dashedName: knights-tour
2019-03-21 06:22:35 +00:00
---
2020-11-27 18:02:05 +00:00
# --description--
2022-06-02 18:03:03 +00:00
Knight's Tour Problem: You have an empty `width` \* `height` chessboard, but for a single knight on some square. The knight must perform a sequence of legal moves that result in the knight visiting every square on the chessboard exactly once. Note that it is *not* a requirement that the tour be "closed"; that is, the knight need not end within a single move of its start position.
2020-11-27 18:02:05 +00:00
# --instructions--
2022-06-02 18:03:03 +00:00
Write a function that takes `width` and `height` as parameters and returns the number of initial positions from where it is possible to achieve the task stated above.
2020-11-27 18:02:05 +00:00
# --hints--
`knightTour` should be a function.
```js
assert(typeof knightTour == 'function');
2019-03-21 06:22:35 +00:00
```
2020-11-27 18:02:05 +00:00
`knightTour(6, 6)` should return a number.
2019-03-21 06:22:35 +00:00
2020-11-27 18:02:05 +00:00
```js
assert(typeof knightTour(6, 6) == 'number');
```
2020-03-30 16:23:18 +00:00
2021-02-03 18:43:29 +00:00
`knightTour(6, 6)` should return `36` .
2019-03-21 06:22:35 +00:00
```js
2021-02-03 18:43:29 +00:00
assert.equal(knightTour(6, 6), 36);
2020-11-27 18:02:05 +00:00
```
2020-09-15 16:57:40 +00:00
2021-02-03 18:43:29 +00:00
`knightTour(5, 6)` should return `30` .
2020-11-27 18:02:05 +00:00
```js
2021-02-03 18:43:29 +00:00
assert.equal(knightTour(5, 6), 30);
2019-03-21 06:22:35 +00:00
```
2021-02-03 18:43:29 +00:00
`knightTour(4, 6)` should return `12` .
2019-03-21 06:22:35 +00:00
2020-11-27 18:02:05 +00:00
```js
2021-02-03 18:43:29 +00:00
assert.equal(knightTour(4, 6), 12);
2020-11-27 18:02:05 +00:00
```
2020-03-30 16:23:18 +00:00
2021-02-03 18:43:29 +00:00
`knightTour(7, 3)` should return `10` .
2020-11-27 18:02:05 +00:00
```js
2021-02-03 18:43:29 +00:00
assert.equal(knightTour(7, 3), 10);
2020-11-27 18:02:05 +00:00
```
2021-02-03 18:43:29 +00:00
`knightTour(8, 6)` should return `48` .
2020-11-27 18:02:05 +00:00
```js
2021-02-03 18:43:29 +00:00
assert.equal(knightTour(8, 6), 48);
2020-11-27 18:02:05 +00:00
```
# --seed--
## --seed-contents--
```js
2022-06-02 18:03:03 +00:00
function knightTour(width, height) {
2020-11-27 18:02:05 +00:00
}
```
# --solutions--
2019-03-21 06:22:35 +00:00
```js
2022-06-02 18:03:03 +00:00
function knightTour(width, height) {
2021-02-03 18:43:29 +00:00
function createBoards(rows, columns) {
const board = [];
const visited = [];
for (let i = 0; i < rows ; i + + ) {
board.push(new Array(columns).fill(-1));
visited.push(new Array(columns).fill(false));
}
return [board, visited];
}
2019-03-21 06:22:35 +00:00
2021-02-03 18:43:29 +00:00
function copyBoard(board) {
const copied = [];
for (let i = 0; i < board.length ; i + + ) {
copied.push([...board[i]]);
}
return copied;
}
2019-03-21 06:22:35 +00:00
2021-02-03 18:43:29 +00:00
function isOnBoard(value, limit) {
return value >= 0 & & value < limit ;
}
2019-03-21 06:22:35 +00:00
2021-02-03 18:43:29 +00:00
function markVisited(board, visited, row, column) {
visited[row][column] = true;
board[row][column] = -1;
}
function areAllVisited(visited) {
return (
visited.filter(row => row.filter(column => column === false).length !== 0)
.length === 0
);
}
function getMovesFrom(board, row, column) {
const possibleMoves = [];
for (let i = 0; i < moves.length ; i + + ) {
const [rowChange, colChange] = moves[i];
const [rowN, colN] = [row + rowChange, column + colChange];
if (!isOnBoard(rowN, board.length) || !isOnBoard(colN, board[0].length)) {
continue;
2019-03-21 06:22:35 +00:00
}
2021-02-03 18:43:29 +00:00
possibleMoves.push([rowN, colN]);
2019-03-21 06:22:35 +00:00
}
2021-02-03 18:43:29 +00:00
return possibleMoves;
}
2019-03-21 06:22:35 +00:00
2021-02-03 18:43:29 +00:00
function fillAllowedMovesCounts(board) {
for (let row = 0; row < board.length ; row + + ) {
for (let column = 0; column < board [ 0 ] . length ; column + + ) {
board[row][column] = getMovesFrom(board, row, column).length;
2020-03-30 16:23:18 +00:00
}
}
2019-03-21 06:22:35 +00:00
}
2021-02-03 18:43:29 +00:00
function updateAllowedMovesCounts(board, possibleMoves) {
for (let i = 0; i < possibleMoves.length ; i + + ) {
const [row, column] = possibleMoves[i];
if (board[row][column] > 0) {
board[row][column]--;
2020-03-30 16:23:18 +00:00
}
2021-02-03 18:43:29 +00:00
}
}
2020-03-30 16:23:18 +00:00
2021-02-03 18:43:29 +00:00
function getBestNextMoves(board, allowedMoves) {
let bestMoves = [];
let fewestNextMoves = Infinity;
let zeroMove = [];
for (let i = 0; i < allowedMoves.length ; i + + ) {
const [moveRow, moveCol] = allowedMoves[i];
const numMoves = board[moveRow][moveCol];
if (numMoves === -1) {
continue;
}
if (numMoves === 0) {
zeroMove.push(allowedMoves[i]);
}
if (numMoves < fewestNextMoves ) {
bestMoves = [allowedMoves[i]];
fewestNextMoves = numMoves;
} else if (numMoves === fewestNextMoves) {
bestMoves.push(allowedMoves[i]);
2020-03-30 16:23:18 +00:00
}
2021-02-03 18:43:29 +00:00
}
2019-03-21 06:22:35 +00:00
2021-02-03 18:43:29 +00:00
if (bestMoves.length > 0) {
return bestMoves;
2020-03-30 16:23:18 +00:00
}
2021-02-03 18:43:29 +00:00
return zeroMove;
2019-03-21 06:22:35 +00:00
}
2021-02-03 18:43:29 +00:00
function solve(board, visited, lastRow, lastColumn) {
if (areAllVisited(visited)) {
return true;
2020-03-30 16:23:18 +00:00
}
2021-02-03 18:43:29 +00:00
const nextMoves = getMovesFrom(board, lastRow, lastColumn);
updateAllowedMovesCounts(board, nextMoves);
const allowedMoves = nextMoves.filter(
([row, column]) => !visited[row][column]
);
const bestMoves = getBestNextMoves(board, allowedMoves);
const restMoves = allowedMoves.filter(
move => bestMoves.indexOf(move) === -1
);
const possibleMoves = [...bestMoves];
possibleMoves.push(...getBestNextMoves(board, restMoves));
for (let i = 0; i < possibleMoves.length ; i + + ) {
const [moveRow, moveCol] = possibleMoves[i];
const newBoard = copyBoard(board);
const newVisited = copyBoard(visited);
markVisited(newBoard, newVisited, moveRow, moveCol);
if (solve(newBoard, newVisited, moveRow, moveCol)) {
return true;
}
}
return false;
2019-03-21 06:22:35 +00:00
}
2021-02-03 18:43:29 +00:00
function solveStart(board, visited, startRow, startColumn) {
const newBoard = copyBoard(board);
const newVisited = copyBoard(visited);
markVisited(newBoard, newVisited, startRow, startColumn);
return solve(newBoard, newVisited, startRow, startColumn);
2019-03-21 06:22:35 +00:00
}
2021-02-03 18:43:29 +00:00
const moves = [
[-1, -2],
[-2, -1],
[-2, 1],
[-1, 2],
[1, 2],
[2, 1],
[2, -1],
[1, -2]
];
2022-06-02 18:03:03 +00:00
const [baseBoard, baseVisited] = createBoards(height, width);
2021-02-03 18:43:29 +00:00
fillAllowedMovesCounts(baseBoard);
let solvedCount = 0;
2022-06-02 18:03:03 +00:00
for (let row = 0; row < height ; row + + ) {
for (let column = 0; column < width ; column + + ) {
2021-02-03 18:43:29 +00:00
if (solveStart(baseBoard, baseVisited, row, column)) {
solvedCount++;
}
}
}
return solvedCount;
2019-03-21 06:22:35 +00:00
}
```