commit 6f358ec9774f3ae10f135a95676d92f1c9d92210 Author: Reimar Date: Sat Apr 12 13:41:28 2025 +0200 Initial commit diff --git a/generator.js b/generator.js new file mode 100644 index 0000000..3616abf --- /dev/null +++ b/generator.js @@ -0,0 +1,103 @@ +function myFunction(elmnt) { + console.log(elmnt.style.backgroundColor); + if (elmnt.style.backgroundColor == "white") { + elmnt.style.backgroundColor = "hsl(120, 100%, 75%)"; + } else { + elmnt.style.backgroundColor = "white"; + } +} +var værdi; +var celle; +function nyfunktion(værdi, celle) { + celle.innerHTML = værdi; +} + +function gen_int(i) { + var number = Math.floor(Math.random() * 10 + i * 10); + while (number === 0) { + var number = Math.floor(Math.random() * 10 + i * 10); + } + if (i === 8) { + var number = Math.floor(Math.random() * 11 + 80); + } + if (i === 100) { + var number = Math.ceil(Math.random() * 9); + } + return number; +} + +function hasDuplicates(array) { + return new Set(array).size !== array.length; +} + +function generate_col(i) { + var col = Array.from({ length: 3 }, () => gen_int(i)); + while (hasDuplicates(col) == true) { + var col = Array.from({ length: 3 }, () => gen_int(i)); + } + return col.sort(); +} + +function generate_row() { + var row = Array.from({ length: 5 }, () => gen_int(100)); + while (hasDuplicates(row) == true) { + var row = Array.from({ length: 5 }, () => gen_int(100)); + } + return row.sort(); +} + +function contains_digits(rows) { + var concat = rows[0].concat(rows[1], rows[2]); + for (var i = 1; i < 9; i++) { + if (concat.indexOf(i) === -1) { + return false; + } + } + + if (concat.indexOf(9) === -1) { + return false; + } + return true; +} +function generate_rows_check() { + console.log(rows); + var rows = new Array(generate_row(), generate_row(), generate_row()); + while (!contains_digits(rows)) { + var rows = new Array(generate_row(), generate_row(), generate_row()); + } + return rows; +} +function update_plates() { + for (var x2 = 1; x2 <= 3; x2++) { + for (var x3 = 1; x3 <= 9; x3++) { + var celle = "p1" + String(x2) + String(x3); + document.getElementById(celle).innerHTML = ""; + } + } + var dict = {}; + for (var n = 1; n <= 3; n++) { + var cols = []; + for (var i = 0; i < 9; i++) { + var col = generate_col(i); + cols.push(col); + } + + var rows_choose = generate_rows_check(); + + var chosen_row; + + for (var j = 0; j < 3; j++) { + for (var i = 0; i < rows_choose[j].length; i++) { + var k = rows_choose[j][i]; + var celle = "p" + String(n) + String(j + 1) + String(k); + dict[celle] = cols[k - 1][j]; + } + } + } + + for (var key in dict) { + var value = dict[key]; + nyfunktion(value, document.getElementById(key)); + } +} + diff --git a/index.html b/index.html new file mode 100644 index 0000000..e9400aa --- /dev/null +++ b/index.html @@ -0,0 +1,61 @@ + + + + + MAGS Banko Cheat + + + + +

Banko Cheat

