在您安裝前,Greasy Fork希望您了解本腳本包含“負面功能”,可能幫助腳本的作者獲利,而不能給你帶來任何收益。
此腳本只有在您 註冊後才能使用全部的功能, 例如加入群組, 訂閱頻道, 或是點讚頁面。
GabiBot is a ModMenu (Key Removed) with Evaluation Bar and PV Display.
// ==UserScript== // @name ♟-GabiBot-: Chess Bot And ModMenu!♟ (No Key) - SYNTAX ROBUST V3 (Expanded) // @namespace http://tampermonkey.net/ // @version 1.1 // @description GabiBot is a ModMenu (Key Removed) with Evaluation Bar and PV Display. // @author thehackerclient (Modified & Expanded) // @match https://www.chess.com/play/computer // @license MIT // @icon https://www.google.com/s2/favicons?sz=64&domain=chess.com // @grant none // @antifeature membership // ==/UserScript== (async function() { // INCREASED DELAY to 5000ms (5 seconds) setTimeout(async function(){ alert("Stockfish Loaded!") console.log("GabiBot: Starting GUI creation sequence."); async function startStockfish(key){ var menuWrap = document.createElement("div") menuWrap.id="menuWrap" var menuWrapStyle = document.createElement("style") // *** UPDATED: Added PV Display item *** menuWrap.innerHTML = [ '<div id="topText">', ' <a id="modTitle">-GabiBot-</a>', ' <a>Ctrl+B To Hide</a>', '</div>', '<div id="itemsList">', ' <div name="enableHack" class="listItem">', ' <input class="checkboxMod" type="checkbox">', ' <a class="itemDescription">Enable Hack: </a>', ' <a class="itemState">Off</a>', ' </div>', ' <div name="autoMove" class="listItem">', ' <input class="checkboxMod" type="checkbox">', ' <a class="itemDescription">Auto Move:</a>', ' <a class="itemState">Off</a>', ' </div>', ' <div name="botPower" class="listItem">', ' <input min="1" max="15" value="12" class="rangeSlider" type="range">', ' <a class="itemDescription">Bot Power:</a>', ' <a class="itemState">12</a>', ' </div>', ' <div name="autoMoveSpeed" class="listItem">', ' <input min="1" max="10" value="4" class="rangeSlider" type="range">', ' <a class="itemDescription">Auto Move Speed:</a>', ' <a class="itemState">3</a>', ' </div>', ' <div name="updateSpeed" class="listItem">', ' <input min="1" max="10" value="8" class="rangeSlider" type="range">', ' <a class="itemDescription">Update Speed:</a>', ' <a class="itemState">8</a>', ' </div>', ' <div name="currentEvaluation" class="listItem">', ' <a class="itemDescription">Current Evaluation:</a>', ' <a class="itemState">-</a>', ' </div>', ' <div name="bestMove" class="listItem">', ' <a class="itemDescription">Best Move:</a>', ' <a class="itemState">-</a>', ' </div>', ' <div name="pvDisplay" class="listItem">', ' <a class="itemDescription">PV (Prediction):</a>', ' <a class="itemState pv-text-state" title="Principal Variation">-</a>', // Use a custom class for styling PV ' </div>', ' <div name="information" class="listItem">', ' <a class="itemDescription">Information: </a>', ' <a class="itemState">GabiHarMotion (1242 Elo)</a>', ' </div>', '</div>' ].join(''); // *** UPDATED: Added CSS for Evaluation Bar and PV Display *** menuWrapStyle.innerHTML = [ '#menuWrap {', ' font-family: monospace;', ' border-radius: 1vh;', ' z-index: 1000000;', ' grid: none;', ' display: grid;', ' grid-template-columns: 90%;', ' grid-template-rows: 15% 85%;', ' justify-content: center;', ' width: 350px;', ' height: 400px;', ' position: absolute;', ' border: 1px solid rgb(100 100 100);', ' background: rgb(16, 16, 16);', ' opacity: 0.98;', ' user-select: none;', ' max-width: 60vw;', ' top: 100px;', ' left: 100px;', '}', // New CSS for Principal Variation text to allow long strings '.pv-text-state {', ' color: white;', ' margin-left: 3%;', ' max-width: 50%;', ' overflow-x: auto;', ' white-space: nowrap;', '}', // New CSS for the Evaluation Bar next to the board '#evaluationBarWrap {', ' position: absolute;', ' height: 100%;', ' width: 20px;', ' background-color: #333;', ' z-index: 999999;', ' right: -25px;', ' top: 0;', ' border-radius: 5px;', ' overflow: hidden;', '}', '#evaluationBar {', ' width: 100%;', ' position: absolute;', ' bottom: 50%;', ' transition: height 0.5s ease, bottom 0.5s ease;', '}', '#evaluationBar.white-advantage {', ' background-color: #f7d247;', /* Gold for White */ '}', '#evaluationBar.black-advantage {', ' background-color: #3d8bff;', /* Blue for Black */ '}', '#topText {', ' width: 40%;', ' justify-self: center;', ' text-align: center;', '}', '#modTitle {', ' color: white;', ' justify-self: center;', ' margin-bottom: 5%;', ' font-size: 17px;', '}', '#itemsList{', ' overflow-x: hidden;', '}', '::-webkit-scrollbar {', ' width: 8px;', '}', '::-webkit-scrollbar-thumb {', ' background: #888;', ' height: 10px;', '}', '.listItem {', ' display: flex;', ' align-items: center;', ' margin-bottom: 6%;', '}', '.checkboxMod {', ' outline: #acacac 1px solid;', ' vertical-align: middle;', ' appearance: none;', ' border-radius: 30%;', ' height: 20px;', ' width: 20px;', ' top: 30%;', ' background: #303030;', ' margin-right: 5%;', '}', '.checkboxMod:checked {', ' background-color: #808080;', '}', '.rangeSlider {', ' -webkit-appearance: none;', ' width: 45%;', ' height: 15px;', ' border-radius: 5px;', ' background: #b0b0b0;', ' outline: none;', ' margin-right: 6%;', '}', '.rangeSlider::-webkit-slider-thumb {', ' appearance: none;', ' width: 17px;', ' height: 17px;', ' border-radius: 50%;', ' background: #505050;', ' cursor: pointer;', '}', '.itemDescription{', ' color: white;', '}', '.itemState{', ' color: white;', ' margin-left: 3%;', '}' ].join(''); document.body.appendChild(menuWrap) document.body.appendChild(menuWrapStyle) // *** UPDATED: Added new window variable for PV *** window.hackEnabled = 0 window.botPower = 12 window.updateSpeed = 8 window.autoMove = 0 window.autoMoveSpeed = 4 window.currentEvaluation = 0 window.bestMove = "" window.principalVariation = "" // New variable var itemWrap = document.getElementById("menuWrap") function getElementByName(name,selector){return selector.querySelector(`[name="${name}"]`)} function getInputElement(element){return element.children[0]} function getStateElement(element){return element.children[element.children.length-1]} function modFunction(name,type,variable){ var modElement = getElementByName(name,itemWrap) var modState = getStateElement(modElement) var modInput = getInputElement(modElement) if(type=="text"){ // PV is often a long string, so we use the 'title' attribute for hover text if(name === "pvDisplay") modState.title = eval(variable); modState.innerHTML=eval(variable) } modInput.onmouseup=e=>{ if(e.button==0){ if(type=="checkbox"){ modState.innerHTML=["Off","On"][Number(!modInput.checked)] // Robust eval syntax eval(variable + "=" + !modInput.checked) } if(type=="range"){ modState.innerHTML=modInput.value // Robust eval syntax eval(variable + "=" + modInput.value) } } } } modFunction("enableHack","checkbox","window.hackEnabled") modFunction("autoMove","checkbox","window.autoMove") modFunction("botPower","range","window.botPower") modFunction("autoMoveSpeed","range","window.autoMoveSpeed") modFunction("updateSpeed","range","window.updateSpeed") function updateTexts(){ modFunction("currentEvaluation","text","window.currentEvaluation"); modFunction("bestMove","text","window.bestMove"); modFunction("pvDisplay","text","window.principalVariation"); // New line for PV // Robust eval syntax for text modFunction("information","text","context.user.username + ' (' + context.user.rating + ' Elo)'"); } // *** NEW FUNCTION: Update Evaluation Bar *** function updateEvaluationBar(evaluation, playingAs) { const bar = document.getElementById("evaluationBar"); if (!bar) return; let score = 0; // Convert mate scores (M-1, M+3) to high numerical values if (typeof evaluation === 'string' && evaluation.includes('M')) { let mateScore = parseInt(evaluation.replace('M', '')); score = Math.sign(mateScore) * 1000; } else { score = parseFloat(evaluation); } const maxScore = 8; let normalizedScore = Math.max(-maxScore, Math.min(maxScore, score)); // Scale for visual effect (0-100% height) let height = Math.abs(normalizedScore) * (50 / maxScore); // If the board is flipped (playing as Black) invert the visual logic if (playingAs === 2) { normalizedScore *= -1; } bar.style.height = `${height}%`; // Position: from 50% upwards for White advantage, downwards for Black advantage bar.style.bottom = normalizedScore > 0 ? '50%' : `${50 - height}%`; // Set class for color bar.classList.remove('white-advantage', 'black-advantage'); if (normalizedScore > 0.5) { bar.classList.add('white-advantage'); } else if (normalizedScore < -0.5) { bar.classList.add('black-advantage'); } } var board = document.querySelector('.board'); var drawingBoard = document.createElement("canvas"); var drawingBoardCTX = drawingBoard.getContext("2d"); // *** NEW HTML FOR EVAL BAR: Check for board and add bar structure *** if (!board) { console.error("GabiBot Error: Could not find the chess board element (selector: .board). The script may be outdated."); return; } // Add Evaluation Bar Structure var evalBarWrap = document.createElement("div"); evalBarWrap.id = "evaluationBarWrap"; var evalBar = document.createElement("div"); evalBar.id = "evaluationBar"; evalBarWrap.appendChild(evalBar); board.appendChild(evalBarWrap); // End Eval Bar HTML // Set canvas size to match the board drawingBoard.width=board.clientWidth; drawingBoard.height=board.clientHeight; board.appendChild(drawingBoard); function clear(){ drawingBoardCTX.clearRect(0,0,board.clientWidth,board.clientHeight) } async function executeAction(bestmove){ // Flip the drawing board if playing as Black if(board.game.getPlayingAs()==2){ drawingBoard.style.rotate="180deg" }else{ drawingBoard.style.rotate="0deg" } clear() console.log(bestmove) bestmove = bestmove.split(" ")[1] // Format: 'bestmove e2e4' -> 'e2e4' var tileSize = (drawingBoard.clientWidth/8) var letters = ["a","b","c","d","e","f","g","h"]; // Calculate coordinates for drawing the move arrow var x1 = letters.indexOf(bestmove[0])+1; var y1 = 9-Number(bestmove[1]); var x2 = letters.indexOf(bestmove[2])+1; var y2 = 9-Number(bestmove[3]); // Draw the arrow drawingBoardCTX.beginPath(); drawingBoardCTX.moveTo(x1*tileSize-(tileSize/2),y1*tileSize-(tileSize/2)); drawingBoardCTX.lineTo(x2*tileSize-(tileSize/2),y2*tileSize-(tileSize/2)); drawingBoardCTX.lineWidth=tileSize/5; drawingBoardCTX.strokeStyle="#00ff0050"; // Green with transparency drawingBoardCTX.stroke(); if(window.autoMove){ setTimeout(function(){ // Find the move object and execute it var legalMoves = game.getLegalMoves() for(var i=0;i<legalMoves.length;i++){ if(legalMoves[i].from==bestmove.split("")[0]+bestmove.split("")[1]){ if(legalMoves[i].to==bestmove.split("")[2]+bestmove.split("")[3]){ var move = legalMoves[i] // 'game' object is expected to be available in the global scope of chess.com game.move({ ...move, promotion: 'false', animate: false, userGenerated: true }); } } } },5000-window.autoMoveSpeed*500) // Speed control: faster if autoMoveSpeed is higher } } var updateBotRunning = false; async function updateBot() { // Throttle the update rate based on user setting (updateSpeed) var updateBotInterval = setTimeout(async function(){ updateTexts(); var board = document.querySelector('.board'); if(!window.hackEnabled) { clear(); updateEvaluationBar(0, 1); // Reset bar updateBotRunning = false; clearInterval(updateBotInterval) return; } // Check for board availability again during updates if (!board || !board.game) { console.error("GabiBot Warning: Board or game object is unavailable during update. Retrying..."); updateBot(); // Recursively retry after a small delay return; } // *** NEW FEATURE: Game State Awareness *** if (board.game.isGameOver()) { var result = board.game.getGameOverReason(); window.currentEvaluation = `GAME OVER: ${result.toUpperCase()}`; window.bestMove = "No moves possible."; window.principalVariation = "Game has ended."; clear(); // Clear the arrow updateEvaluationBar(0, board.game.getPlayingAs()); // Reset bar updateTexts(); // Update the menu // Stop analysis loop until a new game starts updateBotRunning = false; return; } // *** END Game State Awareness *** updateBotRunning = true; // Get FEN from the chess.com game object var FEN = board.game.getFEN(); var depth = window.botPower; // Call the Stockfish API // Note: The 'pv' (Principal Variation) should be returned by this API let response = await fetch(`https://stockfish.online/api/s/v2.php?fen=${encodeURIComponent(FEN)}&depth=${depth}`); let data = await response.json(); window.bestMove = data.bestmove || "Calculating..."; window.currentEvaluation = data.evaluation || "-"; // *** NEW FEATURE: Principal Variation (PV) Display *** window.principalVariation = data.pv || "PV not available"; var bestmove = data.bestmove; // *** UPDATED: Call Evaluation Bar update *** var playingAs = board.game.getPlayingAs(); // 1 for White, 2 for Black updateEvaluationBar(window.currentEvaluation, playingAs); if (bestmove && bestmove.includes("bestmove")) { executeAction(bestmove); } // Use requestAnimationFrame for smooth GUI updates (if any) and recursive call requestAnimationFrame(()=>{ if (updateBotRunning) updateBot(); }); },1100-(window.updateSpeed*100)) } // Re-run the bot logic whenever a change event happens (like a user move) document.addEventListener("change", updateBot); // Start the bot immediately without a key check updateBot(); var draggingElement = document.getElementById("modTitle") dragElement(itemWrap,draggingElement); // Function to allow the menu window to be dragged function dragElement(elmnt,elmnt2) { var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; elmnt2.onmousedown = dragMouseDown; function dragMouseDown(e) { e = e || window.event; e.preventDefault(); pos3 = e.clientX; pos4 = e.clientY; document.onmouseup = closeDragElement; document.onmousemove = elementDrag; } function elementDrag(e) { e = e || window.event; e.preventDefault(); pos1 = pos3 - e.clientX; pos2 = pos4 - e.clientY; pos3 = e.clientX; pos4 = e.clientY; elmnt.style.top = (elmnt.offsetTop - pos2) + "px"; elmnt.style.left = (elmnt.offsetLeft - pos1) + "px"; } function closeDragElement() { document.onmouseup = null; document.onmousemove = null; } } // Keyboard shortcut for menu visibility var menuHidden = 0 document.addEventListener("keyup",(e)=>{ if(e.key=="b"&&e.ctrlKey){ if(!menuHidden){menuWrap.style.display="none"} else{menuWrap.style.display="grid"} menuHidden^=1 } }) } // Start the bot with a dummy key (key is ignored in this version) await startStockfish("key_removed") },5000) // Initial 5 second delay to wait for chess.com to load })();