Torn Chest Solver (Optimized Sidecar)

Restored width with a tightened history column to remove dead space.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Torn Chest Solver (Optimized Sidecar)
// @namespace    Gemini.Torn
// @version      6.8
// @description  Restored width with a tightened history column to remove dead space.
// @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
    };
    
    function saveData() { localStorage.setItem('tornSolverData', JSON.stringify(storage)); }
    let showHelp = false;

    const style = document.createElement('style');
    style.innerHTML = `
        .num-btn { padding: 10px 0; background: #333; color: #fff; border: none; border-radius: 4px; font-weight: bold; font-size: 14px; cursor: pointer; touch-action: manipulation; }
        .num-btn:active { background: #555; }
        .history-row { display: flex; align-items: center; gap: 4px; margin-bottom: 4px; background: #1a1a1a; padding: 4px; border-radius: 4px; width: fit-content; }
        .hist-box { width: 18px; height: 18px; border-radius: 3px; display: flex; align-items: center; justify-content: center; font-size: 11px; font-weight: bold; border: 1px solid #444; }
    `;
    document.head.appendChild(style);

    function createUI() {
        if (document.getElementById("torn-solver-window")) return;
        const container = document.createElement('div');
        container.id = "torn-solver-window";
        // Original comfortable width restored
        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: 380px; box-shadow: 0 4px 15px rgba(0,0,0,0.5); user-select: none; touch-action: none;`;
        
        const header = document.createElement('div');
        header.style = "background: #444; padding: 8px; cursor: move; border-radius: 8px 8px 0 0; display: flex; justify-content: space-between; align-items: center; touch-action: none;";
        header.innerHTML = `
            <div style="display:flex; align-items:center; gap:8px;">
                <button id="help-toggle" style="background: #0af; color: white; border: none; border-radius: 50%; width: 20px; height: 20px; font-weight: bold; cursor: pointer; font-size: 12px;">?</button>
                <span style="font-weight: bold; font-size: 10px; color: #ff4444;">COMBO SOLVER PRO</span>
            </div>
            <button id="min-btn" style="background: #666; color: white; border: none; border-radius: 3px; width: 25px; height: 20px; cursor: pointer;">_</button>
        `;
        container.appendChild(header);

        const contentArea = document.createElement('div');
        contentArea.style = "padding: 10px;";
        container.appendChild(contentArea);
        document.body.appendChild(container);

        const render = () => {
            if (storage.minimized) {
                contentArea.style.display = "none";
                container.style.width = "160px";
                return;
            }
            
            container.style.width = "380px";
            contentArea.style.display = "block";

            if (showHelp) {
                contentArea.innerHTML = `
                    <div class="help-text">
                        <b style="color:#0f0; font-size:11px;">🌟 STEPS</b>
                        <p style="font-size:10px; color:#ccc;">Match game colors by tapping the input boxes, then hit OK.</p>
                        <div style="text-align:center; padding:10px; border-top:1px solid #444;">
                            <a href="https://www.torn.com/profiles.php?XID=3262527" target="_blank" style="color:#ff4444; text-decoration:none; font-weight:bold; font-size:11px;">Contact: Weeb_Phydoe [3262527]</a>
                        </div>
                        <button id="close-help" style="width:100%; background:#0af; color:white; border:none; padding:8px; border-radius:4px; font-weight:bold; cursor:pointer;">BACK</button>
                    </div>
                `;
                document.getElementById('close-help').onclick = () => { showHelp = false; render(); };
            } else {
                const {suggestion, prob} = solveLogic();
                contentArea.innerHTML = `
                    <div style="display: flex; gap: 12px;">
                        <div style="flex: 1.5;">
                            <div style="text-align:center; background:#111; padding:4px; border-radius:5px; margin-bottom:8px; border: 1px solid #333;">
                                <span style="font-size:9px; color:#888;">SUGGESTION</span><br>
                                <b style="color:#0f0; font-size:18px; letter-spacing:2px;">${suggestion}</b>
                                <div style="font-size:9px; color:#0af;">Win: ${prob}%</div>
                            </div>
                            <div style="display:flex; justify-content:center; gap:5px; margin-bottom:8px;">
                                ${[0,1,2].map(i => {
                                    const item = storage.currentInput[i] || {num: '-', color: '#444'};
                                    return `<div class="input-slot" data-index="${i}" style="width:42px; height:42px; background:${item.color}; display:flex; align-items:center; justify-content:center; border-radius:8px; font-weight:bold; font-size:22px; border:1px solid #555;">${item.num}</div>`;
                                }).join('')}
                            </div>
                            <div style="display:grid; grid-template-columns: repeat(4, 1fr); gap:4px;">
                                ${[1,2,3,4,5,6,7,8,9,'C','OK'].map(k => {
                                    let s = ""; if (k === 'OK') s = "background:#080; grid-column: span 2;"; if (k === 'C') s = "background:#800;";
                                    return `<button class="num-btn" data-val="${k}" style="${s}">${k}</button>`;
                                }).join('')}
                            </div>
                        </div>
                        <div style="width: 100px; display: flex; flex-direction: column; border-left: 1px solid #444; padding-left: 10px;">
                            <div style="font-size:9px; color:#888; margin-bottom:4px; text-transform:uppercase;">History</div>
                            <div id="history-column" style="flex-grow: 1; max-height: 120px; overflow-y: auto; margin-bottom: 8px;">
                                ${storage.history.map((h, idx) => `
                                    <div class="history-row">
                                        <span style="font-size:9px; color:#555; width:12px;">${idx+1}</span>
                                        <div class="hist-box" style="background:${h[0].color}">${h[0].num}</div>
                                        <div class="hist-box" style="background:${h[1].color}">${h[1].num}</div>
                                        <div class="hist-box" style="background:${h[2].color}">${h[2].num}</div>
                                    </div>
                                `).reverse().join('')}
                            </div>
                            <button id="reset-game-btn" style="width:100%; background:#444; color:#fff; border:none; padding:5px; border-radius:4px; font-size:9px;">NEW GAME</button>
                        </div>
                    </div>
                `;

                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 onStart = (e) => {
            if (e.target.tagName === 'BUTTON') return;
            const touch = e.touches ? e.touches[0] : e;
            isDragging = true;
            startX = touch.clientX - container.offsetLeft;
            startY = touch.clientY - container.offsetTop;
        };
        const onMove = (e) => { if (isDragging) { const touch = e.touches ? e.touches[0] : e; storage.x = touch.clientX - startX; storage.y = touch.clientY - startY; container.style.left = storage.x + "px"; container.style.top = storage.y + "px"; } };
        const onEnd = () => { isDragging = false; saveData(); };

        header.addEventListener('mousedown', onStart);
        header.addEventListener('touchstart', onStart, { passive: true });
        window.addEventListener('mousemove', onMove);
        window.addEventListener('touchmove', onMove, { passive: false });
        window.addEventListener('mouseup', onEnd);
        window.addEventListener('touchend', onEnd);

        document.getElementById('help-toggle').onclick = (e) => { e.stopPropagation(); showHelp = !showHelp; render(); };
        document.getElementById('min-btn').onclick = (e) => { e.stopPropagation(); 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' && (!p.includes(num) || p[i] === num)) return false;
                }
                return true;
            });
        });
        const count = possibilities.length;
        const confidence = count === 0 ? 0 : Math.floor(100 / count);
        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 };
    }

    createUI();
})();