Загрузка данных


(async function runHighscoreBot() {
    // Укажите желаемый счет (например, 5000, 10000 или 25000)
    // Не ставьте сразу 100 000, чтобы не нарваться на 413 ошибку!
    const TARGET_SCORE = 5000; 

    console.log(`[*] Запуск симулятора на рекорд: ${TARGET_SCORE} очков...`);

    const initData = window.Telegram?.WebApp?.initData;
    if (!initData) {
        console.error("[-] Откройте игру внутри Telegram!");
        return;
    }

    // 1. Старт игры
    const startRes = await fetch('/api/start', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ initData: initData, mode: 'record' })
    }).then(r => r.json());

    if (!startRes.gameId) {
        console.error("[-] Ошибка старта:", startRes);
        return;
    }

    console.log(`[+] Игра начата: gameId=${startRes.gameId}, seed=${startRes.seed}`);

    function mulberry32(a) {
        return function() {
            a |= 0; a = (a + 0x6D2B79F5) | 0;
            var t = Math.imul(a ^ (a >>> 15), 1 | a);
            t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
            return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
        };
    }

    function makeSchedule(seed) {
        var rnd = mulberry32(seed), list = [], next = 0;
        return {
            upTo: function(t) {
                while (next <= t) {
                    var d = Math.min(1, next / 30), r = rnd();
                    var type = r < 0.03 ? 'boost' : r < 0.03 + (0.28 + d * 0.17) ? 'rock' : 'real';
                    list.push({
                        i: list.length,
                        t: next,
                        d: d,
                        type: type,
                        name: Math.floor(rnd() * 5),
                        rx: rnd(),
                        rv: rnd(),
                        rvx: rnd()
                    });
                    // продвигаем неиспользуемые генераторы, чтобы сохранить зерно
                    rnd(); rnd(); rnd();
                    next += (0.6 - d * 0.32) * (0.7 + rnd() * 0.6);
                }
                return list;
            }
        };
    }

    const W = window.innerWidth;
    const H = window.innerHeight;
    const bagW = Math.min(120, W * 0.28);
    const bagH = bagW * 216 / 520;
    const bagY = H - Math.max(70, H * 0.09) - bagH / 2;
    const mouthY = bagY - bagH / 2;

    const sched = makeSchedule(startRes.seed);
    const dt = 1 / 60;
    let t = 0;
    let spawnIdx = 0;
    let activeItems = [];
    let events = [];
    let score = 0;
    let boostUntil = 0;
    let lives = 3;

    // 2. Симулируем пока не наберем TARGET_SCORE
    while (lives > 0 && t < 3600) {
        t += dt;

        const due = sched.upTo(t);
        while (spawnIdx < due.length) {
            const u = due[spawnIdx++];
            const size = Math.min(84, W * 0.2);
            const vy = (300 + u.d * 520) * (0.8 + u.rv * 0.4) * (H / 800);
            const vx = (u.rvx - 0.5) * (40 + u.d * 200);
            activeItems.push({
                i: u.i,
                type: u.type,
                size: size,
                x: Math.max(size / 2, Math.min(W - size / 2, size / 2 + u.rx * (W - size))),
                y: -size,
                vy: vy,
                vx: vx
            });
        }

        for (let i = activeItems.length - 1; i >= 0; i--) {
            const it = activeItems[i];
            it.y += it.vy * dt;
            it.x += it.vx * dt;

            if (it.x < it.size / 2 || it.x > W - it.size / 2) {
                it.vx = -it.vx;
                it.x = Math.max(it.size / 2, Math.min(W - it.size / 2, it.x));
            }

            const half = it.size * 0.42;

            if (it.y + half >= mouthY && it.y - half < mouthY + 20) {
                let catchItem = false;

                if (score < TARGET_SCORE) {
                    if (it.type === 'boost') {
                        catchItem = true;
                        boostUntil = t + 5; // продлеваем x2
                    } else if (it.type === 'real') {
                        catchItem = true;
                        score += (t < boostUntil ? 20 : 10);
                    }
                } else {
                    // Завершаем тремя камнями
                    if (it.type === 'rock') {
                        catchItem = true;
                        lives--;
                    }
                }

                if (catchItem) {
                    events.push([it.i, Math.round(t * 1000)]);
                    activeItems.splice(i, 1);
                    continue;
                }
            }

            if (it.y - it.size > H) {
                activeItems.splice(i, 1);
            }
        }
    }

    const gameMs = Math.round((t + 0.5) * 1000);
    const payloadStr = JSON.stringify({
        initData: initData,
        gameId: startRes.gameId,
        W: W,
        H: H,
        events: events,
        gameMs: gameMs
    });

    console.log(`[+] Набрано очков: ${score}`);
    console.log(`[+] Всего событий: ${events.length}`);
    console.log(`[+] Размер данных для отправки: ${(payloadStr.length / 1024).toFixed(2)} КБ`);
    console.log(`[*] Ожидание времени игры: ${(gameMs / 1000).toFixed(1)} сек...`);

    // Если размер всё равно больше 800 КБ — предупреждаем
    if (payloadStr.length > 900000) {
        console.warn("[!] Внимание: размер тела запроса приближается к 1 МБ, сервер может выдать 413!");
    }

    // Честное ожидание тайминга
    await new Promise(res => setTimeout(res, gameMs));

    console.log("[*] Отправляем запрос на /api/finish...");
    const finishRes = await fetch('/api/finish', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: payloadStr
    }).then(r => r.json());

    console.log("[+] Ответ сервера:", finishRes);
})();