// ==UserScript== // @name Torn Chest Solver (Combo Solver) // @namespace Gemini.Torn // @version 5.8 // @description 4x3 grid, "Suggested Number" title, and professional donation phrasing. // @author Gemini // @match *.torn.com/christmas_town.php* // @grant none // @downloadURL 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 = ` COMBO SOLVER `; 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 = `
🌟 STEP 1: LOOK AT THE BOX

At the top, this tool shows a "Suggested Number." Type those numbers into the game and into this tool's keypad.

🌟 STEP 2: COPY THE COLORS

After you guess in the game, the game boxes will turn colors. Tap the boxes in THIS tool to match them!

🌟 STEP 3: WHAT COLORS MEAN?

Gray: We haven't guessed yet.
🟥 Red: That number is NOT in the code.
🟨 Yellow: The number is correct, but in the WRONG spot.
🟩 Green: Perfect! This number is correct and in the right spot.

🌟 STEP 4: HIT THE "OK" BUTTON

Once the colors in this tool match the game colors, hit OK. The tool will calculate the next best guess!


Questions, comments, concerns, and donations can be directed to:

Weeb_Phydoe [3262527]
`; document.getElementById('close-help').onclick = () => { showHelp = false; render(); }; } else { contentArea.style.display = "block"; container.style.width = "250px"; const {suggestion, prob} = solveLogic(); contentArea.innerHTML = `
✨ SUGGESTED NUMBER ✨
${suggestion}
Win Chance: ${prob}%
${[0,1,2].map(i => { const item = storage.currentInput[i] || {num: '-', color: '#444'}; return `
${item.num}
`; }).join('')}
${[1,2,3,4,5,6,7,8,9,0,'C','OK'].map(k => { let style = ""; if (k === 'OK') style = "background:#080;"; if (k === 'C') style = "background:#800;"; return ``; }).join('')}
`; 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 = 0; i <= 999; i++) { let p = i.toString().padStart(3, '0').split(''); if (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); 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(); })();