☰

Rock Maths Auto Solver PRO

Automatic multiplication solver with digit verification

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği indirebilmeniz için ayrıca Tampermonkey gibi bir eklenti kurmanız gerekmektedir.

Bu betiği yüklemek için bir betik yöneticisi eklentisi yüklemeniz gerekecektir.

(Zaten bir betik yöneticim var, hadi yükleyelim!)

Bu stili yüklemek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için Stylus gibi bir uzantı kurmanız gerekir.

Bu stili yükleyebilmek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı kurmanız gerekir.

Bu stili yükleyebilmek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

(Zateb bir user-style yöneticim var, yükleyeyim!)

// ==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);

})();