Sleazy Fork is available in English.
Automatic multiplication solver with digit verification
// ==UserScript==
// @name Rock Maths Auto Solver PRO
// @namespace rockmaths
// @version 5.1
// @description Automatic multiplication solver with digit verification
// @match *://*/*
// @run-at document-idle
// @grant none
// ==/UserScript==
(() => {
'use strict';
if (window.rockMathsPro) return;
window.rockMathsPro = true;
let enabled = true;
let busy = false;
let lastQuestion = '';
let observerTimer = null;
const sleep = ms =>
new Promise(resolve => setTimeout(resolve, ms));
function visible(el) {
if (!el) return false;
const r = el.getBoundingClientRect();
const s = getComputedStyle(el);
return r.width > 0 &&
r.height > 0 &&
s.display !== 'none' &&
s.visibility !== 'hidden';
}
function readQuestion() {
const text = document.body.innerText || '';
const match = text.match(
/(\d{1,3})\s*[×x*]\s*(\d{1,3})\s*=/
);
if (!match) return null;
const progress = text.match(/\b(\d+)\s*\/\s*(\d+)\b/);
const a = Number(match[1]);
const b = Number(match[2]);
return {
answer: String(a * b),
id: `${a}x${b}:${progress?.[0] || ''}`
};
}
function findKeys() {
const controls = [
...document.querySelectorAll(
'button,[role="button"],[onclick]'
)
].filter(visible);
const groups = {};
for (let n = 0; n <= 9; n++) {
groups[n] = controls.filter(el =>
el.textContent.trim() === String(n)
);
if (!groups[n].length) return null;
}
let root = groups[0][0];
while (
root &&
!Object.values(groups).every(group =>
group.some(el => root.contains(el))
)
) {
root = root.parentElement;
}
if (!root) return null;
const keys = {};
for (let n = 0; n <= 9; n++) {
keys[n] = groups[n]
.filter(el => root.contains(el))
.sort((a, b) => {
const x = a.getBoundingClientRect();
const y = b.getBoundingClientRect();
return x.width * x.height -
y.width * y.height;
})[0];
if (!keys[n]) return null;
}
return { root, keys };
}
function findSpecialButtons(root, keys) {
const digitKeys = Object.values(keys);
const controls = [
...root.querySelectorAll(
'button,[role="button"],[onclick]'
)
].filter(el =>
visible(el) &&
!digitKeys.includes(el) &&
!digitKeys.some(k => el.contains(k))
);
if (controls.length < 2) return null;
const bottom = controls.filter(el => {
const r = el.getBoundingClientRect();
const rr = root.getBoundingClientRect();
return r.bottom > rr.bottom - 110;
});
if (bottom.length < 2) return null;
bottom.sort((a, b) =>
a.getBoundingClientRect().left -
b.getBoundingClientRect().left
);
return {
clear: bottom[0],
submit: bottom[bottom.length - 1]
};
}
function findAnswerDisplay(root) {
const rr = root.getBoundingClientRect();
const candidates = [
...document.querySelectorAll('div,span,p')
].filter(el => {
if (!visible(el) || el.children.length > 1) {
return false;
}
const r = el.getBoundingClientRect();
return r.bottom <= rr.top + 5 &&
r.right > rr.left + rr.width * 0.55;
});
return candidates.sort((a, b) => {
const x = a.getBoundingClientRect();
const y = b.getBoundingClientRect();
return x.width * x.height -
y.width * y.height;
})[0] || null;
}
function click(el) {
if (!visible(el)) return false;
el.click();
return true;
}
async function waitForDisplay(display, expected) {
const start = Date.now();
while (Date.now() - start < 1500) {
if (!enabled) return false;
if (display.textContent.trim() === expected) {
return true;
}
await sleep(30);
}
return false;
}
async function solve() {
if (!enabled || busy) return;
const q = readQuestion();
if (!q || q.id === lastQuestion) return;
const keypad = findKeys();
if (!keypad) return;
const special = findSpecialButtons(
keypad.root,
keypad.keys
);
if (!special) return;
const display = findAnswerDisplay(keypad.root);
if (!display) return;
busy = true;
try {
// Clear the current answer.
if (!click(special.clear)) return;
await sleep(150);
let current = readQuestion();
if (!current || current.id !== q.id) return;
// Enter and verify every digit.
let typed = '';
for (const digit of q.answer) {
if (!enabled) return;
current = readQuestion();
if (!current || current.id !== q.id) return;
if (!click(keypad.keys[digit])) return;
typed += digit;
const verified = await waitForDisplay(
display,
typed
);
if (!verified) {
console.warn(
'[Rock Maths] Digit verification failed:',
typed
);
return;
}
}
// Verify the complete answer.
if (display.textContent.trim() !== q.answer) {
return;
}
current = readQuestion();
if (!current || current.id !== q.id) return;
await sleep(120);
if (!enabled) return;
if (display.textContent.trim() !== q.answer) {
return;
}
// Submit only when verified.
if (click(special.submit)) {
lastQuestion = q.id;
}
await sleep(400);
} catch (error) {
console.error('[Rock Maths]', error);
} finally {
busy = false;
}
}
// Create status button.
const badge = document.createElement('button');
Object.assign(badge.style, {
position: 'fixed',
right: '12px',
bottom: '12px',
zIndex: '2147483647',
padding: '11px 15px',
border: 'none',
borderRadius: '9px',
color: 'white',
fontWeight: 'bold',
cursor: 'pointer'
});
function updateBadge() {
badge.textContent = enabled
? 'SMART SOLVER: ON'
: 'SMART SOLVER: OFF';
badge.style.background = enabled
? '#145b49'
: '#a52d35';
}
badge.onclick = () => {
enabled = !enabled;
updateBadge();
if (enabled) solve();
};
document.body.appendChild(badge);
updateBadge();
// Alt + M toggles the solver.
document.addEventListener('keydown', event => {
if (
event.altKey &&
event.key.toLowerCase() === 'm'
) {
enabled = !enabled;
updateBadge();
if (enabled) solve();
}
});
// Detect new questions automatically.
const observer = new MutationObserver(() => {
clearTimeout(observerTimer);
observerTimer = setTimeout(solve, 70);
});
observer.observe(document.body, {
childList: true,
subtree: true,
characterData: true
});
setTimeout(solve, 500);
})();