AtCoderRGBGraph

AGC/ARC/ABCの指定したもののみでレーティングを計算

// ==UserScript==
// @name         AtCoderRGBGraph
// @namespace    https://twitter.com/merom686
// @version      1.0
// @description  AGC/ARC/ABCの指定したもののみでレーティングを計算
// @author       merom686
// @match        https://atcoder.jp/users/*
// @grant        GM_xmlhttpRequest
// ==/UserScript==

(function(){
    if (typeof rating_history === 'undefined') return;
    let ma = location.href.match(/https:\/\/atcoder\.jp\/users\/(\w+)(\?contestType=(\w+))?/);
    if (ma[3] && ma[3] != 'algo') return;
    let user = ma[1];
    let rating_history_original = rating_history;
    let history_array;

    let ex = {"abl":1999, "acl1":2799, "aising2019":1999, "aising2020":1999, "apc001":9999, "caddi2018":2799, "caddi2018b":1199, "cf16-exhibition-final":9999, "cf16-final":9999, "cf16-final-open":9999, "cf17-final":9999, "cf17-final-open":9999, "code-festival-2016-quala":9999, "code-festival-2016-qualb":9999, "code-festival-2016-qualc":9999, "code-festival-2017-quala":9999, "code-festival-2017-qualb":9999, "code-festival-2017-qualc":9999, "ddcc2020-qual":2799, "diverta2019":2799, "diverta2019-2":2799, "dwacon5th-prelims":2799, "dwacon6th-prelims":2799, "exawizards2019":2799, "hhkb2020":1999, "hitachi2020":2799, "jrex2017":9999, "jsc2019-qual":2799, "jsc2021":1999, "keyence2019":2799, "keyence2020":2799, "keyence2021":2799, "m-solutions2019":2799, "m-solutions2020":1999, "mujin-pc-2017":9999, "nikkei2019-2-qual":2799, "nikkei2019-qual":2799, "nomura2020":2799, "panasonic2020":1999, "soundhound2018-summer-qual":1999, "sumitrust2019":1999, "tenka1-2017":2799, "tenka1-2017-beginner":1199, "tenka1-2018":2799, "tenka1-2018-beginner":1199, "tenka1-2019":2799, "tenka1-2019-beginner":1199, "tokiomarine2020":2799, "wtf19":9999, "yahoo-procon2019-qual":2799, "zone2021":1999 };
    let name = (i) => 'A' + 'GRB'[i] + 'C';

    const T = (1 - 0.9) / Math.sqrt(1 - 0.81);
    let F = (n) => T * Math.sqrt(0.81 - Math.pow(0.81, n + 1)) / (0.9 - Math.pow(0.9, n + 1));
    let f = (n) => (F(n) - T) / (F(1) - T) * 1200;

    let update = () => {
        const e = new CustomEvent('load');
        window.dispatchEvent(e);
    };

    let draw = () => {
        if (!history_array){
            GM_xmlhttpRequest({
                method: 'GET',
                url: 'https://atcoder.jp/users/' + user + '/history/json?contestType=algo',
                onload: (response) => {
                    history_array = JSON.parse(response.responseText);
                    if (history_array) draw();
                }
            });
            return;
        }
        rating_history = [];
        let m = 0;
        for (let i = 0; i < 3; i++){
            if (document.getElementById(name(i)).checked) m ^= 1 << i;
        }
        let old = 0, s0 = 0, s1 = 0;
        let i0 = 0, i1 = 0;
        for (let o of history_array){
            if (!o.IsRated) continue;
            let t = o.ContestScreenName;
            let j = t.indexOf(".");
            if (j >= 0) t = t.substring(0, j);
            let v = 0;
            if (/^a[rgb]c\d{3}$/.test(t)){
                v = {"b":1999, "r":2799, "g":9999}[t[1]];
                if (t[1] == "b" && (t.slice(-3) | 0) < 126) v = 1199;
            } else {
                v = ex[t];
            }
            if (((m & 1) && v == 9999) || ((m & 2) && v == 2799) || ((m & 4) && v <= 1999)){
                s0 += Math.pow(2.0, o.Performance / 800.0);
                s0 *= 0.9;
                s1 += 1;
                s1 *= 0.9;
                let r = 800 * Math.log2(s0 / s1) - f(++i1);
                if (r < 400) r = Math.exp((r - 400) / 400) * 400;
                r = Math.round(r);
                let x = { ...rating_history_original[i0] };
                rating_history.push(x);
                x.NewRating = r;
                x.OldRating = old;
                old = r;
            }
            i0++;
        }
        if (rating_history.length == 0){
            let x = { ...rating_history_original[0] };
            rating_history.push(x);
            x.ContestName = '';
            x.EndTime = 0;
            x.NewRating = 0;
            x.OldRating = 0;
            x.Place = 0;
            x.StandingsUrl = '';
        }
        update();
    };

    let container = document.createElement('div');
    container.style = 'display: inline-block';
    for (let i = 0; i < 3; i++){
        let div = document.createElement('div');
        let id = name(i);

        let checkbox = document.createElement('input');
        checkbox.type = 'checkbox';
        checkbox.checked = true;
        checkbox.id = id;
        checkbox.addEventListener('change', draw);
        div.appendChild(checkbox);

        let label = document.createElement('label');
        label.htmlFor = id;
        label.innerText = id;
        div.appendChild(label);

        container.appendChild(div);
    }
    let a = document.getElementById('rating-graph-expand');
    a.parentNode.insertBefore(container, a.nextSibling);
})();