您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Hides incorrect button for high-low based on remaining cards.
当前为
// ==UserScript== // @name Torn High-Low Helper (Advanced) // @namespace http://tampermonkey.net/ // @version 2.0 // @description Hides incorrect button for high-low based on remaining cards. // @author Lollipop :) // @match https://www.torn.com/page.php?sid=highlow // @grant none // ==/UserScript== (function() { 'use strict'; const CHECK_INTERVAL = 300; // Check slightly more often const MAX_WAIT_TIME = 15000; // Wait a maximum of 15 seconds let checkTimer = null; let waitTime = 0; // --- Game State --- let remainingDeck = {}; // Stores count of each card value (2-14) let lastDealerCardValue = null; let lastPlayerCardValue = null; // To avoid double-counting on rapid mutations let isGameActive = false; // Track if we are in an active game round // --- Card Value Mapping (Ace High) --- const CARD_VALUES = { '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9, '10': 10, 'J': 11, 'Q': 12, 'K': 13, 'A': 14 }; const ALL_RANKS = Object.keys(CARD_VALUES); // ['2', '3', ..., 'A'] // --- Helper Function to Parse Card Value --- function getCardValue(ratingText) { if (!ratingText) return null; const text = ratingText.trim().toUpperCase(); return CARD_VALUES[text] || null; // Return numeric value or null } // --- Reset Deck for New Game --- function resetDeck() { console.log("Resetting deck for new game."); remainingDeck = {}; for (const rank in CARD_VALUES) { remainingDeck[CARD_VALUES[rank]] = 4; // 4 suits per rank } lastDealerCardValue = null; lastPlayerCardValue = null; isGameActive = true; // Mark game as active after reset (usually follows start) console.log("Initial Deck:", JSON.stringify(remainingDeck)); } // --- Update Deck Count When Card is Revealed --- function removeCardFromDeck(cardValue) { if (cardValue !== null && remainingDeck[cardValue] > 0) { remainingDeck[cardValue]--; console.log(`Removed card ${Object.keys(CARD_VALUES).find(key => CARD_VALUES[key] === cardValue)} (${cardValue}). Remaining: ${remainingDeck[cardValue]}`); // console.log("Current Deck State:", JSON.stringify(remainingDeck)); return true; // Card was successfully removed } else if (cardValue !== null) { console.warn(`Attempted to remove card ${Object.keys(CARD_VALUES).find(key => CARD_VALUES[key] === cardValue)} (${cardValue}), but count is already ${remainingDeck[cardValue] ?? 'undefined'}. Deck might be out of sync.`); } return false; // Card not removed (either invalid or count was 0) } // --- Calculate Higher/Lower Counts --- function calculateProbabilities(dealerValue) { if (dealerValue === null || !isGameActive) { return { lower: 0, higher: 0, totalRemaining: 0 }; // Not enough info or game not active } let lowerCount = 0; let higherCount = 0; let totalRemaining = 0; for (const value in remainingDeck) { const numericValue = parseInt(value, 10); const count = remainingDeck[value]; totalRemaining += count; if (numericValue < dealerValue) { lowerCount += count; } else if (numericValue > dealerValue) { higherCount += count; } // Cards with equal value don't count for higher/lower } console.log(`Dealer: ${dealerValue}. Remaining: Lower=${lowerCount}, Higher=${higherCount}, Total=${totalRemaining}`); return { lower: lowerCount, higher: higherCount, totalRemaining: totalRemaining }; } // --- Function to perform the check and update logic --- function checkAndUpdateGame() { // Select elements every time to ensure they are current const dealerCardElement = document.querySelector('.dealer-card'); const playerCardElement = document.querySelector('.you-card'); const lowerButton = document.querySelector('.actions-wrap .action-btn-wrap.low'); const higherButton = document.querySelector('.actions-wrap .action-btn-wrap.high'); const startButton = document.querySelector('.action-btn-wrap.startGame'); // For reset detection const resultWrap = document.querySelector('.game-result-wrap'); // For reset detection const highlowWrap = document.querySelector('.highlow-main-wrap'); // To check if game interface is visible if (!dealerCardElement || !playerCardElement || !lowerButton || !higherButton || !highlowWrap) { console.warn("Core game elements not found during update check."); return; // Should not happen if waitForElements worked, but safety first } // --- Check for Game End/Reset Conditions --- // If start button is visible OR result wrap is visible, the active game has ended. // We also check if the main highlow wrap is hidden (often happens briefly between rounds) const gameEnded = (startButton && startButton.offsetParent !== null) || (resultWrap && resultWrap.offsetParent !== null) || (highlowWrap.offsetParent === null); if (gameEnded && isGameActive) { console.log("Game appears to have ended or reset. Setting isGameActive to false."); isGameActive = false; // Reset button visibility to default (hidden until next dealer card) lowerButton.style.display = 'none'; higherButton.style.display = 'none'; lastDealerCardValue = null; // Clear last known cards lastPlayerCardValue = null; } // --- If game is not active, do nothing further --- if (!isGameActive) { // Make sure buttons remain hidden if game isn't active if (lowerButton.style.display !== 'none') lowerButton.style.display = 'none'; if (higherButton.style.display !== 'none') higherButton.style.display = 'none'; // console.log("Game not active. Waiting for Start Game."); return; } // --- Process Current Cards --- const currentDealerRatingElement = dealerCardElement?.querySelector('span.rating'); const currentPlayerRatingElement = playerCardElement?.querySelector('span.rating'); const dealerRatingText = currentDealerRatingElement?.textContent; const playerRatingText = currentPlayerRatingElement?.textContent?.trim() ?? ''; const currentDealerValue = getCardValue(dealerRatingText); const currentPlayerValue = getCardValue(playerRatingText); // --- Track Revealed Cards --- // Only remove if the value changed and is valid if (currentDealerValue !== null && currentDealerValue !== lastDealerCardValue) { console.log(`New Dealer Card: ${dealerRatingText} (${currentDealerValue})`); removeCardFromDeck(currentDealerValue); lastDealerCardValue = currentDealerValue; lastPlayerCardValue = null; // Reset player card tracking when dealer changes } // Player card is revealed (end of a round, before next dealer card) if (currentPlayerValue !== null && currentPlayerValue !== lastPlayerCardValue) { console.log(`Player Card Revealed: ${playerRatingText} (${currentPlayerValue})`); removeCardFromDeck(currentPlayerValue); lastPlayerCardValue = currentPlayerValue; // Hide buttons as choice is made lowerButton.style.display = 'none'; higherButton.style.display = 'none'; return; // Don't make prediction when player card is shown } // --- Player Card is hidden, Dealer Card is shown: Make Prediction --- if (playerRatingText === '' && currentDealerValue !== null) { const { lower, higher, totalRemaining } = calculateProbabilities(currentDealerValue); if (totalRemaining === 0 && isGameActive) { console.warn("No cards left in tracked deck, but game is active? Deck might be out of sync."); // Fallback to simple strategy if deck is empty? Or hide both? Hide based on simple for now. if (currentDealerValue <= 7) { // Simple fallback lowerButton.style.display = 'none'; higherButton.style.display = 'inline-block'; } else { higherButton.style.display = 'none'; lowerButton.style.display = 'inline-block'; } return; } // Decision Logic: Hide the less likely button if (higher > lower) { // More higher cards remaining -> Suggest 'Higher' lowerButton.style.display = 'none'; higherButton.style.display = 'inline-block'; console.log(`Prediction: HIGHER (H: ${higher} > L: ${lower})`); } else if (lower > higher) { // More lower cards remaining -> Suggest 'Lower' higherButton.style.display = 'none'; lowerButton.style.display = 'inline-block'; console.log(`Prediction: LOWER (L: ${lower} > H: ${higher})`); } else { // Equal probability - Use simple strategy as tie-breaker (7 is middle) // Or maybe show both? Hiding one is the request. console.log(`Prediction: EQUAL (L: ${lower}, H: ${higher}). Using simple tie-breaker.`); if (currentDealerValue <= 7) { // Favor higher for <= 7 lowerButton.style.display = 'none'; higherButton.style.display = 'inline-block'; } else { // Favor lower for > 7 higherButton.style.display = 'none'; lowerButton.style.display = 'inline-block'; } } } else if (playerRatingText === '' && currentDealerValue === null) { // No dealer card yet (very start of game after clicking start) lowerButton.style.display = 'none'; higherButton.style.display = 'none'; } // (Case where player card is revealed is handled earlier) } // --- Function to apply styles and setup observer --- function initializeGameLogic() { console.log("Torn High-Low Helper (Advanced): Core game elements found. Initializing..."); const startButton = document.querySelector('.action-btn-wrap.startGame'); // --- Apply one-time styles (Start button position) --- if (startButton) { try { startButton.style.position = 'relative'; startButton.style.top = '257px'; // Adjust as needed startButton.style.left = '-50px'; // Adjust as needed console.log("Start button repositioned."); // --- Add listener to Start button for deck reset --- startButton.addEventListener('click', () => { console.log("Start Game button clicked - Resetting deck."); resetDeck(); // No need to call checkAndUpdateGame here, mutation observer will catch card changes }); console.log("Added click listener to Start button."); } catch (e) { console.error("Error styling or adding listener to Start button:", e); } } else { console.log('Start Game button not found on initial load (likely game in progress or already finished).'); // Attempt to determine initial state if possible (might be unreliable) const dealerCardElement = document.querySelector('.dealer-card span.rating'); if(dealerCardElement && dealerCardElement.textContent.trim() !== '') { console.log("Game seems to be in progress. Initial deck state might be inaccurate until next game."); isGameActive = true; // Assume active, but deck is unknown // We won't have past cards, so prediction will be off until reset. } else { isGameActive = false; } } // Initial deck reset if we are definitely on the start screen if (startButton && startButton.offsetParent !== null) { resetDeck(); // Reset deck state isGameActive = false; // Not active until *after* start is clicked } // --- Setup MutationObserver --- // Observe a parent container that includes cards and buttons for state changes const gameContainer = document.querySelector('.highlow-main-wrap'); // Or a more specific wrapper if available if (!gameContainer) { console.error("Cannot find main game container for MutationObserver!"); return; } const observer = new MutationObserver(mutationsList => { // Use a debounce/throttle mechanism if performance becomes an issue // For now, just call the update function on any observed change try { // Check if the start button appeared (indicates game ended) const currentStartButton = document.querySelector('.action-btn-wrap.startGame'); if (currentStartButton && currentStartButton.offsetParent !== null && isGameActive) { console.log("Mutation detected Start Button visibility - flagging game end."); isGameActive = false; // Game ended // No need to reset deck here, start button click handler does that. } // Check if result screen appeared const currentResultWrap = document.querySelector('.game-result-wrap'); if (currentResultWrap && currentResultWrap.offsetParent !== null && isGameActive) { console.log("Mutation detected Result Wrap visibility - flagging game end."); isGameActive = false; // Game ended } // Re-run the check on any relevant mutation inside the container checkAndUpdateGame(); } catch(e) { console.error("Error during MutationObserver callback:", e); } }); // Configuration: watch for changes in children (cards appearing/changing) // and subtree (text changes within spans) const config = { childList: true, subtree: true, characterData: true // Needed for card rank text changes }; try { observer.observe(gameContainer, config); console.log("MutationObserver started, watching game container."); } catch(e) { console.error("Error starting MutationObserver:", e); } // --- Initial Check --- // Run once after setup to set initial state based on current DOM // Reset deck if start button is visible initially if (startButton && startButton.offsetParent !== null) { resetDeck(); isGameActive = false; } else { // If not on start screen, try to determine if game is active const dealerCardElement = document.querySelector('.dealer-card span.rating'); const playerCardElement = document.querySelector('.you-card span.rating'); if(dealerCardElement && dealerCardElement.textContent.trim() !== '') { console.log("Initial check: Game seems in progress."); // We *don't* know the deck state accurately here if loaded mid-game. // Initialize an empty deck or full deck? Let's assume full deck but mark as potentially inaccurate. resetDeck(); // Reset to full deck console.warn("Deck reset, but history is unknown as script loaded mid-game."); // Manually remove current dealer/player cards if visible? const initialDealerVal = getCardValue(dealerCardElement.textContent); const initialPlayerVal = getCardValue(playerCardElement?.textContent); if (initialDealerVal) removeCardFromDeck(initialDealerVal); if (initialPlayerVal) removeCardFromDeck(initialPlayerVal); isGameActive = true; checkAndUpdateGame(); // Run prediction logic } else { console.log("Initial check: Game not started or finished."); isGameActive = false; resetDeck(); // Ensure deck is ready for when start is clicked checkAndUpdateGame(); // Hide buttons etc. } } } // --- Wait for core elements to exist before initializing --- function waitForElements() { // Check for essential elements needed for the script's core logic if (document.querySelector('.highlow-main-wrap') && // Main container is crucial document.querySelector('.dealer-card') && document.querySelector('.you-card') && document.querySelector('.actions-wrap .action-btn-wrap.low') && document.querySelector('.actions-wrap .action-btn-wrap.high') ) { clearInterval(checkTimer); // Stop checking initializeGameLogic(); // Run the main setup } else { waitTime += CHECK_INTERVAL; if (waitTime >= MAX_WAIT_TIME) { clearInterval(checkTimer); console.error("Torn High-Low Helper (Advanced): Timed out waiting for game elements to load."); } else { console.log("Waiting for game elements..."); } } } // Start the check checkTimer = setInterval(waitForElements, CHECK_INTERVAL); })(); // End of IIFE wrapper