Mathspace Auto Solver with Explanations

Automatically solves problems on Mathspace.co and collects explanations

// ==UserScript==
// @name         Mathspace Auto Solver with Explanations
// @namespace    http://mathspace.co/
// @version      1.0
// @description  Automatically solves problems on Mathspace.co and collects explanations
// @author       You
// @match        https://mathspace.co/*
// @grant        none
// @run-at       document-idle
// ==/UserScript==

(function() {
    'use strict';
    
    // Store explanations for all problems
    let problemExplanations = [];
    let currentProblemIndex = 0;
    let solverActive = true;
    
    // Add control panel to toggle functionality
    const addControlPanel = () => {
        const controlPanel = document.createElement('div');
        controlPanel.style.position = 'fixed';
        controlPanel.style.top = '10px';
        controlPanel.style.right = '10px';
        controlPanel.style.zIndex = '9999';
        controlPanel.style.backgroundColor = '#f0f0f0';
        controlPanel.style.padding = '10px';
        controlPanel.style.borderRadius = '5px';
        controlPanel.style.boxShadow = '0 0 10px rgba(0,0,0,0.2)';
        
        const toggleButton = document.createElement('button');
        toggleButton.innerText = solverActive ? 'Pause Solver' : 'Start Solver';
        toggleButton.style.marginRight = '10px';
        toggleButton.onclick = () => {
            solverActive = !solverActive;
            toggleButton.innerText = solverActive ? 'Pause Solver' : 'Start Solver';
            if (solverActive) {
                processProblem();
            }
        };
        
        const showExplanationsButton = document.createElement('button');
        showExplanationsButton.innerText = 'Show Explanations';
        showExplanationsButton.onclick = showExplanations;
        
        controlPanel.appendChild(toggleButton);
        controlPanel.appendChild(showExplanationsButton);
        document.body.appendChild(controlPanel);
    };
    
    // Process the current problem
    const processProblem = () => {
        if (!solverActive) return;
        
        console.log('Processing problem...');
        
        // Check if we are on a problem page
        const problemContainer = findProblemContainer();
        if (!problemContainer) {
            console.log('No problem container found, waiting...');
            setTimeout(processProblem, 1000);
            return;
        }
        
        // Get the problem information
        const problemTitle = findProblemTitle();
        const problemText = findProblemText();
        
        console.log('Problem found:', problemTitle);
        
        // First, collect the solution for this problem
        collectExplanation(problemTitle, problemText).then(() => {
            // Then look for the answer input field
            const answerInput = findAnswerInput();
            if (!answerInput) {
                console.log('No answer input found, waiting...');
                setTimeout(processProblem, 1000);
                return;
            }
            
            // Try to solve the problem by entering the final answer
            enterFinalAnswer(answerInput).then(success => {
                if (success) {
                    console.log('Answer submitted successfully');
                    // Wait for the answer to be processed
                    setTimeout(() => {
                        // Click the next button to proceed to next problem
                        clickNextButton();
                    }, 2000);
                } else {
                    console.log('Could not submit answer, trying step-by-step');
                    // If direct answer failed, try step-by-step approach
                    solveStepByStep();
                }
            });
        });
    };
    
    // Find the problem container element
    const findProblemContainer = () => {
        // Look for common containers that might hold the problem
        return document.querySelector('.problem-container') || 
               document.querySelector('.question-container') || 
               document.querySelector('.workbook-problem');
    };
    
    // Find the problem title
    const findProblemTitle = () => {
        const titleElement = document.querySelector('.problem-title') || 
                            document.querySelector('.question-title') ||
                            document.querySelector('h1');
        return titleElement ? titleElement.textContent.trim() : `Problem ${currentProblemIndex + 1}`;
    };
    
    // Find the problem text
    const findProblemText = () => {
        const textElement = document.querySelector('.problem-text') || 
                           document.querySelector('.question-text') ||
                           document.querySelector('.problem-statement');
        return textElement ? textElement.textContent.trim() : 'No problem text found';
    };
    
    // Find the answer input field
    const findAnswerInput = () => {
        // Look for the main input element or math editor
        return document.querySelector('.mathspace-editor') || 
               document.querySelector('input[type="text"]') ||
               document.querySelector('textarea') ||
               document.querySelector('.math-input');
    };
    
    // Collect the explanation for the current problem
    const collectExplanation = async (title, text) => {
        // Find the worked solutions or explanations
        try {
            // Click on hints or worked examples to reveal solutions
            const hintButton = document.querySelector('button:contains("Hint")') || 
                              document.querySelector('button:contains("hint")') ||
                              document.querySelector('.hint-button');
            
            const workedExampleButton = document.querySelector('button:contains("Worked Example")') || 
                                      document.querySelector('button:contains("worked example")') ||
                                      document.querySelector('.worked-example-button');
            
            const videoButton = document.querySelector('button:contains("Video")') || 
                              document.querySelector('button:contains("video")') ||
                              document.querySelector('.video-button');
            
            if (hintButton) {
                hintButton.click();
                await new Promise(resolve => setTimeout(resolve, 1000));
            }
            
            if (workedExampleButton) {
                workedExampleButton.click();
                await new Promise(resolve => setTimeout(resolve, 1000));
            }
            
            // Extract the explanation content
            const explanationElement = document.querySelector('.hint-content') ||
                                     document.querySelector('.worked-example-content') ||
                                     document.querySelector('.solution-steps');
            
            let explanation = explanationElement ? explanationElement.textContent.trim() : 'No explanation found';
            
            // Store the explanation
            problemExplanations.push({
                index: currentProblemIndex + 1,
                title: title,
                problem: text,
                explanation: explanation
            });
            
            currentProblemIndex++;
            console.log('Explanation collected:', explanation.substring(0, 50) + '...');
            
            // Close any open modal dialogs
            const closeButtons = document.querySelectorAll('.modal-close, .close-button, button:contains("Close")');
            closeButtons.forEach(button => button.click());
            
            return true;
        } catch (error) {
            console.error('Error collecting explanation:', error);
            return false;
        }
    };
    
    // Enter the final answer directly
    const enterFinalAnswer = async (inputElement) => {
        try {
            // Look for worked solutions or the final answer
            const workedSolutionButton = document.querySelector('button:contains("Worked Solution")') ||
                                       document.querySelector('button:contains("worked solution")');
            
            if (workedSolutionButton) {
                workedSolutionButton.click();
                await new Promise(resolve => setTimeout(resolve, 1000));
                
                // Extract the final answer from the worked solution
                const solutionContainer = document.querySelector('.worked-solution-container') ||
                                        document.querySelector('.solution-steps');
                
                if (solutionContainer) {
                    const steps = solutionContainer.querySelectorAll('p, div, span');
                    const lastStep = steps[steps.length - 1];
                    const finalAnswer = lastStep ? lastStep.textContent.trim() : null;
                    
                    if (finalAnswer) {
                        // Close the worked solution modal
                        const closeButton = document.querySelector('.modal-close, .close-button, button:contains("Close")');
                        if (closeButton) closeButton.click();
                        
                        // Input the final answer
                        inputElement.value = finalAnswer;
                        // Trigger change event
                        const event = new Event('input', { bubbles: true });
                        inputElement.dispatchEvent(event);
                        
                        // Click submit button
                        const submitButton = document.querySelector('button:contains("Submit")') ||
                                           document.querySelector('button:contains("submit")') ||
                                           document.querySelector('.submit-button');
                        
                        if (submitButton) {
                            submitButton.click();
                            return true;
                        }
                    }
                }
            }
            
            // If we couldn't find worked solutions, try skipping to the answer
            const skipButton = document.querySelector('button:contains("Skip Step")') ||
                             document.querySelector('button:contains("skip step")') ||
                             document.querySelector('.skip-step-button');
            
            if (skipButton) {
                skipButton.click();
                await new Promise(resolve => setTimeout(resolve, 1000));
                
                // After skipping, try to find the final answer
                const finalAnswerElement = document.querySelector('.final-answer') ||
                                         document.querySelector('.solution-step:last-child');
                
                if (finalAnswerElement) {
                    const finalAnswer = finalAnswerElement.textContent.trim();
                    
                    // Input the final answer
                    inputElement.value = finalAnswer;
                    // Trigger change event
                    const event = new Event('input', { bubbles: true });
                    inputElement.dispatchEvent(event);
                    
                    // Click submit button
                    const submitButton = document.querySelector('button:contains("Submit")') ||
                                       document.querySelector('button:contains("submit")') ||
                                       document.querySelector('.submit-button');
                    
                    if (submitButton) {
                        submitButton.click();
                        return true;
                    }
                }
            }
            
            return false;
        } catch (error) {
            console.error('Error entering final answer:', error);
            return false;
        }
    };
    
    // Solve the problem step-by-step
    const solveStepByStep = async () => {
        try {
            let solved = false;
            let maxSteps = 10; // Limit the number of steps to prevent infinite loops
            
            for (let i = 0; i < maxSteps && !solved; i++) {
                // Click the "Skip Step" button to get the next step
                const skipButton = document.querySelector('button:contains("Skip Step")') ||
                                 document.querySelector('button:contains("skip step")') ||
                                 document.querySelector('.skip-step-button');
                
                if (skipButton) {
                    skipButton.click();
                    await new Promise(resolve => setTimeout(resolve, 1000));
                    
                    // Check if we've solved the problem (look for a filled green tick/check)
                    const solvedIndicator = document.querySelector('.filled-green-tick') ||
                                          document.querySelector('.correct-final-answer');
                    
                    if (solvedIndicator) {
                        solved = true;
                        console.log('Problem solved step-by-step');
                        
                        // Click the next button after a delay
                        setTimeout(clickNextButton, 2000);
                        break;
                    }
                } else {
                    console.log('No skip button found, cannot continue step-by-step');
                    break;
                }
            }
            
            if (!solved) {
                console.log('Could not solve step-by-step, clicking next anyway');
                setTimeout(clickNextButton, 2000);
            }
        } catch (error) {
            console.error('Error in step-by-step solving:', error);
            setTimeout(clickNextButton, 2000);
        }
    };
    
    // Click the next button to proceed to the next problem
    const clickNextButton = () => {
        const nextButton = document.querySelector('button:contains("Next")') ||
                         document.querySelector('button:contains("next")') ||
                         document.querySelector('.next-button') ||
                         document.querySelector('button[aria-label="Next"]');
        
        if (nextButton) {
            console.log('Clicking next button');
            nextButton.click();
            
            // Wait for the next problem to load
            setTimeout(processProblem, 2000);
        } else {
            console.log('No next button found, might be at the end of problems');
            // Try again after a short delay
            setTimeout(() => {
                const retryNextButton = document.querySelector('button:contains("Next")') ||
                                      document.querySelector('button:contains("next")') ||
                                      document.querySelector('.next-button') ||
                                      document.querySelector('button[aria-label="Next"]');
                
                if (retryNextButton) {
                    console.log('Found next button on retry');
                    retryNextButton.click();
                    setTimeout(processProblem, 2000);
                } else {
                    console.log('Still no next button, might be finished');
                }
            }, 1000);
        }
    };
    
    // Display all collected explanations
    const showExplanations = () => {
        // Create a modal to display all explanations
        const modal = document.createElement('div');
        modal.style.position = 'fixed';
        modal.style.top = '0';
        modal.style.left = '0';
        modal.style.width = '100%';
        modal.style.height = '100%';
        modal.style.backgroundColor = 'rgba(0,0,0,0.8)';
        modal.style.zIndex = '10000';
        modal.style.overflowY = 'auto';
        modal.style.padding = '20px';
        
        const content = document.createElement('div');
        content.style.backgroundColor = 'white';
        content.style.borderRadius = '5px';
        content.style.padding = '20px';
        content.style.maxWidth = '800px';
        content.style.margin = '20px auto';
        
        // Add a close button
        const closeButton = document.createElement('button');
        closeButton.innerText = 'Close';
        closeButton.style.float = 'right';
        closeButton.onclick = () => {
            document.body.removeChild(modal);
        };
        
        // Add heading
        const heading = document.createElement('h2');
        heading.innerText = 'Problem Explanations';
        
        content.appendChild(closeButton);
        content.appendChild(heading);
        
        // Add each explanation
        if (problemExplanations.length === 0) {
            const noData = document.createElement('p');
            noData.innerText = 'No explanations collected yet.';
            content.appendChild(noData);
        } else {
            problemExplanations.forEach(item => {
                const container = document.createElement('div');
                container.style.borderBottom = '1px solid #ccc';
                container.style.marginBottom = '20px';
                container.style.paddingBottom = '20px';
                
                const title = document.createElement('h3');
                title.innerText = `Problem ${item.index}: ${item.title}`;
                
                const problem = document.createElement('div');
                problem.innerHTML = '<strong>Problem:</strong><br>' + item.problem;
                problem.style.marginBottom = '10px';
                
                const explanation = document.createElement('div');
                explanation.innerHTML = '<strong>Explanation:</strong><br>' + item.explanation;
                
                container.appendChild(title);
                container.appendChild(problem);
                container.appendChild(explanation);
                
                content.appendChild(container);
            });
        }
        
        // Add an export button
        const exportButton = document.createElement('button');
        exportButton.innerText = 'Export to JSON';
        exportButton.onclick = () => {
            const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(problemExplanations, null, 2));
            const downloadAnchorNode = document.createElement('a');
            downloadAnchorNode.setAttribute("href", dataStr);
            downloadAnchorNode.setAttribute("download", "mathspace_explanations.json");
            document.body.appendChild(downloadAnchorNode);
            downloadAnchorNode.click();
            downloadAnchorNode.remove();
        };
        
        content.appendChild(document.createElement('br'));
        content.appendChild(exportButton);
        
        modal.appendChild(content);
        document.body.appendChild(modal);
    };
    
    // Extended querySelector to support :contains selector
    const originalQuerySelector = Element.prototype.querySelector;
    Element.prototype.querySelector = function(selector) {
        if (selector.includes(':contains(')) {
            const match = selector.match(/:contains\(["']?([^)"']+)["']?\)/);
            if (match) {
                const text = match[1];
                const newSelector = selector.replace(/:contains\(["']?([^)"']+)["']?\)/, '');
                const elements = this.querySelectorAll(newSelector || '*');
                for (let i = 0; i < elements.length; i++) {
                    if (elements[i].textContent.includes(text)) {
                        return elements[i];
                    }
                }
                return null;
            }
        }
        return originalQuerySelector.call(this, selector);
    };
    
    const originalDocumentQuerySelector = Document.prototype.querySelector;
    Document.prototype.querySelector = function(selector) {
        if (selector.includes(':contains(')) {
            const match = selector.match(/:contains\(["']?([^)"']+)["']?\)/);
            if (match) {
                const text = match[1];
                const newSelector = selector.replace(/:contains\(["']?([^)"']+)["']?\)/, '');
                const elements = this.querySelectorAll(newSelector || '*');
                for (let i = 0; i < elements.length; i++) {
                    if (elements[i].textContent.includes(text)) {
                        return elements[i];
                    }
                }
                return null;
            }
        }
        return originalDocumentQuerySelector.call(this, selector);
    };
    
    // Initialize the script
    const initialize = () => {
        console.log('Mathspace Auto Solver initialized');
        addControlPanel();
        
        // Start processing after a short delay
        setTimeout(processProblem, 2000);
    };
    
    // Run the script when the page is fully loaded
    if (document.readyState === 'complete') {
        initialize();
    } else {
        window.addEventListener('load', initialize);
    }
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址