fix: correct Rosetta Code Knight's tour solution (#40830)
* fix: correct answers in tests and descriptions * fix: replace sample solution for correct one * fix: missing space * fix: improve sample solution performance
This commit is contained in:
@ -8,7 +8,7 @@ dashedName: knights-tour
|
||||
|
||||
# --description--
|
||||
|
||||
[Knight's Tour](https://en.wikipedia.org/wiki/Knight%27s_tour)Problem: You have an empty `w` \* `h` 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.
|
||||
[Knight's Tour](https://en.wikipedia.org/wiki/Knight%27s_tour) Problem: You have an empty `w` \* `h` 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.
|
||||
|
||||
# --instructions--
|
||||
|
||||
@ -28,34 +28,34 @@ assert(typeof knightTour == 'function');
|
||||
assert(typeof knightTour(6, 6) == 'number');
|
||||
```
|
||||
|
||||
`knightTour(6, 6)` should return `35`.
|
||||
`knightTour(6, 6)` should return `36`.
|
||||
|
||||
```js
|
||||
assert.equal(knightTour(6, 6), 35);
|
||||
assert.equal(knightTour(6, 6), 36);
|
||||
```
|
||||
|
||||
`knightTour(5, 6)` should return `20`.
|
||||
`knightTour(5, 6)` should return `30`.
|
||||
|
||||
```js
|
||||
assert.equal(knightTour(5, 6), 20);
|
||||
assert.equal(knightTour(5, 6), 30);
|
||||
```
|
||||
|
||||
`knightTour(4, 6)` should return `10`.
|
||||
`knightTour(4, 6)` should return `12`.
|
||||
|
||||
```js
|
||||
assert.equal(knightTour(4, 6), 10);
|
||||
assert.equal(knightTour(4, 6), 12);
|
||||
```
|
||||
|
||||
`knightTour(7, 3)` should return `4`.
|
||||
`knightTour(7, 3)` should return `10`.
|
||||
|
||||
```js
|
||||
assert.equal(knightTour(7, 3), 4);
|
||||
assert.equal(knightTour(7, 3), 10);
|
||||
```
|
||||
|
||||
`knightTour(8, 6)` should return `47`.
|
||||
`knightTour(8, 6)` should return `48`.
|
||||
|
||||
```js
|
||||
assert.equal(knightTour(8, 6), 47);
|
||||
assert.equal(knightTour(8, 6), 48);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
@ -72,83 +72,154 @@ function knightTour(w, h) {
|
||||
|
||||
```js
|
||||
function knightTour(w, h) {
|
||||
var b,
|
||||
cnt = 0;
|
||||
|
||||
var dx = [-2, -2, -1, 1, 2, 2, 1, -1];
|
||||
var dy = [-1, 1, 2, 2, 1, -1, -2, -2];
|
||||
|
||||
function init_board() {
|
||||
var i, j, k, x, y;
|
||||
// * b is board; a is board with 2 rows padded at each side
|
||||
|
||||
for (i = 0; i < h; i++) {
|
||||
for (j = 0; j < w; j++) {
|
||||
b[i][j] = 255;
|
||||
}
|
||||
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];
|
||||
}
|
||||
|
||||
for (i = 0; i < h; i++) {
|
||||
for (j = 0; j < w; j++) {
|
||||
for (k = 0; k < 8; k++) {
|
||||
(x = j + dx[k]), (y = i + dy[k]);
|
||||
if (b[i][j] == 255) b[i][j] = 0;
|
||||
if (x >= 0 && x < w && y >= 0 && y < h) b[i][j]++;
|
||||
}
|
||||
function copyBoard(board) {
|
||||
const copied = [];
|
||||
for (let i = 0; i < board.length; i++) {
|
||||
copied.push([...board[i]]);
|
||||
}
|
||||
return copied;
|
||||
}
|
||||
|
||||
function isOnBoard(value, limit) {
|
||||
return value >= 0 && value < limit;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
possibleMoves.push([rowN, colN]);
|
||||
}
|
||||
return possibleMoves;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function walk_board(x, y) {
|
||||
var i, nx, ny, least;
|
||||
var steps = 0;
|
||||
// printf(E"H"E"J"E"%d;%dH"E"32m[]"E"m", y + 1, 1 + 2 * x);
|
||||
|
||||
while (1) {
|
||||
// * occupy cell
|
||||
b[y][x] = 255;
|
||||
|
||||
// * reduce all neighbors' neighbor count
|
||||
for (i = 0; i < 8; i++)
|
||||
if (y + dy[i] >= 0 && x + dx[i] >= 0 && y + dy[i] < h && x + dx[i] < w)
|
||||
b[y + dy[i]][x + dx[i]]--;
|
||||
|
||||
// find neighbor with lowest neighbor count
|
||||
least = 255;
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (y + dy[i] >= 0 && x + dx[i] >= 0 && y + dy[i] < h && x + dx[i] < w)
|
||||
if (b[y + dy[i]][x + dx[i]] < least) {
|
||||
nx = x + dx[i];
|
||||
ny = y + dy[i];
|
||||
least = b[ny][nx];
|
||||
}
|
||||
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]--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (least > 7) {
|
||||
return steps == w * h - 1;
|
||||
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]);
|
||||
}
|
||||
|
||||
steps++;
|
||||
(x = nx), (y = ny);
|
||||
}
|
||||
|
||||
if (bestMoves.length > 0) {
|
||||
return bestMoves;
|
||||
}
|
||||
return zeroMove;
|
||||
}
|
||||
|
||||
function solve(x, y) {
|
||||
b = new Array(h);
|
||||
for (var i = 0; i < h; i++) b[i] = new Array(w);
|
||||
|
||||
init_board();
|
||||
if (walk_board(x, y)) {
|
||||
cnt++;
|
||||
function solve(board, visited, lastRow, lastColumn) {
|
||||
if (areAllVisited(visited)) {
|
||||
return true;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
for (var i = 0; i < h; i++) {
|
||||
for (var j = 0; j < w; j++) {
|
||||
solve(j, i);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
return cnt;
|
||||
const moves = [
|
||||
[-1, -2],
|
||||
[-2, -1],
|
||||
[-2, 1],
|
||||
[-1, 2],
|
||||
[1, 2],
|
||||
[2, 1],
|
||||
[2, -1],
|
||||
[1, -2]
|
||||
];
|
||||
|
||||
const [baseBoard, baseVisited] = createBoards(h, w);
|
||||
fillAllowedMovesCounts(baseBoard);
|
||||
let solvedCount = 0;
|
||||
for (let row = 0; row < h; row++) {
|
||||
for (let column = 0; column < w; column++) {
|
||||
if (solveStart(baseBoard, baseVisited, row, column)) {
|
||||
solvedCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return solvedCount;
|
||||
}
|
||||
```
|
||||
|
Reference in New Issue
Block a user