// ==UserScript== // @name Combination Chest Solver for Torn Christmas Town ( NON-PDA ) // @namespace torn.christmas.chest.solver // @version 5.2 // @description Updated Win Chance to "Strategic Probability" using look-ahead filtering logic. // @match https://www.torn.com/christmas_town.php* // @grant none // @downloadURL https://update.greasyfork.icu/scripts/559558/Combination%20Chest%20Solver%20for%20Torn%20Christmas%20Town%20%28%20NON-PDA%20%29.user.js // @updateURL https://update.greasyfork.icu/scripts/559558/Combination%20Chest%20Solver%20for%20Torn%20Christmas%20Town%20%28%20NON-PDA%20%29.meta.js // ==/UserScript== (function () { 'use strict'; if (!window.location.href.includes('christmas_town.php')) return; const digits = "123456789"; let possibleCodes = []; let attemptsUsed = 0; let currentFeedback = ""; let historyState = []; const MAX_ATTEMPTS = 4; function generateCodes() { const codes = []; for (let a of digits) { for (let b of digits) { for (let c of digits) { if (a !== b && b !== c && a !== c) codes.push(a + b + c); } } } return codes; } function score(guess, code) { let res = ""; for (let i = 0; i < 3; i++) { if (guess[i] === code[i]) res += "g"; else if (code.includes(guess[i])) res += "y"; else res += "r"; } return res; } function getBestGuess() { if (possibleCodes.length === 0) return "---"; if (possibleCodes.length === 1) return possibleCodes[0]; let bestCode = possibleCodes[0], maxEntropy = -1; const allPossible = generateCodes(); const candidates = possibleCodes.length < 20 ? allPossible : possibleCodes; for (let guess of candidates) { let groups = {}; for (let code of possibleCodes) { let res = score(guess, code); groups[res] = (groups[res] || 0) + 1; } let entropy = 0; for (let res in groups) { let p = groups[res] / possibleCodes.length; entropy -= p * Math.log2(p); } if (possibleCodes.includes(guess)) entropy += 0.01; if (entropy > maxEntropy) { maxEntropy = entropy; bestCode = guess; } } return bestCode; } // NEW: Calculate probability based on filtering efficiency function calculateStrategicProb(count, turns, bestMove) { if (count <= turns) return 100; if (turns <= 0 || count === 0) return 0; // Estimate how many possibilities the best move eliminates on average let groups = {}; for (let code of possibleCodes) { let res = score(bestMove, code); groups[res] = (groups[res] || 0) + 1; } let avgRemaining = 0; for (let res in groups) { avgRemaining += (groups[res] * groups[res]); } avgRemaining /= count; // If filtering leaves us with fewer candidates than remaining turns, we are likely to win let strength = (turns / avgRemaining) * 100; return Math.min(100, Math.max(Math.floor(strength), Math.floor((turns/count)*100))); } function getColorScale(pct) { return `rgb(${Math.floor(255 * (1 - pct))}, ${Math.floor(255 * pct)}, 50)`; } function createUI() { if (document.getElementById("chestSolverBox")) return; const box = document.createElement("div"); box.id = "chestSolverBox"; const savedTop = localStorage.getItem('cs_top') || "120px"; const savedLeft = localStorage.getItem('cs_left') || "calc(100% - 300px)"; const isMinimized = localStorage.getItem('cs_minimized') === 'true'; box.style = `position: fixed; top: ${savedTop}; left: ${savedLeft}; width: ${isMinimized ? '180px' : '280px'}; background: #222; color: #fff; border: 1px solid #444; border-radius: 8px; z-index: 999999; box-shadow: 0 4px 15px rgba(0,0,0,0.5); font-family: 'Segoe UI', Arial, sans-serif; overflow: hidden;`; box.innerHTML = `
🎄 Chest Solver Elite
${isMinimized ? '+' : '−'}
IDEAL MOVE 🖱️
---
TURNS
4
STRATEGIC PROBABILITY
100%
RIGHT SPOT
WRONG SPOT
NOT IN CODE
KNOWN POSITIONS
_ _ _
DISCARDED
None
ENTRY HISTORY REMAINING
Likely Solutions ():
`; document.body.appendChild(box); // Draggable logic and all button listeners... let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; const header = document.getElementById("cs_header"); header.onmousedown = (e) => { if (e.target.id === "cs_min_btn") return; e.preventDefault(); pos3 = e.clientX; pos4 = e.clientY; document.onmouseup = () => { document.onmouseup = null; document.onmousemove = null; localStorage.setItem('cs_top', box.style.top); localStorage.setItem('cs_left', box.style.left); }; document.onmousemove = (e) => { e.preventDefault(); pos1 = pos3 - e.clientX; pos2 = pos4 - e.clientY; pos3 = e.clientX; pos4 = e.clientY; box.style.top = (box.offsetTop - pos2) + "px"; box.style.left = (box.offsetLeft - pos1) + "px"; }; }; const minBtn = document.getElementById("cs_min_btn"); const content = document.getElementById("cs_content"); minBtn.onclick = () => { const becomingMinimized = content.style.display !== "none"; content.style.display = becomingMinimized ? "none" : "block"; box.style.width = becomingMinimized ? "180px" : "280px"; minBtn.textContent = becomingMinimized ? "+" : "−"; localStorage.setItem('cs_minimized', becomingMinimized); }; const gInput = document.getElementById("cs_guess"); gInput.oninput = updateFeedbackDisplay; document.getElementById("btn_g").onclick = () => addColor('g'); document.getElementById("btn_y").onclick = () => addColor('y'); document.getElementById("btn_r").onclick = () => addColor('r'); document.getElementById("cs_add").onclick = addGuess; document.getElementById("cs_undo").onclick = undoLast; document.getElementById("cs_reset").onclick = resetSolver; document.getElementById("cs_best_move_container").onclick = function() { const move = document.getElementById("cs_best_move").innerText; if (move !== "---") { gInput.value = move; hideError(); updateFeedbackDisplay(); } }; resetSolver(); } function addColor(char) { const val = document.getElementById("cs_guess").value; if (val.length === 3 && currentFeedback.length < 3) { currentFeedback += char; updateFeedbackDisplay(); hideError(); } else if (val.length < 3) { showError("Input 3 digits first."); } } function updateFeedbackDisplay() { const display = document.getElementById("cs_feedback_display"); const val = document.getElementById("cs_guess").value; let html = ""; for (let i = 0; i < 3; i++) { let num = val[i] || "_"; let color = "#555"; if (currentFeedback[i]) { color = currentFeedback[i] === 'g' ? "#4CAF50" : (currentFeedback[i] === 'y' ? "#FFC107" : "#F44336"); } html += `${num}`; } display.innerHTML = html; } function updateTrackers() { let pattern = ["_", "_", "_"]; let discarded = new Set(digits.split("")); if (possibleCodes.length > 0) { for (let i = 0; i < 3; i++) { let char = possibleCodes[0][i]; if (possibleCodes.every(code => code[i] === char)) pattern[i] = char; } possibleCodes.forEach(code => { for (let char of code) discarded.delete(char); }); } document.getElementById("cs_pattern").textContent = pattern.join(" "); const dArr = Array.from(discarded).sort(); document.getElementById("cs_discarded").textContent = dArr.length > 0 ? dArr.join(" ") : "None"; } function addGuess() { const gInput = document.getElementById("cs_guess"); const g = gInput.value; const f = currentFeedback; const histDiv = document.getElementById("cs_history"); if (g.length < 3 || f.length < 3) { showError(g.length < 3 ? "Need 3 digits." : "Assign 3 colors."); return; } historyState.push({ codes: [...possibleCodes], attempts: attemptsUsed, histHTML: histDiv.innerHTML }); attemptsUsed++; possibleCodes = possibleCodes.filter(c => score(g, c) === f); let coloredGuess = ""; for (let i = 0; i < 3; i++) { let color = f[i] === 'g' ? "#4CAF50" : (f[i] === 'y' ? "#FFC107" : "#F44336"); coloredGuess += `${g[i]}`; } const row = `
${coloredGuess} ${possibleCodes.length} left
`; histDiv.innerHTML = row + histDiv.innerHTML; updateUI(); gInput.value = ""; currentFeedback = ""; updateFeedbackDisplay(); } function undoLast() { if (historyState.length === 0) return; const last = historyState.pop(); possibleCodes = last.codes; attemptsUsed = last.attempts; document.getElementById("cs_history").innerHTML = last.histHTML; updateUI(); } function updateUI() { const count = possibleCodes.length; const left = Math.max(0, MAX_ATTEMPTS - attemptsUsed); document.getElementById("cs_attempts_left").textContent = left; document.getElementById("cs_count").textContent = count; const bestMove = getBestGuess(); document.getElementById("cs_best_move").textContent = bestMove; const winEl = document.getElementById("cs_win_chance"); let prob = calculateStrategicProb(count, left, bestMove); winEl.textContent = prob + "%"; winEl.style.color = getColorScale(prob / 100); const resEl = document.getElementById("cs_results"); resEl.innerHTML = ""; possibleCodes.slice(0, 20).forEach(c => { const row = document.createElement("div"); row.style = "cursor: pointer; border-bottom: 1px solid #222; padding: 2px;"; row.onclick = () => { document.getElementById('cs_guess').value = c; updateFeedbackDisplay(); }; row.innerHTML = `${c}`; resEl.appendChild(row); }); updateTrackers(); } function resetSolver() { possibleCodes = generateCodes(); attemptsUsed = 0; historyState = []; currentFeedback = ""; document.getElementById("cs_guess").value = ""; const h = document.getElementById("cs_history"); if(h) h.innerHTML = ""; updateUI(); updateFeedbackDisplay(); } function showError(msg) { const err = document.getElementById("cs_error_msg"); err.textContent = msg; err.style.display = "block"; setTimeout(() => { err.style.display = "none"; }, 3000); } function hideError() { document.getElementById("cs_error_msg").style.display = "none"; } const observer = new MutationObserver(() => { if (document.body) createUI(); }); observer.observe(document.documentElement, { childList: true, subtree: true }); createUI(); })();