Codeforces Visual Enhancer (Dynamic Elo Tier)

rank 排名着色,提交结果流光效果

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği yüklemek için Tampermonkey gibi bir uzantı yüklemeniz gerekir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği indirebilmeniz için ayrıca Tampermonkey gibi bir eklenti kurmanız gerekmektedir.

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

Bu stili yüklemek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için Stylus gibi bir uzantı kurmanız gerekir.

Bu stili yükleyebilmek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı kurmanız gerekir.

Bu stili yükleyebilmek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

(Zateb bir user-style yöneticim var, yükleyeyim!)

// ==UserScript==
// @name         Codeforces Visual Enhancer (Dynamic Elo Tier)
// @namespace    http://tampermonkey.net/
// @version      4.7
// @license      MIT
// @description  rank 排名着色,提交结果流光效果
// @author       Gemini & Guanglong Shen
// @match        https://codeforces.com/*
// @match        http://codeforces.com/*
// @match        https://mirror.codeforces.com/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // --- 高级配置区 ---
    const BASE_THRESHOLDS = {
        LGM: 0.0006, GM: 0.0037, MAS: 0.0064, CM: 0.0512, EXP: 0.1213, SPC: 0.2189, PPL: 0.3986
    };

    // 1. 样式定义
    const style = document.createElement('style');
    style.innerHTML = `
        @keyframes shine-slide {
            0% { transform: translateX(-150%) skewX(-25deg); opacity: 0; }
            5% { opacity: 1; }
            20% { transform: translateX(250%) skewX(-25deg); opacity: 1; }
            100% { transform: translateX(250%) skewX(-25deg); opacity: 0; }
        }
        .tm-vfx-base { position: relative; display: inline-block; overflow: hidden; vertical-align: middle; line-height: inherit; !important; font-weight: bold !important; padding: 0 2px; }
        .tm-vfx-base::after { content: ''; position: absolute; top: 0; left: 0; width: 60%; height: 100%;
            background: linear-gradient(to right, rgba(255,255,255,0) 0%, rgba(255,255,255,0.7) 50%, rgba(255,255,255,0) 100%);
            animation: shine-slide 2.5s ease-in-out infinite; pointer-events: none; }

        .rank-lgm { color: #aa0000 !important; font-weight: bold; }
        .rank-lgm::first-letter { color: #000000 !important; }
        .rank-gm { color: #ff0000 !important; font-weight: bold; }
        .rank-master { color: #ff8c00 !important; font-weight: bold; }
        .rank-cm { color: #aa00aa !important; font-weight: bold; }
        .rank-expert { color: #0000ff !important; font-weight: bold; }
        .rank-specialist { color: #03a89e !important; font-weight: bold; }
        .rank-pupil { color: #008000 !important; font-weight: bold; }
        .rank-newbie { color: #808080 !important; font-weight: bold; }

        /* 修改:层级信息样式 - 右对齐且不设固定颜色 */
        .tm-tier-info {
            float: right;
            font-size: 0.9em;
            font-weight: bold !important;
            margin-left: 10px;
            font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
            line-height: 1.2;
        }

        .tm-ac { color: #00a900 !important; }
        .tm-pre { color: #006400 !important; }
        .tm-wa { color: #ff0000 !important; }
        .tm-orange { color: #FF8C00 !important; }
        .tm-blue { color: #00008B !important; }
        .tm-hack { color: #B8860B !important; }
    `;
    document.head.appendChild(style);

    // 2. 获取有效人数
    function getTotalParticipants() {
        const allRows = document.querySelectorAll('.standings tr');
        let statRow = null;
        for (let row of allRows) {
            if (row.innerText.includes('Accepted')) {
                statRow = row;
                break;
            }
        }
        if (!statRow) {
            const pagination = document.querySelector('.pagination');
            if (pagination) {
                const match = pagination.innerText.match(/of\s+(\d+)/);
                if (match) return parseInt(match[1]);
            }
            return 8500;
        }
        const acceptedCounts = [];
        const cells = statRow.querySelectorAll('td');
        cells.forEach(cell => {
            const numbers = cell.innerText.match(/\d+/g);
            if (numbers && numbers.length > 0) {
                acceptedCounts.push(parseInt(numbers[0])); // 只取 Accepted 数
            }
        });
        return acceptedCounts.length > 0 ? Math.max(...acceptedCounts) : 8500;
    }

    // 3. 计算动态阈值
    function getDynamicThresholds(N) {
        const baseline = 8000;
        let ratio = N / baseline;
        if (ratio < 1) ratio = 1;
        const compression = Math.pow(ratio, 0.4);

        return {
            LGM: BASE_THRESHOLDS.LGM / compression,
            GM:  BASE_THRESHOLDS.GM / compression,
            MAS: BASE_THRESHOLDS.MAS / compression,
            CM:  BASE_THRESHOLDS.CM / compression,
            EXP: BASE_THRESHOLDS.EXP,
            SPC: BASE_THRESHOLDS.SPC,
            PPL: BASE_THRESHOLDS.PPL
        };
    }

    // 4. 排名染色及 Who 列增强
    function applyRankColoring() {
        const total = getTotalParticipants();
        const thresholds = getDynamicThresholds(total);
        const rankCells = document.querySelectorAll('.standings td:first-child:not(.header)');

        const tierBounds = [
            { cls: 'rank-lgm', minP: 0, maxP: thresholds.LGM },
            { cls: 'rank-gm', minP: thresholds.LGM, maxP: thresholds.GM },
            { cls: 'rank-master', minP: thresholds.GM, maxP: thresholds.MAS },
            { cls: 'rank-cm', minP: thresholds.MAS, maxP: thresholds.CM },
            { cls: 'rank-expert', minP: thresholds.CM, maxP: thresholds.EXP },
            { cls: 'rank-specialist', minP: thresholds.EXP, maxP: thresholds.SPC },
            { cls: 'rank-pupil', minP: thresholds.SPC, maxP: thresholds.PPL },
            { cls: 'rank-newbie', minP: thresholds.PPL, maxP: 1.0 }
        ];

        // 自动定位 Who 列索引 (防止列顺序变动)
        let whoIndex = 2;
        const headerCells = document.querySelectorAll('.standings th');
        headerCells.forEach((th, idx) => { if(th.innerText.includes('Who')) whoIndex = idx; });

        rankCells.forEach(cell => {
            if (cell.dataset.rankApplied) return;
            const text = cell.innerText.trim();
            if (!text) return;

            let rank = 0;
            const parenMatch = text.match(/\((\d+)\)/); //
            rank = parenMatch ? parseInt(parenMatch[1]) : parseInt(text);
            if (!rank || isNaN(rank)) return;

            const p = rank / total;
            let targetTier = tierBounds[tierBounds.length - 1];

            for (const tier of tierBounds) {
                if (p < tier.maxP) {
                    targetTier = tier;
                    break;
                }
            }

            // 排名列颜色应用
            cell.classList.remove('rank-lgm','rank-gm','rank-master','rank-cm','rank-expert','rank-specialist','rank-pupil','rank-newbie');
            cell.classList.add(targetTier.cls);
            cell.dataset.rankApplied = 'true';

            // 可选:鼠标悬停显示计算的百分比(Debug用)
            cell.title = `Rank: ${rank}/${total} (Top ${(p*100).toFixed(2)}%)`;

            // 计算层级区间信息
            const A = Math.max(1, Math.floor(targetTier.minP * total) + 1);
            const B = Math.floor(targetTier.maxP * total);
            let C = ((rank - A) / Math.max(1, B - A)) * 100;
            C = Math.max(0, Math.min(100, C)).toFixed(1);

            // 注入 Who 列
            const row = cell.parentElement;
            const whoCell = row.cells[whoIndex];
            if (whoCell) {
                // 清理可能存在的旧标签
                const oldInfo = whoCell.querySelector('.tm-tier-info');
                if (oldInfo) oldInfo.remove();

                const infoSpan = document.createElement('span');
                // 关键点:infoSpan 使用和排名一样的 class 来获得颜色
                infoSpan.className = `tm-tier-info ${targetTier.cls}`;
                infoSpan.innerText = `\t(${A}~${B}, ${C}%)`;

                // 确保 Who 单元格内容左对齐,而 span 向右浮动
                whoCell.style.textAlign = 'left';
                whoCell.appendChild(infoSpan);
                whoCell.title = `Tier Range: ${A}~${B}, you are ≈ ${C}% into this tier.`;
            }
        });
    }

    // 5. Verdict 流光 (保持不变)
    function applyVerdictVFX() {
        const verdicts = document.querySelectorAll('span.verdict-accepted, span.verdict-rejected, span.verdict-failed, span.submissionVerdictWrapper, .popup-content span');
        verdicts.forEach(el => {
            if (el.dataset.vfxApplied) return;
            const text = el.innerText.trim();
            if (!text || text.length < 2) return;
            el.classList.add('tm-vfx-base');
            if (text.includes('Accepted') || text.includes('passed')) el.classList.add('tm-ac');
            else if (text.includes('Pretests')) el.classList.add('tm-pre');
            else if (text.includes('Wrong')) el.classList.add('tm-wa');
            else if (text.includes('error')) el.classList.add('tm-orange');
            else if (text.includes('limit')) el.classList.add('tm-blue');
            else if (text.includes('Hacked')) el.classList.add('tm-hack');
            el.dataset.vfxApplied = 'true';
        });
    }

    const run = () => { applyRankColoring(); applyVerdictVFX(); };
    const observer = new MutationObserver(run);
    observer.observe(document.body, { childList: true, subtree: true });
    run();
})();