216 lines
5.2 KiB
JavaScript
216 lines
5.2 KiB
JavaScript
class KnightTour {
|
|
constructor() {
|
|
this.width = 856;
|
|
this.height = 856;
|
|
this.cellCount = 8;
|
|
this.size = 0;
|
|
this.knightPiece = "\u2658";
|
|
this.knightPos = {
|
|
x: 0,
|
|
y: 0
|
|
};
|
|
this.ctx = null;
|
|
this.step = this.width / this.cellCount;
|
|
this.lastTime = 0;
|
|
this.wait;
|
|
this.delay;
|
|
this.success;
|
|
this.jumps;
|
|
this.directions = [];
|
|
this.visited = [];
|
|
this.path = [];
|
|
document.getElementById("start").addEventListener("click", () => {
|
|
this.startHtml();
|
|
});
|
|
this.init();
|
|
this.drawBoard();
|
|
}
|
|
|
|
drawBoard() {
|
|
let a = false, xx, yy;
|
|
for (let y = 0; y < this.cellCount; y++) {
|
|
for (let x = 0; x < this.cellCount; x++) {
|
|
if (a) {
|
|
this.ctx.fillStyle = "#607db8";
|
|
} else {
|
|
this.ctx.fillStyle = "#aecaf0";
|
|
}
|
|
a = !a;
|
|
xx = x * this.step;
|
|
yy = y * this.step;
|
|
this.ctx.fillRect(xx, yy, xx + this.step, yy + this.step);
|
|
}
|
|
if (!(this.cellCount & 1)) a = !a;
|
|
}
|
|
if (this.path.length) {
|
|
const s = this.step >> 1;
|
|
this.ctx.lineWidth = 3;
|
|
this.ctx.fillStyle = "black";
|
|
this.ctx.beginPath();
|
|
this.ctx.moveTo(this.step * this.knightPos.x + s, this.step * this.knightPos.y + s);
|
|
let a, b, v = this.path.length - 1;
|
|
for (; v > -1; v--) {
|
|
a = this.path[v].pos.x * this.step + s;
|
|
b = this.path[v].pos.y * this.step + s;
|
|
this.ctx.lineTo(a, b);
|
|
this.ctx.fillRect(a - 5, b - 5, 10, 10);
|
|
}
|
|
this.ctx.stroke();
|
|
}
|
|
}
|
|
|
|
createMoves(pos) {
|
|
const possibles = [];
|
|
let x = 0,
|
|
y = 0,
|
|
m = 0,
|
|
l = this.directions.length;
|
|
for (; m < l; m++) {
|
|
x = pos.x + this.directions[m].x;
|
|
y = pos.y + this.directions[m].y;
|
|
if (x > -1 && x < this.cellCount && y > -1 && y < this.cellCount && !this.visited[x + y * this.cellCount]) {
|
|
possibles.push({
|
|
x,
|
|
y
|
|
})
|
|
}
|
|
}
|
|
return possibles;
|
|
}
|
|
|
|
warnsdorff(pos) {
|
|
const possibles = this.createMoves(pos);
|
|
if (possibles.length < 1) return [];
|
|
const moves = [];
|
|
for (let p = 0, l = possibles.length; p < l; p++) {
|
|
let ps = this.createMoves(possibles[p]);
|
|
moves.push({
|
|
len: ps.length,
|
|
pos: possibles[p]
|
|
});
|
|
}
|
|
moves.sort((a, b) => {
|
|
return b.len - a.len;
|
|
});
|
|
return moves;
|
|
}
|
|
|
|
startHtml() {
|
|
this.cellCount = parseInt(document.getElementById("cellCount").value);
|
|
this.size = Math.floor(this.width / this.cellCount)
|
|
this.wait = this.delay = parseInt(document.getElementById("delay").value);
|
|
this.step = this.width / this.cellCount;
|
|
this.ctx.font = this.size + "px Arial";
|
|
document.getElementById("log").innerText = "";
|
|
document.getElementById("path").innerText = "";
|
|
this.path = [];
|
|
this.jumps = 1;
|
|
this.success = true;
|
|
this.visited = [];
|
|
const cnt = this.cellCount * this.cellCount;
|
|
for (let a = 0; a < cnt; a++) {
|
|
this.visited.push(false);
|
|
}
|
|
const kx = parseInt(document.getElementById("knightx").value),
|
|
ky = parseInt(document.getElementById("knighty").value);
|
|
this.knightPos = {
|
|
x: (kx > this.cellCount || kx < 0) ? Math.floor(Math.random() * this.cellCount) : kx,
|
|
y: (ky > this.cellCount || ky < 0) ? Math.floor(Math.random() * this.cellCount) : ky
|
|
};
|
|
this.mainLoop = (time = 0) => {
|
|
const dif = time - this.lastTime;
|
|
this.lastTime = time;
|
|
this.wait -= dif;
|
|
if (this.wait > 0) {
|
|
requestAnimationFrame(this.mainLoop);
|
|
return;
|
|
}
|
|
this.wait = this.delay;
|
|
let moves;
|
|
if (this.success) {
|
|
moves = this.warnsdorff(this.knightPos);
|
|
} else {
|
|
if (this.path.length > 0) {
|
|
const path = this.path[this.path.length - 1];
|
|
moves = path.m;
|
|
if (moves.length < 1) this.path.pop();
|
|
this.knightPos = path.pos
|
|
this.visited[this.knightPos.x + this.knightPos.y * this.cellCount] = false;
|
|
this.jumps--;
|
|
this.wait = this.delay;
|
|
} else {
|
|
document.getElementById("log").innerText = "Can't find a solution!";
|
|
return;
|
|
}
|
|
}
|
|
this.drawBoard();
|
|
const ft = this.step - (this.step >> 3);
|
|
this.ctx.fillStyle = "#000";
|
|
this.ctx.fillText(this.knightPiece, this.knightPos.x * this.step, this.knightPos.y * this.step + ft);
|
|
if (moves.length < 1) {
|
|
if (this.jumps === this.cellCount * this.cellCount) {
|
|
document.getElementById("log").innerText = "Tour finished!";
|
|
let str = "";
|
|
for (let z of this.path) {
|
|
str += `${1 + z.pos.x + z.pos.y * this.cellCount}, `;
|
|
}
|
|
str += `${1 + this.knightPos.x + this.knightPos.y * this.cellCount}`;
|
|
document.getElementById("path").innerText = str;
|
|
return;
|
|
} else {
|
|
this.success = false;
|
|
}
|
|
} else {
|
|
this.visited[this.knightPos.x + this.knightPos.y * this.cellCount] = true;
|
|
const move = moves.pop();
|
|
this.path.push({
|
|
pos: this.knightPos,
|
|
m: moves
|
|
});
|
|
this.knightPos = move.pos
|
|
this.success = true;
|
|
this.jumps++;
|
|
}
|
|
requestAnimationFrame(this.mainLoop);
|
|
};
|
|
this.mainLoop();
|
|
}
|
|
|
|
init() {
|
|
const canvas = document.createElement("canvas");
|
|
canvas.id = "cv";
|
|
canvas.width = this.width;
|
|
canvas.height = this.height;
|
|
this.ctx = canvas.getContext("2d");
|
|
document.getElementById("out").appendChild(canvas);
|
|
this.directions = [{
|
|
x: -1,
|
|
y: -2
|
|
}, {
|
|
x: -2,
|
|
y: -1
|
|
}, {
|
|
x: 1,
|
|
y: -2
|
|
}, {
|
|
x: 2,
|
|
y: -1
|
|
},
|
|
{
|
|
x: -1,
|
|
y: 2
|
|
}, {
|
|
x: -2,
|
|
y: 1
|
|
}, {
|
|
x: 1,
|
|
y: 2
|
|
}, {
|
|
x: 2,
|
|
y: 1
|
|
}
|
|
];
|
|
}
|
|
}
|
|
new KnightTour();
|