+ +
+
+
+ Win type + + + + + +
+ +
+ Workers + +
+
+ +
+
+ Numbers + +
+
+ +
+ +   + + -- plates tried   + (--/sec) + +
+ +
+ Log +
+
+
+ + + diff --git a/main.js b/main.js new file mode 100644 index 0000000..1dd7e11 --- /dev/null +++ b/main.js @@ -0,0 +1,134 @@ +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); +} + diff --git a/seedrandom.js b/seedrandom.js new file mode 100644 index 0000000..28a51ee --- /dev/null +++ b/seedrandom.js @@ -0,0 +1,236 @@ +(function (global, pool, math, width, chunks, digits, module, define, rngname) { + // + // The following constants are related to IEEE 754 limits. + // + var startdenom = math.pow(width, chunks), + significance = math.pow(2, digits), + overflow = significance * 2, + mask = width - 1, + nodecrypto; + + // + // seedrandom() + // This is the seedrandom function described above. + // + var impl = (math["seed" + rngname] = function (seed, options, callback) { + var key = []; + options = options == true ? { entropy: true } : options || {}; + + // Flatten the seed string or build one from local entropy if needed. + var shortseed = mixkey( + flatten( + options.entropy + ? [seed, tostring(pool)] + : seed == null + ? autoseed() + : seed, + 3 + ), + key + ); + + // Use the seed to initialize an ARC4 generator. + var arc4 = new ARC4(key); + + // Mix the randomness into accumulated entropy. + mixkey(tostring(arc4.S), pool); + + // Calling convention: what to return as a function of prng, seed, is_math. + return ( + options.pass || + callback || + // If called as a method of Math (Math.seedrandom()), mutate Math.random + // because that is how seedrandom.js has worked since v1.0. Otherwise, + // it is a newer calling convention, so return the prng directly. + function (prng, seed, is_math_call) { + if (is_math_call) { + math[rngname] = prng; + return seed; + } else return prng; + } + )( + // This function returns a random double in [0, 1) that contains + // randomness in every bit of the mantissa of the IEEE 754 value. + function () { + var n = arc4.g(chunks), // Start with a numerator n < 2 ^ 48 + d = startdenom, // and denominator d = 2 ^ 48. + x = 0; // and no 'extra last byte'. + while (n < significance) { + // Fill up all significant digits by + n = (n + x) * width; // shifting numerator and + d *= width; // denominator and generating a + x = arc4.g(1); // new least-significant-byte. + } + while (n >= overflow) { + // To avoid rounding up, before adding + n /= 2; // last byte, shift everything + d /= 2; // right using integer math until + x >>>= 1; // we have exactly the desired bits. + } + return (n + x) / d; // Form the number within [0, 1). + }, + shortseed, + "global" in options ? options.global : this == math + ); + }); + + // + // ARC4 + // + // An ARC4 implementation. The constructor takes a key in the form of + // an array of at most (width) integers that should be 0 <= x < (width). + // + // The g(count) method returns a pseudorandom integer that concatenates + // the next (count) outputs from ARC4. Its return value is a number x + // that is in the range 0 <= x < (width ^ count). + // + /** @constructor */ + function ARC4(key) { + var t, + keylen = key.length, + me = this, + i = 0, + j = (me.i = me.j = 0), + s = (me.S = []); + + // The empty key [] is treated as [0]. + if (!keylen) { + key = [keylen++]; + } + + // Set up S using the standard key scheduling algorithm. + while (i < width) { + s[i] = i++; + } + for (i = 0; i < width; i++) { + s[i] = s[(j = mask & (j + key[i % keylen] + (t = s[i])))]; + s[j] = t; + } + + // The "g" method returns the next (count) outputs as one number. + (me.g = function (count) { + // Using instance members instead of closure state nearly doubles speed. + var t, + r = 0, + i = me.i, + j = me.j, + s = me.S; + while (count--) { + t = s[(i = mask & (i + 1))]; + r = + r * width + s[mask & ((s[i] = s[(j = mask & (j + t))]) + (s[j] = t))]; + } + me.i = i; + me.j = j; + return r; + // For robust unpredictability, the function call below automatically + // discards an initial batch of values. This is called RC4-drop[256]. + // See http://google.com/search?q=rsa+fluhrer+response&btnI + })(width); + } + + // + // flatten() + // Converts an object tree to nested arrays of strings. + // + function flatten(obj, depth) { + var result = [], + typ = typeof obj, + prop; + if (depth && typ == "object") { + for (prop in obj) { + try { + result.push(flatten(obj[prop], depth - 1)); + } catch (e) {} + } + } + return result.length ? result : typ == "string" ? obj : obj + "\0"; + } + + // + // mixkey() + // Mixes a string seed into a key that is an array of integers, and + // returns a shortened string seed that is equivalent to the result key. + // + function mixkey(seed, key) { + var stringseed = seed + "", + smear, + j = 0; + while (j < stringseed.length) { + key[mask & j] = + mask & ((smear ^= key[mask & j] * 19) + stringseed.charCodeAt(j++)); + } + return tostring(key); + } + + // + // autoseed() + // Returns an object for autoseeding, using window.crypto if available. + // + /** @param {Uint8Array|Navigator=} seed */ + function autoseed(seed) { + try { + if (nodecrypto) return tostring(nodecrypto.randomBytes(width)); + global.crypto.getRandomValues((seed = new Uint8Array(width))); + return tostring(seed); + } catch (e) { + return [ + +new Date(), + global, + (seed = global.navigator) && seed.plugins, + global.screen, + tostring(pool), + ]; + } + } + + // + // tostring() + // Converts an array of charcodes to a string + // + function tostring(a) { + return String.fromCharCode.apply(0, a); + } + + // + // When seedrandom.js is loaded, we immediately mix a few bits + // from the built-in RNG into the entropy pool. Because we do + // not want to interfere with deterministic PRNG state later, + // seedrandom will not call math.random on its own again after + // initialization. + // + mixkey(math[rngname](), pool); + + // + // Nodejs and AMD support: export the implementation as a module using + // either convention. + // + if (module && module.exports) { + module.exports = impl; + try { + // When in node.js, try using crypto package for autoseeding. + nodecrypto = require("crypto"); + } catch (ex) {} + } else if (define && define.amd) { + define(function () { + return impl; + }); + } + + // + // Node.js native crypto support. + // + + // End anonymous scope, and pass initial values. +})( + this, // global window object + [], // pool: entropy pool starts empty + Math, // math: package containing random, pow, and seedrandom + 256, // width: each RC4 output is 0 <= x < 256 + 6, // chunks: at least six RC4 outputs for each double + 52, // digits: there are 52 significant digits in a double + typeof module == "object" && module, // present in node.js + typeof define == "function" && define, // present with an AMD loader + "random" // rngname: name for Math.random and Math.seedrandom +); + diff --git a/style.css b/style.css new file mode 100644 index 0000000..fc619ce --- /dev/null +++ b/style.css @@ -0,0 +1,45 @@ +body { + margin: 2rem; +} + +* { + box-sizing: border-box; +} + +input[type=number] { + width: 150px; +} + +main { + display: flex; + gap: 1rem; +} + +.input-container { + display: flex; + flex-direction: column; + gap: 0.5rem; + background-color: #EEE; + margin: 0.5rem; +} + +#results { + margin-top: 1rem; + width: 400px; +} + +#log { + height: 200px; + width: 400px; + font-family: monospace; + overflow-y: scroll; +} + +#log time { + color: #BDBDBD; +} + +#log .worker-id { + color: #2E7D32; +} + diff --git a/worker.js b/worker.js new file mode 100644 index 0000000..1f4f0c7 --- /dev/null +++ b/worker.js @@ -0,0 +1,107 @@ +importScripts("seedrandom.js"); +importScripts("generator.js"); + +console.log("worker"); + +let workerId; +let currentInput = ""; +let attempts = 0; +let startedAt; + +let statsInterval; +let guessInterval; + +function getNumbers(name) { + Math.seedrandom(name); + + let numbers = []; + + for (let i = 0; i < 9; i++) { + numbers = numbers.concat(generate_col(i)); + } + + const activatedCols = generate_rows_check(); + const activatedNumbers = []; + + for (let row = 0; row < 3; row++) { + for (const col of activatedCols[row]) { + activatedNumbers.push(numbers[(col - 1) * 3 + row]); + } + } + + return activatedNumbers; +} + +function sendStats() { + if (!startedAt) return; + + self.postMessage({ type: "stats", attempts, time: Date.now() - startedAt }); + resetStats(); +} + +function resetStats() { + attempts = 0; + startedAt = Date.now(); +} + +function generateNextInput(input) { + const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + + if (input.length === 0) + return alphabet[0]; + + for (let i = input.length - 1; i >= 0; i--) { + const index = alphabet.indexOf(input[i]); + + if (index < alphabet.length - 1) { + return input.substring(0, i) + alphabet[index + 1] + alphabet[0].repeat(input.length - i - 1); + } + } + + return alphabet[0] + input; +} + +function guess() { + currentInput = generateNextInput(currentInput); + attempts++; + + // TODO +} + +function run() { + statsInterval = setInterval(sendStats, 1000); + guessInterval = setInterval(guess, 0); + + resetStats(); + + self.postMessage({ type: "log", text: "Starting worker", id: workerId }); +} + +function stop() { + clearInterval(statsInterval); + clearInterval(guessInterval); + + sendStats(); + + self.postMessage({ type: "log", text: "Stopping worker", id: workerId }); +} + +self.onmessage = message => { + switch (message.data.type) { + case "init": + workerId = message.data.id; + self.postMessage({ type: "ready" }); + break; + case "run": + run(); + break; + case "stop": + stop(); + break; + case "terminate": + stop(); + self.postMessage({ type: "terminate" }); + break; + } +}; +