Greasy Fork

Greasy Fork is available in English.

头歌自动尝试正确答案

Automatically fetch answers for tasks

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         头歌自动尝试正确答案
// @match        *://*.educoder.net/tasks/*
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Automatically fetch answers for tasks
// @author       Owwk
// @grant        none
// ==/UserScript==

(function () {
  'use strict';

  // 定义答案的所有可能组合
  const answers = [];
  const delayMs = 1;
  const isDelay = false;

  function fillAnswers(maxChar) {
    const options = [];
    for (let i = 'A'.charCodeAt(0); i <= maxChar.charCodeAt(0); i++) {
      options.push(String.fromCharCode(i));
    }
    for (let i = 1; i <= options.length; i++) {
      combinations(options, i).forEach(c => answers.push(c.join('')));
    }
    console.info("答案组合", answers)
  }

  function combinations(arr, k) {
    const result = [];
    const combination = (start, prefix) => {
      if (prefix.length === k) {
        result.push(prefix);
        return;
      }
      for (let i = start; i < arr.length; i++) {
        combination(i + 1, prefix.concat(arr[i]));
      }
    };
    combination(0, []);
    return result;
  }

  // 每秒获取一次目标元素,超时时间为5秒
  const interval = setInterval(() => {
    const target = document.querySelector('#task-left-panel > div.task-header > span');
    if (target) {
      clearInterval(interval);
      insertButton(target);
    }
  }, 1000);

  setTimeout(() => clearInterval(interval), 5000);

  function insertButton(target) {
    const button = document.createElement('button');
    button.textContent = 'Fetch Answer';
    button.style.marginRight = '10px';
    target.parentElement.insertBefore(button, target);

    button.addEventListener('click', () => {
      const userInput = prompt('请输入fetch字符串:');
      if (userInput) {
        try {
          const { url, options } = parseFetchString(userInput);
          attemptAllCombinations(url, options);
        } catch (e) {
          alert('无效的fetch字符串');
        }
      }
    });
  }

  function parseFetchString(fetchString) {
    const urlMatch = fetchString.match(/fetch\("([^"]+)"/);
    const optionsMatch = fetchString.match(/,\s*({[\s\S]*})\s*\)\s*;/);

    if (!urlMatch || !optionsMatch) {
      throw new Error('无法解析fetch字符串');
    }

    const url = urlMatch[1];
    const options = JSON.parse(optionsMatch[1]);
    return { url, options };
  }

  async function attemptAllCombinations(url, options) {
    // 解析 options.body 获取答案数组的长度
    let bodyObj = JSON.parse(options.body);
    const answerLength = bodyObj.answer.length;

    let maxChar = 'A';
    // 解析最大Char
    bodyObj.answer.forEach((val, index) => {
      if (val > maxChar) {
        maxChar = val;
      }
    });
    fillAnswers(maxChar);

    // 初始化 correctAnswers 数组,假设所有题目都是错误的
    let correctAnswers = Array(answerLength).fill(false);
    let num = 0;

    for (let answer of answers) {
      num++;
      bodyObj = modifyRequestBody(bodyObj, answer, correctAnswers);
      const newRequestOptions = { ...options, body: JSON.stringify(bodyObj) };
      const response = await sendRequest(url, newRequestOptions);

      console.info("尝试", num, bodyObj.answer)
      // 更新 correctAnswers 数组,标记已经正确的题目
      correctAnswers = updateCorrectAnswers(correctAnswers, response);
      console.info("结果", num, correctAnswers)
      if (isAllCorrect(response)) {
        alert('找到正确答案: ' + getAnswer(response).join(', '));
        break
      }
      if (isDelay) await delay(delayMs);
    }
  }


  function updateCorrectAnswers(correctAnswers, response) {
    // 遍历 response 中的 test_sets,更新 correctAnswers
    response.test_sets.forEach((test, index) => {
      if (test.result) {
        correctAnswers[index] = true;
      }
    });

    return correctAnswers;
  }


  function modifyRequestBody(body, answer, correctAnswers) {
    const bodyObj = body;

    // 仅在对应位置没有正确答案时才修改答案数组
    for (let i = 0; i < bodyObj.answer.length; i++) {
      if (!correctAnswers[i]) {
        bodyObj.answer[i] = answer;
      }
    }

    return bodyObj
  }


  function sendRequest(url, options) {
    Object.keys(options.headers).forEach(header => {
      if (header.startsWith('x-edu-')) {
        delete options.headers[header];
      }
    });

    return fetch(url, options)
      .then(response => response.json())
      .catch(error => console.error('Error:', error));
  }


  function isAllCorrect(response) {
    return response.test_sets.every(test => test.result);
  }

  function getAnswer(response) {
    return response.test_sets.map(val => val.actual_output)
  }

  function delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
})();