Initial commit
This commit is contained in:
commit
6f358ec977
103
generator.js
Normal file
103
generator.js
Normal file
@ -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));
|
||||
}
|
||||
}
|
||||
|
61
index.html
Normal file
61
index.html
Normal file
@ -0,0 +1,61 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>MAGS Banko Cheat</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<script defer src="main.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Banko Cheat</h1>
|
||||
|
||||
<main>
|
||||
<div>
|
||||
<fieldset class="input-container">
|
||||
<legend>Win type</legend>
|
||||
<label>
|
||||
<input type="radio" name="win-type" value="row" checked>
|
||||
One row
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<input type="radio" name="win-type" value="two-rows">
|
||||
Two rows
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<input type="radio" name="win-type" value="full">
|
||||
Full board
|
||||
</label>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="input-container">
|
||||
<legend>Workers</legend>
|
||||
<input id="workers" type="number" min="1" value="1">
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<fieldset id="numbers-container" class="input-container">
|
||||
<legend>Numbers</legend>
|
||||
<input type="number" min="1" max="90">
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<div id="results">
|
||||
<button id="pause">Pause</button>
|
||||
 
|
||||
<b>
|
||||
<span id="attempts">--</span> plates tried  
|
||||
(<span id="speed">--</span>/sec)
|
||||
</b>
|
||||
</div>
|
||||
|
||||
<fieldset>
|
||||
<legend>Log</legend>
|
||||
<div id="log"></div>
|
||||
</fieldset>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
|
134
main.js
Normal file
134
main.js
Normal file
@ -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 = `<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() {
|
||||
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);
|
||||
}
|
||||
|
236
seedrandom.js
Normal file
236
seedrandom.js
Normal file
@ -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
|
||||
);
|
||||
|
45
style.css
Normal file
45
style.css
Normal file
@ -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;
|
||||
}
|
||||
|
107
worker.js
Normal file
107
worker.js
Normal file
@ -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;
|
||||
}
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user