Greasy Fork 还支持 简体中文。

头歌自动尝试正确答案

Automatically fetch answers for tasks

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 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));
  }
})();