let paused = false; 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 = ` `; 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() { const numbers = [].map.call(document.getElementsByClassName("number-input"), input => input.valueAsNumber) .filter(value => !isNaN(value)); if (numbers.length < 4) return; if (paused && !autoStart) return; 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 += `
Winner: ${message.data.name}
(${winTypeLabel} — ${numbers.join(", ")})
`; 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); }