Chess.com Stockfish Bot

Simplifies move logic to reliably play the first move as White without user interaction.

  1. // ==UserScript==
  2. // @name Chess.com Stockfish Bot
  3. // @namespace BottleOrg Scripts
  4. // @version 1.7.5
  5. // @description Simplifies move logic to reliably play the first move as White without user interaction.
  6. // @author [REDACTED] - Rightful Owner(qbaonguyen050@gmail.com, Gemini 2.5 Pro, Kimi K2
  7. // @license Chess.com Bot/Cheat by BottleOrg(qbaonguyen050@gmail.com)
  8. // @match https://www.chess.com/play/*
  9. // @match https://www.chess.com/game/*
  10. // @match https://www.chess.com/puzzles/*
  11. // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
  12. // @grant GM_getValue
  13. // @grant GM_setValue
  14. // @grant GM_xmlhttpRequest
  15. // @grant GM_getResourceText
  16. // @grant GM_registerMenuCommand
  17. // @require https://gf.qytechs.cn/scripts/445697/code/index.js
  18. // @require https://code.jquery.com/jquery-3.6.0.min.js
  19. // @run-at document-start
  20. // ==/UserScript==
  21.  
  22. const currentVersion = '1.7.5';
  23. const scriptURL = 'https://gf.qytechs.cn/en/scripts/526240-chess-com-stockfish-bot';
  24.  
  25. function checkForUpdate() {
  26. console.log("Checking for script updates...");
  27. GM_xmlhttpRequest({
  28. method: "GET",
  29. url: scriptURL,
  30. onload: function(response) {
  31. if (response.status === 200) {
  32. const html = response.responseText;
  33. const versionMatch = html.match(/@version\s+([\d.]+)/);
  34. if (versionMatch && versionMatch[1]) {
  35. const latestVersion = versionMatch[1];
  36. console.log("Latest version found:", latestVersion);
  37. if (compareVersions(latestVersion, currentVersion) > 0) {
  38. const message = `New Version: ${latestVersion} has been uploaded. Would you like me to take you there or continue with old version ${currentVersion}? (Not recommended for stability)`;
  39. if (confirm(message)) {
  40. window.location.href = scriptURL;
  41. } else {
  42. console.log("User chose to continue with old version.");
  43. }
  44. } else {
  45. console.log("No newer version available.");
  46. }
  47. }
  48. }
  49. },
  50. });
  51. }
  52.  
  53. function compareVersions(v1, v2) {
  54. const parts1 = v1.split('.').map(Number);
  55. const parts2 = v2.split('.').map(Number);
  56. for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
  57. const p1 = parts1[i] || 0;
  58. const p2 = parts2[i] || 0;
  59. if (p1 > p2) return 1;
  60. if (p1 < p2) return -1;
  61. }
  62. return 0;
  63. }
  64.  
  65. function main() {
  66. let myVars = document.myVars = {
  67. autoMove: false,
  68. autoRun: false,
  69. autoMatch: false,
  70. delay: 0.1,
  71. gameEnded: false
  72. };
  73. let myFunctions = document.myFunctions = {};
  74. const stockfishAPI_URI = "https://stockfish.online/api/s/v2.php";
  75. let board;
  76.  
  77. myFunctions.rescan = function() {
  78. if (board && board.game && typeof board.game.getFEN === 'function') {
  79. const fen = board.game.getFEN();
  80. console.log("Using built-in FEN:", fen);
  81. return fen;
  82. }
  83. console.warn("Could not get FEN from board.game object.");
  84. return null;
  85. };
  86.  
  87. myFunctions.color = function(dat) {
  88. const bestmoveUCI = dat.split(' ')[1];
  89. console.log("Extracted bestmove UCI:", bestmoveUCI);
  90. if (myVars.autoMove) myFunctions.movePiece(bestmoveUCI);
  91. else myFunctions.highlightMove(bestmoveUCI);
  92. isThinking = false;
  93. myFunctions.spinner();
  94. };
  95.  
  96. myFunctions.highlightMove = function(bestmoveUCI) {
  97. const res1 = bestmoveUCI.substring(0, 2);
  98. const res2 = bestmoveUCI.substring(2, 4);
  99. $(board).prepend(`<div class="highlight square-${res2}" style="background-color: rgb(235, 97, 80); opacity: 0.71;"></div>`).delay(1800).queue(function() { $(this).remove(); });
  100. $(board).prepend(`<div class="highlight square-${res1}" style="background-color: rgb(235, 97, 80); opacity: 0.71;"></div>`).delay(1800).queue(function() { $(this).remove(); });
  101. };
  102.  
  103. myFunctions.movePiece = function(bestmoveUCI) {
  104. if (!board || !board.game) { console.error("Board or board.game not initialized!"); return; }
  105. const fromSquare = bestmoveUCI.substring(0, 2);
  106. const toSquare = bestmoveUCI.substring(2, 4);
  107. const promotionPiece = bestmoveUCI.length > 4 ? bestmoveUCI.substring(4, 5) : null;
  108. const legalMoves = board.game.getLegalMoves();
  109. const foundMove = legalMoves.find(move =>
  110. move.from === fromSquare && move.to === toSquare && (promotionPiece ? move.promotion === promotionPiece : !move.promotion)
  111. );
  112.  
  113. if (foundMove) {
  114. console.log("Executing move:", bestmoveUCI);
  115. board.game.move({ ...foundMove, animate: true, userGenerated: true });
  116. } else {
  117. console.warn(`Could not find a legal move for UCI: ${bestmoveUCI}.`);
  118. }
  119. };
  120.  
  121. myFunctions.fetchBestMoveFromAPI = function(fen, depth) {
  122. const apiURL = `${stockfishAPI_URI}?fen=${encodeURIComponent(fen)}&depth=${depth}`;
  123. console.log(`Fetching from: ${apiURL}`);
  124. GM_xmlhttpRequest({
  125. method: "GET", url: apiURL,
  126. onload: function(response) {
  127. if (response.status === 200) {
  128. try { const jsonResponse = JSON.parse(response.responseText);
  129. if (jsonResponse.success) myFunctions.color(jsonResponse.bestmove);
  130. else { console.error("API failed:", jsonResponse); isThinking = false; myFunctions.spinner(); }
  131. } catch (e) { console.error("API parse error:", e); isThinking = false; myFunctions.spinner(); }
  132. } else { console.error("API error:", response.status); isThinking = false; myFunctions.spinner(); }
  133. },
  134. onerror: function() { console.error("API request error"); isThinking = false; myFunctions.spinner(); }
  135. });
  136. };
  137.  
  138. myFunctions.startNewGame = function() {
  139. console.log("Starting new game...");
  140. const newGameButton = $('.game-over-modal-content .game-over-buttons-component .cc-button-component:not([aria-label="Rematch"])') ||
  141. $('.game-over-buttons-component .cc-button-component:not([aria-label="Rematch"])');
  142. if (newGameButton.length) {
  143. newGameButton[0].click();
  144. } else {
  145. console.error("Could not find a 'New Game' button.");
  146. }
  147. };
  148.  
  149. myFunctions.declineRematch = function() {
  150. const declineButton = $('.cc-button-component.cc-button-secondary[aria-label="Decline"], .cc-button-component.cc-button-secondary:contains("Decline")');
  151. if (declineButton.length) {
  152. declineButton[0].click();
  153. console.log("Declined rematch.");
  154. }
  155. };
  156.  
  157. let lastValue = 12, MAX_DEPTH = 15, MIN_DEPTH = 1;
  158.  
  159. myFunctions.runChessEngine = function(depth) {
  160. depth = Math.max(MIN_DEPTH, Math.min(MAX_DEPTH, depth));
  161. const fen = myFunctions.rescan();
  162. if (!fen) { console.log("Skipping analysis, FEN not available."); return; }
  163. console.log(`Analyzing FEN: ${fen}, Depth: ${depth}`);
  164. isThinking = true;
  165. myFunctions.spinner();
  166. myFunctions.fetchBestMoveFromAPI(fen, depth);
  167. lastValue = depth;
  168. updateDepthDisplay();
  169. };
  170.  
  171. function updateDepthDisplay() {
  172. if (uiElementsLoaded && $('#depthText')[0]) {
  173. $('#depthText')[0].innerHTML = `Depth: <strong>${lastValue}</strong>`;
  174. }
  175. }
  176.  
  177. myFunctions.incrementDepth = function(delta) {
  178. lastValue = Math.max(MIN_DEPTH, Math.min(MAX_DEPTH, lastValue + delta));
  179. updateDepthDisplay();
  180. };
  181.  
  182. myFunctions.autoRun = function() {
  183. if (board && board.game && board.game.getTurn() === board.game.getPlayingAs()) {
  184. myFunctions.runChessEngine(lastValue);
  185. }
  186. };
  187.  
  188. document.onkeydown = function(e) {
  189. if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
  190. switch (e.keyCode) {
  191. case 81: myFunctions.runChessEngine(1); break; case 87: myFunctions.runChessEngine(2); break;
  192. case 69: myFunctions.runChessEngine(3); break; case 82: myFunctions.runChessEngine(4); break;
  193. case 84: myFunctions.runChessEngine(5); break; case 89: myFunctions.runChessEngine(6); break;
  194. case 85: myFunctions.runChessEngine(7); break; case 73: myFunctions.runChessEngine(8); break;
  195. case 79: myFunctions.runChessEngine(9); break; case 80: myFunctions.runChessEngine(10); break;
  196. case 65: myFunctions.runChessEngine(11); break; case 83: myFunctions.runChessEngine(12); break;
  197. case 68: myFunctions.runChessEngine(13); break; case 70: myFunctions.runChessEngine(14); break;
  198. case 71: myFunctions.runChessEngine(15); break; case 187: myFunctions.incrementDepth(1); break;
  199. case 189: myFunctions.incrementDepth(-1); break;
  200. }
  201. };
  202.  
  203. myFunctions.spinner = function() {
  204. if (uiElementsLoaded && $('#overlay')[0]) {
  205. $('#overlay')[0].style.display = isThinking ? 'block' : 'none';
  206. }
  207. };
  208.  
  209. let uiElementsLoaded = false;
  210. let loaded = false;
  211. myFunctions.loadEx = function() {
  212. try {
  213. console.log("Attempting to load UI...");
  214. board = document.querySelector('chess-board, wc-chess-board');
  215. const style = document.createElement('style');
  216. style.innerHTML = `
  217. .chess-ui-panel { width: 280px; background: linear-gradient(135deg, #ffe6e6, #fff5f5); border: 2px solid #ffcccc; border-radius: 12px; box-shadow: 4px 4px 16px rgba(0,0,0,0.3); animation: fadeIn 1s ease-out; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; user-select: none; }
  218. .chess-ui-header { background: #ffcccc; border-bottom: 1px solid #ffb3b3; border-top-left-radius: 10px; border-top-right-radius: 10px; padding: 8px; cursor: move; font-weight: bold; color: #800000; text-align: center; }
  219. .chess-ui-body { padding: 10px; }
  220. .chess-ui-panel button { background: #e91e63; border: none; color: white; padding: 8px 12px; margin: 4px 2px; border-radius: 5px; cursor: pointer; transition: box-shadow 0.3s ease, transform 0.2s ease; }
  221. .chess-ui-panel button:hover { box-shadow: 0 0 8px #ff69b4; transform: scale(1.05); }
  222. .chess-ui-panel input[type="checkbox"], .chess-ui-panel input[type="number"] { margin: 5px 0; padding: 4px; border: 1px solid #ffcccc; border-radius: 4px; }
  223. @keyframes fadeIn { from { opacity: 0; transform: translateY(-10px); } to { opacity: 1; transform: translateY(0); } }
  224. body.is-dragging { user-select: none; }
  225. `;
  226. document.head.appendChild(style);
  227.  
  228. const panel = document.createElement('div');
  229. panel.className = "chess-ui-panel";
  230. panel.style.cssText = 'position: fixed; top: 10px; right: 10px; z-index: 10000;';
  231. panel.innerHTML = `
  232. <div class="chess-ui-header">Stockfish Bot (v${currentVersion})</div>
  233. <div class="chess-ui-body">
  234. <p id="depthText">Depth: <strong>${lastValue}</strong></p>
  235. <button id="depthMinus">-</button> <button id="depthPlus">+</button>
  236. <p style="font-size: 12px;">Keys: Q-G (1-15), +/-</p>
  237. <p id="engineVersionText">Engine: <strong>Stockfish API</strong> 😘</p>
  238. <label><input type="checkbox" id="autoRun"> Auto Run</label><br>
  239. <label><input type="checkbox" id="autoMove"> Auto Move</label><br>
  240. <label><input type="checkbox" id="autoMatch"> Auto-Match</label><br>
  241. <label>Min Delay (s): <input type="number" id="timeDelayMin" min="0.1" value="0.1" step="0.1" style="width: 60px;"></label><br>
  242. <label>Max Delay (s): <input type="number" id="timeDelayMax" min="0.1" value="0.3" step="0.1" style="width: 60px;"></label><br>
  243. </div>`;
  244. document.body.appendChild(panel);
  245.  
  246. const header = panel.querySelector('.chess-ui-header');
  247. makeDraggable(panel, header);
  248.  
  249. setTimeout(() => {
  250. $('#depthPlus').on('click', () => myFunctions.incrementDepth(1));
  251. $('#depthMinus').on('click', () => myFunctions.incrementDepth(-1));
  252. $('#autoMatch').on('change', () => { myVars.autoMatch = $('#autoMatch')[0].checked; });
  253. }, 100);
  254.  
  255. const spinCont = document.createElement('div');
  256. spinCont.id = 'overlay';
  257. spinCont.style.cssText = 'display: none; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);';
  258. panel.appendChild(spinCont);
  259. const spinr = document.createElement('div');
  260. spinr.style.cssText = "height: 64px; width: 64px; animation: rotate 0.8s infinite linear; border: 5px solid firebrick; border-right-color: transparent; border-radius: 50%;";
  261. spinCont.appendChild(spinr);
  262. const dynamicStyles = document.createElement('style');
  263. document.head.appendChild(dynamicStyles);
  264. dynamicStyles.sheet.insertRule('@keyframes rotate { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }', 0);
  265.  
  266. loaded = true;
  267. uiElementsLoaded = true;
  268. checkForUpdate();
  269. } catch (error) { console.error("loadEx error:", error); }
  270. };
  271.  
  272. function makeDraggable(panel, header) { let oX, oY; header.addEventListener('mousedown', e => { e.preventDefault(); const r = panel.getBoundingClientRect(); oX = e.clientX - r.left; oY = e.clientY - r.top; document.body.classList.add('is-dragging'); document.addEventListener('mousemove', elementDrag); document.addEventListener('mouseup', closeDragElement); }); function elementDrag(e) { e.preventDefault(); panel.style.left = (e.clientX - oX) + "px"; panel.style.top = (e.clientY - oY) + "px"; } function closeDragElement() { document.body.classList.remove('is-dragging'); document.removeEventListener('mousemove', elementDrag); document.removeEventListener('mouseup', closeDragElement); } }
  273.  
  274. function runWithDelay(delay) {
  275. const endTime = Date.now() + delay;
  276. const timer = setInterval(() => {
  277. if (Date.now() >= endTime) {
  278. myFunctions.autoRun();
  279. canGo = true;
  280. clearInterval(timer);
  281. }
  282. }, 10);
  283. }
  284.  
  285. const mainLoop = setInterval(() => {
  286. if (!loaded) {
  287. myFunctions.loadEx();
  288. } else {
  289. if (!board) board = document.querySelector('chess-board, wc-chess-board');
  290. if (!board || !board.game) return;
  291.  
  292. myVars.autoRun = $('#autoRun')[0].checked;
  293. myVars.autoMove = $('#autoMove')[0].checked;
  294. myVars.autoMatch = $('#autoMatch')[0].checked;
  295. const minDel = parseFloat($('#timeDelayMin')[0].value) || 0.1;
  296. const maxDel = parseFloat($('#timeDelayMax')[0].value) || 0.3;
  297. myVars.delay = Math.random() * (maxDel - minDel) + minDel;
  298. myTurn = board.game.getTurn() === board.game.getPlayingAs();
  299. updateDepthDisplay();
  300.  
  301. const gameOver = document.querySelector('.game-over-message-component, .game-result');
  302.  
  303. if (gameOver && !myVars.gameEnded) {
  304. console.log("Game ended detected.");
  305. myVars.gameEnded = true;
  306. if (myVars.autoMatch) {
  307. myFunctions.declineRematch();
  308. setTimeout(myFunctions.startNewGame, 1000);
  309. }
  310. } else if (!gameOver && myVars.gameEnded) {
  311. myVars.gameEnded = false;
  312. }
  313.  
  314. // This is the simplified, correct logic.
  315. // It will trigger on any turn, including the first move (where getMoves().length is 0).
  316. if (myVars.autoRun && canGo && !isThinking && myTurn) {
  317. canGo = false;
  318. runWithDelay(myVars.delay * 1000);
  319. }
  320. }
  321. }, 200);
  322.  
  323. setTimeout(() => { if (!loaded) myFunctions.loadEx(); }, 2000);
  324. }
  325.  
  326. let isThinking = false, canGo = true, myTurn = false;
  327. window.addEventListener("load", () => main());

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址