186 lines
4.7 KiB
JavaScript
186 lines
4.7 KiB
JavaScript
let paused = true;
|
|
let autoStart = true;
|
|
let workers = [];
|
|
let totalTime = 0;
|
|
let startedAt;
|
|
let totalAttempts = 0;
|
|
let names = [];
|
|
|
|
fetch("names.json").then(res => res.json()).then(json => names = json);
|
|
|
|
function showStats() {
|
|
const time = totalTime + (Date.now() - startedAt);
|
|
|
|
document.getElementById("attempts").innerText = totalAttempts;
|
|
document.getElementById("speed").innerText = (totalAttempts / time).toFixed(2);
|
|
}
|
|
|
|
function log(text, workerId) {
|
|
const time = new Date().toLocaleTimeString("en-GB");
|
|
|
|
let html = `<time>${time}</time> `;
|
|
if (workerId) html += `<span class="worker-id">[${workerId}]</span> `;
|
|
html += `${text}<br>`;
|
|
|
|
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() {
|
|
const numbers = [].map.call(document.getElementsByClassName("number-input"), input => input.valueAsNumber)
|
|
.filter(value => !isNaN(value));
|
|
|
|
if (numbers.length < 4) return;
|
|
|
|
if (paused && autoStart) setPaused(false);
|
|
|
|
startedAt = Date.now();
|
|
|
|
const selectedWinType = document.querySelector("input[name=win-type]:checked");
|
|
const winType = selectedWinType.value;
|
|
const winTypeLabel = selectedWinType.parentElement.innerText.trim();
|
|
|
|
resetWorkers();
|
|
|
|
const amount = document.getElementById("workers").valueAsNumber;
|
|
const batchSize = document.getElementById("batch-size").valueAsNumber;
|
|
|
|
log(`Creating ${amount} workers`);
|
|
log(`* Win type: ${winType}`);
|
|
log(`* Numbers: ${numbers.join(", ")}`);
|
|
|
|
const usedNames = [];
|
|
|
|
for (let i = 0; i < amount; i++) {
|
|
let name;
|
|
do
|
|
name = names[Math.floor(Math.random() * names.length)];
|
|
while (usedNames.includes(name));
|
|
usedNames.push(name);
|
|
|
|
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":
|
|
totalAttempts += message.data.attempts;
|
|
|
|
showStats();
|
|
break;
|
|
case "log":
|
|
log(message.data.text, message.data.id);
|
|
break;
|
|
case "winner":
|
|
document.getElementById("result-list").innerHTML +=
|
|
`<div>
|
|
<span class="winner">Winner:</span> <b>${message.data.name}</b>
|
|
<br>
|
|
<small>(${winTypeLabel} — ${numbers.join(", ")})</small>
|
|
</div>`;
|
|
|
|
setPaused(true);
|
|
autoStart = true;
|
|
case "terminate":
|
|
worker.terminate();
|
|
workers.splice(workers.indexOf(worker), 1);
|
|
break;
|
|
}
|
|
});
|
|
|
|
worker.postMessage({ type: "init", id: i + 1, numbers, winType, name, batchSize });
|
|
workers.push(worker);
|
|
}
|
|
}
|
|
|
|
function setPaused(value) {
|
|
paused = value;
|
|
|
|
for (const worker of workers) {
|
|
worker.postMessage({ type: paused ? "stop" : "run" });
|
|
}
|
|
|
|
document.getElementById("pause").innerText = paused ? "Start" : "Pause";
|
|
|
|
if (paused)
|
|
totalTime += (Date.now() - startedAt);
|
|
|
|
startedAt = Date.now();
|
|
}
|
|
|
|
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.className = "number-input";
|
|
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 => {
|
|
setPaused(!paused);
|
|
autoStart = false;
|
|
});
|
|
|
|
document.getElementById("workers").value = navigator.hardwareConcurrency ?? 1;
|
|
|
|
for (const container of document.getElementsByClassName("input-container")) {
|
|
container.addEventListener("change", runWorkers);
|
|
}
|
|
|