洛谷通过题目比较器 - yyfcpp

比较你和其他用户在洛谷通过的题目

目前為 2018-08-28 提交的版本,檢視 最新版本

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

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

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

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

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

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

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         洛谷通过题目比较器 - yyfcpp
// @namespace    http://tampermonkey.net/
// @version      1.2
// @description  比较你和其他用户在洛谷通过的题目
// @author       yyfcpp
// @match        https://www.luogu.org/space/*
// @grant        none
// ==/UserScript==


/*
 * 这是一个注释区,用于保存 TODO 之类的东西
 * 如果网络不是很好,可能会出现卡顿的情况。
 * 现在使用的是 O(n^2) 的比较算法。如果出现了 AC 了数千题神犇,或许需要改为二分算法。
*/


function clearData(acs) {
    var res = new Array();
    for (var i = 1; i < acs.length; i++) { // 把每一行非题号字符删掉(从 1 开始循环为了避开 split 之后产生的垃圾)
        var tmpStr = "";
        for (var j = 0; j < acs[i].length; j++) {
            if (acs[i][j] != '"') { // 引号后面的不是题号部分字符
                tmpStr = tmpStr.concat(acs[i][j]); // 拼接字符串
            } else {
                break;
            }
        }
        if (acs[i].length > 50) { // 这是最后一个题目 / 下一个是「尝试过的题目」
            res.push(tmpStr);
            break;
        }
        res.push(tmpStr);
    }
    return res;
}


function extractData(content) {
    // 如果你有一个问题打算用正则表达式来解决,那么就是两个问题了。
    // 所以窝还是用 split() 解决这一个问题吧!
    // var re = new RegExp('\[<a data-pjax href="/problem/show\?pid=.*?">.*?</a>\]', 'g');
    // console.log(re.test(content));
    // var acs = content.match('/\[<a data-pjax href="/problem/show\?pid=(\S*)">(\S*)</a>\]/');
    // var acs = content.match(/^[<a data-pjax href="\/problem\/show\?pid=[A-Z]+[0-9]+">[A-Z]+[0-9]+<\/a>]/g);
    // console.log(acs);
    // var acs = content.replace(/\[<a data-pjax href="\/problem\/show\?pid=[A-Z]+[0-9]+>/g, '').replace(/<\/a>\]/g, ' ').split(' ');
    // console.log(acs);
    var acs = content.split('[<a data-pjax href="/problem/show?pid='); // 使用 split() 方法把通过的题目分割出来
    acs = clearData(acs); // 把分割好的数据清洁一下
    return acs;
}


function getAc(uid) {
    // 向指定的个人空间发送 get 请求,获取 AC 列表
    var xhr = new XMLHttpRequest();
    xhr.open('GET', 'https://' + window.location.host + '/space/show?uid=' + uid, false);
    xhr.send(null);
    console.log('got ' + uid + "'s AC list: " + xhr.status);
    if (xhr.status == 200) {
        // console.log(xhr.responseText);
        return extractData(xhr.responseText); // 返回 AC 列表
    } else {
        return []; // 空列表
    }
}


function changeStyle(pid, meToo) {
    var cssSelector = "a[href='/problem/show?pid=" + pid + "']";
    document.querySelector(cssSelector).style.color = meToo ? "#008000" : "red"; // 绿色表示也 AC。红色表示未 AC
    // document.querySelector(cssSelector).style.fontWeight = meToo ? "normal" : "bold"; // 加粗有点血腥,算了
}

function displayTot(tot) {
    var cssSelector = "body > div.am-cf.lg-main > div.lg-content > div.am-g.lg-main-content > div.am-u-md-4.lg-right > div > h2";
    document.querySelector(cssSelector).style.fontSize = "18px"; // 避免在一些低分辨率显示器上一行显示不开
    document.querySelector(cssSelector).textContent = "通过题目(其中有 " + tot + " 道题你尚未 AC)";
}


function compare(hisAc, myAc) {
    var tot = hisAc.length; // 对方 AC 自己却没有 AC 的总数
    for (var i = 0; i < hisAc.length; i++) {
        var meToo = false; // 自己是否 AC 过
        for (var j = 0; j < myAc.length; j++) {
            if (hisAc[i] == myAc[j]) { // 也 AC 了
                meToo = true;
                tot--;
                break;
            }
        }
        changeStyle(hisAc[i], meToo); // 更改题号样式
    }
    displayTot(tot); // 显示未 AC 总数
}


function work() {
    var myAc = getAc(myUid);
    var hisAc = getAc(hisUid);
    // console.log(myAc);
    console.log(hisAc);
    if (hisAc.length > 0) { // 对方没开完全隐私保护
        compare(hisAc, myAc);
    }
    console.log("对方开启了完全隐私保护,无法比较。");
}


function displayAcCntForThousandsShenBen(AcCnt) {
    var cssSelector = "body > div.am-cf.lg-main > div.lg-content > div.am-g.lg-main-content > div.am-u-md-4.lg-right > div > h2";
    document.querySelector(cssSelector).textContent = "通过题目(共 " + AcCnt + " 道题)";
}


var myUid = document.getElementsByClassName("am-topbar-brand")[0].attributes["myuid"].value; // 获取当前登录账号的 uid
var myUrl = 'https://www.luogu.org/space/show?uid=' + myUid; // 获取自己个人主页的 URL
var nowUrl = window.location.href; // 获取当前所在个人主页的 URL
var hisUid = window.location.href.match(/uid=[0-9]+/)[0].substr(4); // 获取当前所在个人空间主人的 UID

if (myUrl != nowUrl) { // 只有访问他人个人空间才进行比较
    work();
} else { // 要对千题神犇们特别添加一个功能
    var myAcCnt = getAc(myUid).length
    if (myAcCnt >= 1000) {
        displayAcCntForThousandsShenBen(myAcCnt);
    }
}