let paused = false; let workers = []; let totalTime = 0; let totalAttempts = 0; function showStats() { document.getElementById("attempts").innerText = totalAttempts; document.getElementById("speed").innerText = (totalAttempts / totalTime).toFixed(2); } function log(text, workerId) { const time = new Date().toLocaleTimeString("en-GB"); let html = ` `; if (workerId) html += `[${workerId}] `; html += `${text}
`; document.getElementById("log").innerHTML += html; document.getElementById("log").scrollTo(0, document.getElementById("log").scrollHeight); } function resetWorkers() { log("Terminating workers"); for (const worker of workers) { worker.postMessage({ type: "terminate" }); } totalTime = 0; totalAttempts = 0; } function runWorkers() { resetWorkers(); const amount = document.getElementById("workers").valueAsNumber; log(`Creating ${amount} workers`); for (let i = 0; i < amount; i++) { const worker = new Worker("worker.js"); worker.addEventListener("message", message => { switch (message.data.type) { case "ready": if (!paused) worker.postMessage({ type: "run" }); break; case "stats": totalTime += message.data.time; totalAttempts += message.data.attempts; showStats(); break; case "log": log(message.data.text, message.data.id); break; case "terminate": worker.terminate(); workers.splice(workers.indexOf(worker), 1); break; } }); worker.postMessage({ type: "init", id: i + 1 }); workers.push(worker); } } document.getElementById("numbers-container").addEventListener("keydown", event => { // Add new input field on enter if (event.key === "Enter") { const input = document.createElement("input"); input.type = "number"; input.min = 1; input.max = 90; event.target.parentElement.appendChild(input); input.focus(); return; } // Remove empty input field on backspace if (event.key === "Backspace" || event.key === "Delete") { if (event.target.value !== "" || document.querySelectorAll("#numbers-container input").length < 2) return; if (event.target.previousElementSibling.localName === "input") event.target.previousElementSibling.focus(); else event.target.nextElementSibling.focus(); event.target.parentElement.removeChild(event.target); event.preventDefault(); return; } // Move between inputs on arrow up/down if (event.key === "ArrowUp" || event.key === "ArrowDown") { event.preventDefault(); const elem = event.key === "ArrowUp" ? event.target.previousElementSibling : event.target.nextElementSibling; if (!elem || elem.localName !== "input") return; elem.focus(); return; } // Disallow non-digits const allowed = [/^Digit/, /^Arrow/, /Tab/]; if (!event.altKey && !event.ctrlKey && !allowed.some(regex => regex.test(event.code))) { event.preventDefault(); } }); document.getElementById("pause").addEventListener("click", event => { paused = !paused; for (const worker of workers) { worker.postMessage({ type: paused ? "stop" : "run" }); } event.target.innerText = paused ? "Start" : "Pause"; }); document.getElementById("workers").value = navigator.hardwareConcurrency ?? 1; for (const container of document.getElementsByClassName("input-container")) { container.addEventListener("change", runWorkers); }