Autodarts - Better X01-Stats

Better stats-table

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         Autodarts - Better X01-Stats
// @namespace    http://tampermonkey.net/
// @version      0.79
// @description  Better stats-table
// @author       benebelter
// @require      https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js
// @require      https://update.greasyfork.org/scripts/570871/AD%20-%20Bearer-update%20v3.js
// @match        https://play.autodarts.io/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=autodarts.io
// @license      MIT
// @run-at       document-end
// ==/UserScript==


(function () {
    'use strict';
    const NAME_COL_WIDTH = "200px";
    const COL_WIDTH_1 = "50px";
    let currentGameId = null;


    let LANG = "en";

function detectLanguage() {
    let uiLang =
        localStorage.getItem("i18nextLng") ||
        localStorage.getItem("language") ||
        "";

    let browserLang = navigator.language || "";

    let lang = (uiLang || browserLang).toLowerCase();

    if (lang.startsWith("de")) {
        LANG = "de";
    } else if (lang.startsWith("nl")) {
        LANG = "nl";
    } else {
        LANG = "en";
    }
}

const TEXT = {
    en: {
        matchStats: "📊 Match Stats",
        avg: "Avg",
        first9: "First 9",
        to170: "— 170",
        trebles: "Trebless",
        triples: "T17 — T20",
        corrections: "Corrections",
        checkout: "Checkout",
        highest: "Highest",
        finishes: "90+ Finishes",
        dartsRest: "<span style='color: #00e676;'>Darts</span> / <span style='color: grey'>Rest</span>",
        noticeTitle: "⚠️ Notice:",
        noticeText: "A winning dart was corrected by the system. Hover over the value for details.",
        corrected: "has been corrected for the win"
    },
    de: {
        matchStats: "📊 Match Statistiken",
        avg: "Avg",
        first9: "First 9",
        to170: "— 170",
        trebles: "Ohne Triple",
        triples: "T17 — T20",
        corrections: "Korrekturen",
        checkout: "Checkout",
        highest: "Höchstes",
        finishes: "90+ Finishes",
        dartsRest: "<span style='color: #00e676;'>Darts</span> / <span style='color: grey'>Rest</span>",

        noticeTitle: "⚠️ Hinweis:",
        noticeText: "Ein Leg-Finish wurde vom System korrigiert. Fahre mit der Maus über den Wert für Details.",
        corrected: "wurde für den Gewinn korrigiert"
    },
    nl: {
        matchStats: "📊 Wedstrijd Statistieken",
        avg: "Gem",
        first9: "Eerste 9",
        to170: "— 170",
        trebles: "Zonder Triple",
        triples: "T17 — T20",
        corrections: "Correcties",
        checkout: "Checkout",
        highest: "Hoogste",
        finishes: "90+ Finishes",
        dartsRest: "<span style='color: #00e676;'>Darts</span> / <span style='color: grey'>Rest</span>",
        noticeTitle: "⚠️ Opmerking:",
        noticeText: "Een winnende dart werd door het systeem gecorrigeerd. Beweeg met de muis over de waarde voor details.",
        corrected: "is gecorrigeerd voor de winst"
    }
};

function t(key) {
    try {
        return TEXT?.[LANG]?.[key] ?? key;
    } catch {
        return key;
    }
}

    async function getMatchStats(gameid) {
        const res = await fetch(
            'https://api.autodarts.io/as/v0/matches/' + gameid + '/stats',
            {
                credentials: 'include',
                headers: { "Authorization": localStorage.getItem("bearer") },
            }
        );
        return await res.json();
    }

    function buildStats(match) {
        const customScoreMap = countCustomScores(match);
        const highFinishListMap = getHighFinishList(match);
        const nonDetectedMap = countNonDetectedThrows(match);
        const tripleMap = countHighTriples(match);
        const noTripleMap = countNoTripleOver100(match);

        const result = {};
        const playerMap = {};

        // 👉 Player-ID → Name
        match.players.forEach(p => {
            playerMap[p.id] = p.name;
        });

        match.matchStats.forEach(s => {
            const name = playerMap[s.playerId];
            if (!name) return;

            const upper = name.toUpperCase();

            const attempts = s.checkouts ?? 0;
            const hits = s.checkoutsHit ?? 0;

            result[name] = {
                counts: {
                    // ✅ FIX: Uppercase Key verwenden
                    highFinishList: highFinishListMap[upper] ?? [],

                    nonDetected: nonDetectedMap[upper] ?? 0,
                    noTriple100: noTripleMap[name] ?? 0,
                    triplesHigh: tripleMap[upper] ?? 0,

                    "60+": s.plus60 ?? 0,
                    "90+": customScoreMap[upper]?.["90+"] ?? 0,
                    "130+": customScoreMap[upper]?.["130+"] ?? 0,
                    "171+": customScoreMap[upper]?.["171+"] ?? 0,
                    "170+": s.plus170 ?? 0,
                    "180": s.total180 ?? 0
                },

                averages: {
                    total: s.average ?? 0,
                    first9: s.first9Average ?? 0,
                    to170: s.averageUntil170 ?? 0
                },

                checkout: {
                    attempts,
                    hits,
                    percentage: attempts > 0 ? (hits / attempts) * 100 : 0,
                    high: s.checkoutPoints ?? 0
                }
            };
        });

        return result;
    }

    function buildLegStats(match) {
        const isOnlineGame = !!match.host;
        const playerMap = {};
        match.players.forEach(p => {
            playerMap[p.id] = p.name;
        });

        const START_SCORE =
              match?.settings?.baseScore ??
              match?.games?.[0]?.settings?.baseScore ??
              501;


        const legs = [];

        match.games?.forEach((game, index) => {
            const leg = {
                leg: index + 1,
                set: game.set ?? 0,
                players: {}
            };

            const scorePerPlayer = {};
            const dartsPerPlayer = {};
            const correctedMap = {};
            const correctedDetailMap = {};

            match.players.forEach(p => {
                scorePerPlayer[p.id] = START_SCORE;
                dartsPerPlayer[p.id] = 0;
                correctedMap[p.id] = false;
                correctedDetailMap[p.id] = ""; // 👈 NEU
            });

            game.turns?.forEach(turn => {
                const pid = turn.playerId;

                let scored =
                    turn.points ??
                    turn.score ??
                    turn.roundScore ??
                    0;

                let darts =
                    turn.dartsThrown ??
                    turn.darts ??
                    3;

                if (scorePerPlayer[pid] > 0) {

                    // 🎯 Checkout Turn
                    if (scorePerPlayer[pid] - scored <= 0) {

                        let remaining = scorePerPlayer[pid];
                        let usedDarts = 0;

                        const throws = turn.throws ?? [];

                        let corrected = false;
                        let correctedDetail = "";

                        const GOOD_ENTRIES = [
                            "detected",
                            "corrected_bouncer",
                            "referee_ai_corrected",
                            "corrected_after_referee_ai",
                            "referee_ai_confirmed"
                        ];

                        if (throws.length > 0) {
                            for (let th of throws) {

                                const val =
                                      th?.points ??
                                      th?.value ??
                                      (th?.segment?.multiplier ?? 0) * (th?.segment?.number ?? 0);

                                remaining -= val;
                                usedDarts++;

                                if (remaining <= 0) {



                                    const VALID_ENTRIES = [
                                        "detected",
                                        "corrected_bouncer",
                                        "referee_ai_corrected",
                                        "corrected_after_referee_ai",
                                        "referee_ai_confirmed"
                                    ];

                                    if (!VALID_ENTRIES.includes(th.entry)) {
                                        corrected = true;
                                        const seg = th?.segment?.name ?? "";
                                        correctedDetail = `⚠️ ${seg} ${t("corrected")}`;
                                    }



                                    break;
                                }
                            }

                            darts = usedDarts;
                        }

                        scorePerPlayer[pid] = 0;
                        dartsPerPlayer[pid] += darts;
                        correctedMap[pid] = corrected;
                        correctedDetailMap[pid] = correctedDetail;

                    } else {
                        scorePerPlayer[pid] -= scored;
                        dartsPerPlayer[pid] += darts;
                    }
                }
            });

            // 👉 FINAL setzen
            match.players.forEach(p => {
                const id = p.id;
                const name = playerMap[id];
                const remaining = scorePerPlayer[id];

                const winnerId = game.winnerPlayerId ?? game.winner;

                if (id === winnerId) {
                    leg.players[name] = {
                        type: "darts",
                        value: dartsPerPlayer[id],
                        correctedFinish: correctedMap[id],
                        correctedDetail: correctedDetailMap[id]
                    };
                } else {
                    leg.players[name] = {
                        type: "remaining",
                        value: remaining
                    };
                }
            });

            legs.push(leg);
        });

        return legs;
    }








    function appendDate(match) {

        // ❌ Schon vorhanden → nichts tun
        if (document.querySelector("#ad-dateplayed")) return;

        const target = document.querySelector(".css-a6m3v9");
        if (!target) return;

        // 👉 mögliche Felder aus der API
        const rawDate =
              match.finishedAt ??
              match.endedAt ??
              match.startedAt ??
              null;

        const span = document.createElement("span");
        span.id = "ad-dateplayed";
        span.className = "css-1xbroe7";

        // ❌ kein Datum vorhanden
        if (!rawDate) {
            span.textContent = "läuft aktuell...";
            target.appendChild(span);
            return;
        }

        const date = new Date(rawDate);

        // ❌ ungültiges Datum
        if (isNaN(date.getTime())) {
            span.textContent = "läuft aktuell...";
            target.appendChild(span);
            return;
        }

        // ✅ korrekt formatieren
        const LOCALE_MAP = {
            en: "en-US",
            de: "de-DE",
            nl: "nl-NL"
        };
        const locale = LOCALE_MAP[LANG] || "en-US";

        const formatted = date.toLocaleString(locale, {
            day: "numeric",
            month: "short",
            year: "numeric",
            hour: "2-digit",
            minute: "2-digit"
        });

        // 👉 wenn Match noch läuft
        if (!match.finishedAt) {
            span.textContent = "läuft aktuell...";
        } else {
            span.textContent = LANG === "de" ? formatted + " Uhr" : formatted;
        }

        target.appendChild(span);
    }






    function render(stats, match) {

        detectLanguage();

        if (!match?.variant || !match.variant.toLowerCase().includes("x01")) {
            return;
        }
        appendDate(match);


        const players = Object.values(stats);

        const bestAvg = Math.max(...players.map(p => p.averages.total || 0));
        const bestFirst9 = Math.max(...players.map(p => p.averages.first9 || 0));
        const bestTo170 = Math.max(...players.map(p => p.averages.to170 || 0));
        const bestCheckout = Math.max(...players.map(p => p.checkout.percentage || 0));
        const bestCheckoutRounded = Math.round(bestCheckout * 100) / 100;

        document.querySelector('#ad-stats')?.remove();

        const container = document.createElement('div');
        container.id = "ad-stats";
        container.style.width = "96%";
        container.style.maxWidth = "96%";
        container.style.margin = "5px 2% 20px";
        container.style.padding = "12px";
        container.style.background = "#111";
        container.style.color = "#fff";
        container.style.border = "1px solid #333";
        container.style.borderRadius = "8px";
        container.style.fontFamily = "Arial";
        container.style.fontSize = "16px";
        container.style.boxShadow = "0 6px 20px rgba(0,0,0,0.5)";
        container.style.fontVariantNumeric = "tabular-nums";

        let html = `<div style="font-weight:bold; margin-bottom:10px; font-size: 3.0em; text-align: center;">${t("matchStats")}</div>`;

        // 🔥 Highscores
        html += `
<table style="
    width:auto;
    border-collapse: collapse;
    margin-bottom:12px;
    table-layout:fixed;
">
    <colgroup>
        <col style="width:${NAME_COL_WIDTH};">
        <col style="width:70px;">
        <col style="width:70px;">
        <col style="width:70px;">
        <col style="width:70px;">
        <col style="width:70px;">
    </colgroup>
    <tr style="border-bottom:1px solid #444;">
        <th style="text-align:left"></th>
        <th style="text-align:center">60+</th>
        <th style="text-align:center">90+</th>
        <th style="text-align:center">130+</th>
        <th style="text-align:center">171+</th>
        <th style="text-align:center">180</th>
    </tr>
`;



        Object.entries(stats).forEach(([name, s]) => {
            html += `
    <tr>
        <td style="text-align:left; padding-right:10px;  text-transform:uppercase;">
            ${name}
        </td>

        <td style="text-align:center;">
            ${s.counts["60+"] ?? 0}
        </td>

        <td style="text-align:center; color:#ffd700;">
            ${s.counts["90+"] ?? 0}
        </td>

        <td style="text-align:center; color:#ff8c00;">
            ${s.counts["130+"] ?? 0}
        </td>
        <td style="text-align:center; color:#ff8c00;">
        ${s.counts["171+"] ?? 0}
        </td>
        <td style="text-align:center; color:#ff4d4d;">
            ${s.counts["180"] ?? 0}
        </td>
    </tr>
    `;
        });
        html += `</table>`;


        // 📈 Averages

        const bestClean = Math.min(...players.map(p => p.counts.noTriple100 || 0));
        const bestTriplesHigh = Math.max(...players.map(p => p.counts.triplesHigh || 0));

        const bestNonDetected = Math.min(...players.map(p => p.counts.nonDetected ?? Infinity));

        html += `
<table style="
    width:auto;
    border-collapse: collapse;
    margin-bottom:12px;
    table-layout:fixed;
">
    <colgroup>
        <col style="width:${NAME_COL_WIDTH};">
        <col style="width:80px;">
        <col style="width:80px;">
        <col style="width:80px;">
        <col style="width:110px;">
        <col style="width:110px;">
        <col style="width:110px;">
    </colgroup>

    <tr style="border-bottom:1px solid #444;">
    <th style="text-align:center"></th>
<th style="text-align:center">${t("avg")}</th>
<th style="text-align:center">${t("first9")}</th>
<th style="text-align:center">${t("to170")}</th>
<th style="text-align:center">${t("trebles")}</th>
<th style="text-align:center">${t("triples")}</th>
<th style="text-align:center">${t("corrections")}</th>
    </tr>
`;

        Object.entries(stats).forEach(([name, s]) => {
            html += `
    <tr>
        <td style="
            text-align:left;
            padding-right:10px;
            text-transform:uppercase;
        ">
            ${name}
        </td>

        <td style="
            text-align:center;
            color: ${s.averages.total === bestAvg ? '#00e676' : 'inherit'};
            font-weight: ${s.averages.total === bestAvg ? 'bold' : 'normal'};
        ">
            ${(s.averages.total ?? 0).toFixed(2)}
        </td>

        <td style="
            text-align:center;
            color: ${s.averages.first9 === bestFirst9 ? '#00e676' : 'inherit'};
            font-weight: ${s.averages.first9 === bestFirst9 ? 'bold' : 'normal'};
        ">

            ${(s.averages.first9 ?? 0).toFixed(2)}
        </td>

        <td style="
            text-align:center;
            color: ${s.averages.to170 === bestTo170 ? '#00e676' : 'inherit'};
            font-weight: ${s.averages.to170 === bestTo170 ? 'bold' : 'normal'};
        ">

             ${(s.averages.to170 ?? 0).toFixed(2)}
        </td>

        <!-- 🟢 Clean (weniger ist besser!) -->
        <td style="
            text-align:center;
            color: ${s.counts.noTriple100 === bestClean ? '#00e676' : 'inherit'};
            font-weight: ${s.counts.noTriple100 === bestClean ? 'bold' : 'normal'};
        ">
            ${s.counts.noTriple100}
        </td>

        <!-- 🎯 Triple -->
<td style="
    text-align:center;
    color: ${s.counts.triplesHigh === bestTriplesHigh ? '#00e676' : 'inherit'};
    font-weight: ${s.counts.triplesHigh === bestTriplesHigh ? 'bold' : 'normal'};
">
    ${s.counts.triplesHigh ?? 0}
</td>

<td style="
    text-align:center;
    color: ${s.counts.nonDetected === bestNonDetected ? '#00e676' : '#ff5252'};
    font-weight: ${s.counts.nonDetected === bestNonDetected ? 'bold' : 'normal'};
">
    ${s.counts.nonDetected}
</td>
    </tr>
    `;
        });

        html += `</table>`;



        // 🎯 Checkout
        const bestHigh = Math.max(...players.map(p => p.checkout.high || 0));

        html += `
<table style="
    width:auto;
    border-collapse: collapse;
    margin-bottom:12px;
    table-layout:fixed;
">
 <colgroup>
    <col style="width:${NAME_COL_WIDTH};">
    <col style="width:140px;">
    <col style="width:140px;">
    <col style="width:300px;">  <!-- 👈 NEU -->
</colgroup>
<tr style="border-bottom:1px solid #444;">
<th style="text-align:center"> </th>
<th style="text-align:center">${t("checkout")}</th>
<th style="text-align:center">${t("highest")}</th>
<th style="text-align:left">${t("finishes")}</th>

</tr>
`;

        Object.entries(stats).forEach(([name, s]) => {
            html += `
<tr>
    <td style="
        text-align:left;
        padding-right:10px;
        text-transform:uppercase;
    ">
        ${name}
    </td>

    <td style="text-align:center;">
        <span style="
            font-weight:bold;

            color: ${
    (Math.round((s.checkout.percentage ?? 0) * 100) / 100) === bestCheckoutRounded
        ? '#00e676'
        : 'inherit'
};
        ">
            ${s.checkout.percentage.toFixed(2)} %
        </span>
        <span style="opacity:0.6; margin-left:4px;">
            (${s.checkout.hits}/${s.checkout.attempts})
        </span>
    </td>

    <td style="
        text-align:center;
        font-weight:bold;
        color: ${s.checkout.high === bestHigh ? '#00e676' : 'inherit'};
    ">
        ${s.checkout.high > 0 ? s.checkout.high : ""}
    </td>

    <!-- 👇 WICHTIG: eigenes TD -->
    <td style="
        text-align:left;
        padding-left:10px;
        font-size:0.9em;
        opacity:0.85;
    ">
        ${s.counts.highFinishList.length > 0
                ? s.counts.highFinishList.join(", ")
            : "--"}
    </td>
</tr>
    `;
        });

        html += `</table>`;
        // 🟢 Leg Übersicht
        const legs = buildLegStats(match);

        const setGroups = [];
        let currentSet = null;

        legs.forEach((l, i) => {
            if (l.set !== currentSet) {
                setGroups.push({
                    set: l.set,
                    start: i,
                    count: 1
                });
                currentSet = l.set;
            } else {
                setGroups[setGroups.length - 1].count++;
            }
        });


        let hasCorrections = false;

        legs.forEach(l => {
            Object.values(l.players).forEach(p => {
                if (p.correctedFinish) {
                    hasCorrections = true;
                }
            });
        });

        if (legs.length > 0) {

            html += `
    <table style="
        width:auto;
        border-collapse: collapse;
        margin-top:8px;
        table-layout:fixed;
    ">
        <colgroup>
            <col style="width:${NAME_COL_WIDTH};">
            ${legs.map(() => `<col style="width:70px;">`).join("")}
        </colgroup>

        <tr style="border-bottom:1px solid #444;">

            <th style="text-align:left">${t("dartsRest")}</th>
            ${legs.map((l, i) => {
                const prevSet = legs[i - 1]?.set;
                const isNewSet = i > 0 && l.set !== prevSet;

                return `
        <th style="
            text-align:center;
            font-size:0.85em;
            ${isNewSet ? 'border-left:3px solid #888; padding-left:6px;' : ''}
        ">
            L${l.leg}
        </th>
    `;
}).join("")}
        </tr>
    `;
if (setGroups.length > 1) {
    html += `
<tr style="border-bottom:1px solid #444;">
    <th></th>
    ${setGroups.map((s, i) => `
        <th colspan="${s.count}" style="
            text-align:center;
            font-size:0.75em;
            color:#aaa;
            border-left:${i > 0 ? '3px solid #888' : 'none'};
        ">
           SET ${s.set + 1}
        </th>
    `).join("")}
</tr>
`;
}
            const playerNames = Object.keys(legs[0].players);

            playerNames.forEach(name => {
                html += `<tr style="border-top:1px solid #333;">`;

                // Name
                html += `
            <td style="
                text-align:left;
                padding-right:10px;
                text-transform:uppercase;
            ">
                ${name}
            </td>
        `;

                // Legs
                const setGroups = [];
                let currentSet = null;
                legs.forEach((l, i) => {
                    const data = l.players[name];

                    const prevSet = legs[i - 1]?.set;
                    const isNewSet = i > 0 && l.set !== prevSet;

                    if (data.type === "darts") {

                        legs.forEach((l, i) => {
                            if (l.set !== currentSet) {
                                setGroups.push({
                                    set: l.set,
                    start: i,
                    count: 1
                });
                currentSet = l.set;
            } else {
                setGroups[setGroups.length - 1].count++;
            }
});

        html += `
  <td style="
    text-align:center;
    font-weight:bold;
    ${isNewSet ? 'border-left:3px solid #888; padding-left:6px;' : ''}
"
title="${data.correctedFinish ? data.correctedDetail : ''}"
>
    <span style="
        display:inline-block;
        min-width:28px;
        padding:2px 6px;
        border-radius:50%;
        border:${data.correctedFinish ? '2px solid orange' : 'none'};
        color:${data.correctedFinish ? '#00e676' : '#00e676'};
    ">
        ${data.value}
    </span>
</td>
`;
    }
    else {
        html += `
<td style="
    text-align:center;
    color:#c0c0c0;
    opacity:0.8;
    ${isNewSet ? 'border-left:3px solid #888; padding-left:6px;' : ''}
">
    ${data.value}
</td>
`;
    }
});

                html += `</tr>`;





            });

            html += `</table>`;


            if (hasCorrections) {
                html += `
<div style="
    margin-top:10px;
    padding:8px 10px;
    font-size:0.85em;
    opacity:0.85;
    border-top:1px solid #333;
">
    <span style="color:orange; font-weight:bold;">${t("noticeTitle")}</span>
    ${t("noticeText")}
</div>
`;
            }
        }



        container.innerHTML = html;
        const target = document.querySelector('.chakra-card__body');

        if (target) {
            target.parentNode.insertBefore(container, target);
        } else {
            // Fallback falls UI noch nicht geladen
            document.body.prepend(container);
        }
    }

    function getGameId() {
        return window.location.href.match(/matches\/([a-z0-9-]+)/)?.[1];
    }



    function countCustomScores(match) {
        const result = {};

        // init
        match.players.forEach(p => {
            result[p.name.toUpperCase()] = {
                "90+": 0,
                "130+": 0,
                "171+": 0
            };
        });

        match.games?.forEach(game => {
            game.turns?.forEach(turn  => {

                const player = match.players
                .find(p => p.id === turn.playerId)
                ?.name.toUpperCase();

                const score =
                      turn.points ??
                      turn.score ??
                      turn.roundScore ??
                      0;

                if (!player) return;

                if (score === 180) {
                    // optional: falls du 180 separat zählen willst
                    // result[player]["180"]++;
                }
                else if (score >= 171 && score < 180) {
                    result[player]["171+"]++;
                }
                else if (score >= 130 && score < 171) {
                    result[player]["130+"]++;
                }
                else if (score >= 90 && score < 130) {
                    result[player]["90+"]++;
                }

            });
        });

        return result;
    }





    function countNoTripleOver100(match) {
        const result = {};
        match.players.forEach(p => result[p.name] = 0);

        match.games?.forEach(game => {

            const scorePerPlayer = {};
            match.players.forEach(p => {
                scorePerPlayer[p.id] = 501;
            });

            game.turns?.forEach(turn => {
                const pid = turn.playerId;

                let scored =
                    turn.points ??
                    turn.score ??
                    turn.roundScore ??
                    0;
                const player = match.players.find(p => p.id === pid)?.name;

                const currentScore = scorePerPlayer[pid];

                // 🔥 echte Würfe holen
                const throws = Array.isArray(turn.throws) ? turn.throws : [];

                // 🔥 NUR diese Triples zählen
                const allowedTriples = ["T20", "T19", "T18", "T17"];

                const hasTriple = throws.some(th =>
                                              allowedTriples.includes(th?.segment?.name)
                                             );

                // ✅ Deine finale Bedingung
                if (
                    currentScore > 100 &&
                    !hasTriple
                ) {
                    result[player]++;
                }

                // Score runterzählen (immer am Ende!)
                const score =
                      turn.points ??
                      turn.score ??
                      turn.roundScore ??
                      0;

                scorePerPlayer[pid] -= score;
                if (scorePerPlayer[pid] < 0) scorePerPlayer[pid] = 0;
            });
        });

        return result;
    }


    function countHighTriples(match) {
        const result = {};
        match.players.forEach(p => result[p.name.toUpperCase()] = 0);

        match.games?.forEach(game => {
            game.turns?.forEach(turn => {
                const player = match.players.find(p => p.id === turn.playerId)?.name.toUpperCase();

                const throws = turn.throws ?? [];

                throws.forEach(th => {
                    const name = th?.segment?.name;

                    if (["T20", "T19", "T18", "T17"].includes(name)) {
                        result[player]++;
                    }
                });
            });
        });

        return result;
    }



    function countNonDetectedThrows(match) {
        const result = {};

        // init
        match.players.forEach(p => {
            result[p.name.toUpperCase()] = 0;
        });

        match.games?.forEach(game => {
            game.turns?.forEach(turn => {
                const player = match.players.find(p => p.id === turn.playerId)?.name.toUpperCase();

                const throws = turn.throws ?? [];

                throws.forEach(th => {
                    if (th.entry !== "detected"
                        && th.entry !== "corrected_bouncer"
                        && th.entry !== "fill_miss"
                        && th.entry !== "referee_ai_corrected"
                        && th.entry !== "corrected_after_referee_ai"
                        && th.entry !== "referee_ai_confirmed") {
                        result[player]++;
                    }
                });
            });
        });

        return result;
    }








    function getHighFinishList(match) {
        const result = {};

        // init
        match.players.forEach(p => {
            result[p.name.toUpperCase()] = [];
        });

        match.games?.forEach(game => {

            const winnerId = game.winnerPlayerId;
            if (!winnerId) return;

            const playerName = match.players
            .find(p => p.id === winnerId)
            ?.name.toUpperCase();

            const turns = game.turns ?? [];

            // 👉 letzten Turn des Gewinners finden
            const lastTurn = [...turns]
            .reverse()
            .find(turn => turn.playerId === winnerId);

            if (!lastTurn) return;

            const points =
                  lastTurn.points ??
                  lastTurn.score ??
                  lastTurn.roundScore ??
                  0;

            // 👉 jetzt klappt’s garantiert
            if (points >= 90) {
                result[playerName].push(points);
            }
        });

        // sortieren
        Object.keys(result).forEach(name => {
            result[name].sort((a, b) => b - a);
        });

        return result;
    }




async function init() {
    const gameId = getGameId();
    if (!gameId) return;

    try {
        const match = await getMatchStats(gameId);

        // 🔥 Warten bis Daten wirklich da sind
        if (!match || !match.players || !match.matchStats) {
            return;
        }

        const stats = buildStats(match);

        // 👉 nur bei neuem Match loggen
        if (gameId !== currentGameId) {
            console.log("NEW MATCH:", gameId);
            currentGameId = gameId;
        }


// ❌ während Match → nichts anzeigen
if (!match.finishedAt) {
    document.querySelector('#ad-stats')?.remove();
    return;
}

// 🔥 WICHTIG: warten bis Games da sind
if (!match.games || match.games.length === 0) {
    console.log("Warte auf Legs...");
    return;
}

// ✅ jetzt erst rendern
render(stats, match);

    } catch (e) {
        console.error("Stats Error:", e);
    }
}


    (function () {
    const pushState = history.pushState;
    const replaceState = history.replaceState;

    function onChange() {
        console.log("URL CHANGE");
        currentGameId = null;

        // kleine Verzögerung → API ready
        setTimeout(init, 1000);
    }

    history.pushState = function () {
        pushState.apply(this, arguments);
        onChange();
    };

    history.replaceState = function () {
        replaceState.apply(this, arguments);
        onChange();
    };

    window.addEventListener("popstate", onChange);
})();

   setInterval(init, 2000);

})();