// ==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 none
// ==/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 = `
STRATEGIC PROBABILITY
100%
RIGHT SPOT
WRONG SPOT
NOT IN CODE
ENTRY HISTORY
REMAINING
`;
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();
})();