Greasy Fork

Greasy Fork is available in English.

Torn Chest Solver (No-Zero Edition)

4x3 grid, "Suggested Number" title, logic restricted to digits 1-9.

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Torn Chest Solver (No-Zero Edition)
// @namespace    Gemini.Torn
// @version      5.9
// @description  4x3 grid, "Suggested Number" title, logic restricted to digits 1-9.
// @author       Gemini
// @match        *.torn.com/christmas_town.php*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    let storage = JSON.parse(localStorage.getItem('tornSolverData')) || {
        history: [], currentInput: [], x: 20, y: 80, minimized: false
    };
    
    if (storage.x < 0 || storage.x > window.innerWidth) storage.x = 20;
    if (storage.y < 0 || storage.y > window.innerHeight) storage.y = 80;

    function saveData() { localStorage.setItem('tornSolverData', JSON.stringify(storage)); }
    let showHelp = false;

    const style = document.createElement('style');
    style.innerHTML = `
        @keyframes blinker { 50% { opacity: 0.2; color: #fff; } }
        .blinking-link { animation: blinker 1.2s linear infinite; }
        .help-text b { color: #0f0; font-size: 12px; }
        .help-text p { margin: 8px 0; color: #ccc; }
        .num-btn { padding: 12px 0; background: #333; color: #fff; border: none; border-radius: 4px; font-weight: bold; font-size: 16px; cursor: pointer; }
        .num-btn:active { background: #555; }
    `;
    document.head.appendChild(style);

    function createUI() {
        if (document.getElementById("torn-solver-window")) return;
        const container = document.createElement('div');
        container.id = "torn-solver-window";
        container.style = `position: fixed; top: ${storage.y}px; left: ${storage.x}px; z-index: 999999; background: #222; color: #fff; border-radius: 10px; border: 2px solid #444; font-family: sans-serif; width: 250px; box-shadow: 0 4px 15px rgba(0,0,0,0.5); touch-action: none; user-select: none;`;
        
        const header = document.createElement('div');
        header.style = "background: #444; padding: 10px; cursor: move; border-radius: 8px 8px 0 0; display: flex; justify-content: space-between; align-items: center; touch-action: none;";
        header.innerHTML = `
            <button id="help-toggle" style="background: #0af; color: white; border: none; border-radius: 50%; width: 24px; height: 24px; font-weight: bold; cursor: pointer;">?</button>
            <span style="font-weight: bold; font-size: 10px; color: #ff4444;">COMBO SOLVER</span>
            <button id="min-btn" style="background: #666; color: white; border: none; border-radius: 3px; width: 25px; height: 20px;">${storage.minimized ? '□' : '_'}</button>
        `;
        container.appendChild(header);

        const contentArea = document.createElement('div');
        contentArea.style = "padding: 10px; display: " + (storage.minimized ? "none" : "block");
        container.appendChild(contentArea);
        document.body.appendChild(container);

        const render = () => {
            if (storage.minimized) {
                contentArea.style.display = "none";
                document.getElementById('min-btn').innerText = "□";
                container.style.width = "140px";
            } else if (showHelp) {
                contentArea.style.display = "block";
                container.style.width = "250px";
                contentArea.innerHTML = `
                    <div class="help-text" style="font-size:11px; line-height:1.4; max-height:350px; overflow-y:auto; padding-right:5px;">
                        <b>🌟 STEP 1: LOOK AT THE BOX</b>
                        <p>At the top, this tool shows a "Suggested Number." Type those numbers into the game and into this tool's keypad.</p>
                        
                        <b>🌟 STEP 2: COPY THE COLORS</b>
                        <p>After you guess in the game, the game boxes will turn colors. Tap the boxes in THIS tool to match them!</p>
                        
                        <b>🌟 STEP 3: WHAT COLORS MEAN?</b>
                        <p>⬜ <b>Gray:</b> We haven't guessed yet.<br>
                           🟥 <b>Red:</b> That number is NOT in the code.<br>
                           🟨 <b>Yellow:</b> The number is correct, but in the WRONG spot.<br>
                           🟩 <b>Green:</b> Perfect! This number is correct and in the right spot.</p>
                        
                        <b>🌟 STEP 4: HIT THE "OK" BUTTON</b>
                        <p>Once the colors in this tool match the game colors, hit OK. The tool will calculate the next best guess!</p>

                        <hr style="border:0; border-top:1px solid #444; margin:10px 0;">
                        <div style="font-style:italic; color:#bbb; text-align:center;">
                            Questions, comments, and concerns can be directed to:
                            <br><br>
                            <a href="https://www.torn.com/profiles.php?XID=3262527" target="_blank" class="blinking-link" style="color:#ff4444; text-decoration:none; font-weight:bold; font-size:12px;">Weeb_Phydoe [3262527]</a>
                        </div>
                        <button id="close-help" style="margin-top:10px; width:100%; background:#0af; color:white; border:none; padding:10px; border-radius:4px; font-weight:bold; cursor:pointer;">BACK</button>
                    </div>
                `;
                document.getElementById('close-help').onclick = () => { showHelp = false; render(); };
            } else {
                contentArea.style.display = "block";
                container.style.width = "250px";
                const {suggestion, prob} = solveLogic();
                
                contentArea.innerHTML = `
                    <div style="text-align:center; background:#111; padding:6px; border-radius:5px; margin-bottom:10px;">
                        <span style="font-size:9px; color:#888;">✨ SUGGESTED NUMBER ✨</span><br>
                        <b style="color:#0f0; font-size:22px; letter-spacing:3px;">${suggestion}</b>
                        <div style="font-size:10px; color:#0af; margin-top:3px;">Win Chance: ${prob}%</div>
                    </div>
                    <div style="display:flex; justify-content:center; gap:8px; margin-bottom:10px;">
                        ${[0,1,2].map(i => {
                            const item = storage.currentInput[i] || {num: '-', color: '#444'};
                            return `<div class="input-slot" data-index="${i}" style="width:55px; height:55px; background:${item.color}; display:flex; align-items:center; justify-content:center; border-radius:10px; font-weight:bold; font-size:30px; border:1px solid #666;">${item.num}</div>`;
                        }).join('')}
                    </div>
                    <div style="display:grid; grid-template-columns: repeat(4, 1fr); gap:6px; margin-bottom:10px;">
                        ${[1,2,3,4,5,6,7,8,9,'C','OK'].map(k => {
                            let style = "";
                            if (k === 'OK') style = "background:#080; grid-column: span 2;";
                            if (k === 'C') style = "background:#800;";
                            return `<button class="num-btn" data-val="${k}" style="${style}">${k}</button>`;
                        }).join('')}
                    </div>
                    <button id="reset-game-btn" style="width:100%; background:#444; color:#fff; border:none; padding:6px; border-radius:4px; font-size:9px;">NEW GAME</button>
                `;

                contentArea.querySelectorAll('.input-slot').forEach(slot => {
                    slot.onclick = () => {
                        const i = slot.getAttribute('data-index');
                        if (!storage.currentInput[i]) return;
                        const colors = ['#666', '#ff4444', '#ffcc00', '#00aa00'];
                        storage.currentInput[i].color = colors[(colors.indexOf(storage.currentInput[i].color) + 1) % colors.length];
                        saveData(); render();
                    };
                });

                contentArea.querySelectorAll('.num-btn').forEach(btn => btn.onclick = () => {
                    const v = btn.getAttribute('data-val');
                    if (v === 'C') storage.currentInput = [];
                    else if (v === 'OK' && storage.currentInput.length === 3) {
                        storage.history.push([...storage.currentInput]); storage.currentInput = [];
                    } else if (storage.currentInput.length < 3 && !isNaN(v)) {
                        storage.currentInput.push({num: v, color: '#666'});
                    }
                    saveData(); render();
                });
                document.getElementById('reset-game-btn').onclick = () => { storage.history = []; storage.currentInput = []; saveData(); render(); };
            }
        };

        let isDragging = false, startX, startY;
        const dStart = (e) => { const t = e.touches ? e.touches[0] : e; isDragging = true; startX = t.clientX - container.offsetLeft; startY = t.clientY - container.offsetTop; };
        const dMove = (e) => { if (!isDragging) return; const t = e.touches ? e.touches[0] : e; storage.x = t.clientX - startX; storage.y = t.clientY - startY; container.style.left = storage.x + "px"; container.style.top = storage.y + "px"; if (e.cancelable) e.preventDefault(); };
        const dEnd = () => { isDragging = false; saveData(); };
        header.addEventListener('mousedown', dStart); header.addEventListener('touchstart', dStart, {passive: false});
        window.addEventListener('mousemove', dMove); window.addEventListener('touchmove', dMove, {passive: false});
        window.addEventListener('mouseup', dEnd); window.addEventListener('touchend', dEnd);
        document.getElementById('help-toggle').onclick = () => { showHelp = !showHelp; render(); };
        document.getElementById('min-btn').onclick = () => { storage.minimized = !storage.minimized; saveData(); render(); };
        render();
    }

    function solveLogic() {
        let possibilities = [];
        for (let i = 111; i <= 999; i++) {
            let p = i.toString().split('');
            if (!p.includes('0') && new Set(p).size === 3) possibilities.push(p);
        }
        storage.history.forEach(attempt => {
            possibilities = possibilities.filter(p => {
                for (let i = 0; i < 3; i++) {
                    const color = attempt[i].color;
                    const num = attempt[i].num;
                    if (color === '#00aa00' && p[i] !== num) return false;
                    if (color === '#ff4444' && p.includes(num)) return false;
                    if (color === '#ffcc00') { if (!p.includes(num) || p[i] === num) return false; }
                }
                return true;
            });
        });

        const count = possibilities.length;
        const confidence = count === 0 ? 0 : Math.floor(100 / count);

        // Turn 1, 2, 3 Sweep (using only 1-9)
        if (storage.history.length < 3 && count > 1) {
            let tested = new Set();
            storage.history.forEach(att => att.forEach(item => tested.add(item.num)));
            let untried = "123456789".split("").filter(d => !tested.has(d));
            if (untried.length >= 3) return { suggestion: untried.slice(0,3).join(" "), prob: confidence };
        }
        return { suggestion: possibilities[0] ? possibilities[0].join(" ") : "???", prob: confidence };
    }

    setInterval(createUI, 2000);
    createUI();
})();