Chess AI

Chess.com Bot/Cheat that finds the best move with evaluation bar and ELO control!

  1. // ==UserScript==
  2. // @name Chess AI
  3. // @namespace github.com/longkidkoolstar
  4. // @version 1.0.1
  5. // @description Chess.com Bot/Cheat that finds the best move with evaluation bar and ELO control!
  6. // @author longkidkoolstar
  7. // @license none
  8. // @match https://www.chess.com/play/*
  9. // @match https://www.chess.com/game/*
  10. // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
  11. // @grant GM.getValue
  12. // @grant GM.setValue
  13. // @grant GM.getResourceText
  14. // @resource stockfish.js https://raw.githubusercontent.com/longkidkoolstar/stockfish/refs/heads/main/stockfish.js
  15. // @require https://gf.qytechs.cn/scripts/445697/code/index.js
  16. // @require https://code.jquery.com/jquery-3.6.0.min.js
  17. // @run-at document-start
  18. // ==/UserScript==
  19.  
  20.  
  21. const currentVersion = '1.0.1'; // Updated version number
  22.  
  23. function main() {
  24.  
  25. var stockfishObjectURL;
  26. var engine = document.engine = {};
  27. var myVars = document.myVars = {};
  28. myVars.autoMovePiece = false;
  29. myVars.autoRun = false;
  30. myVars.delay = 0.1;
  31. myVars.eloRating = 1500; // Default ELO rating
  32. myVars.currentEvaluation = 0; // Current evaluation value
  33. myVars.persistentHighlights = true; // Default to persistent highlights
  34. myVars.moveIndicatorType = 'highlights'; // Default to highlights instead of arrows
  35. var myFunctions = document.myFunctions = {};
  36.  
  37. // Create evaluation bar
  38. var evalBar = null;
  39. var evalText = null;
  40.  
  41. stop_b = stop_w = 0;
  42. s_br = s_br2 = s_wr = s_wr2 = 0;
  43. obs = "";
  44. myFunctions.rescan = function(lev) {
  45. var ari = $("chess-board")
  46. .find(".piece")
  47. .map(function() {
  48. return this.className;
  49. })
  50. .get();
  51. jack = ari.map(f => f.substring(f.indexOf(' ') + 1));
  52. function removeWord(arr, word) {
  53. for (var i = 0; i < arr.length; i++) {
  54. arr[i] = arr[i].replace(word, '');
  55. }
  56. }
  57. removeWord(ari, 'square-');
  58. jack = ari.map(f => f.substring(f.indexOf(' ') + 1));
  59. for (var i = 0; i < jack.length; i++) {
  60. jack[i] = jack[i].replace('br', 'r')
  61. .replace('bn', 'n')
  62. .replace('bb', 'b')
  63. .replace('bq', 'q')
  64. .replace('bk', 'k')
  65. .replace('bb', 'b')
  66. .replace('bn', 'n')
  67. .replace('br', 'r')
  68. .replace('bp', 'p')
  69. .replace('wp', 'P')
  70. .replace('wr', 'R')
  71. .replace('wn', 'N')
  72. .replace('wb', 'B')
  73. .replace('br', 'R')
  74. .replace('wn', 'N')
  75. .replace('wb', 'B')
  76. .replace('wq', 'Q')
  77. .replace('wk', 'K')
  78. .replace('wb', 'B')
  79. }
  80. str2 = "";
  81. var count = 0,
  82. str = "";
  83. for (var j = 8; j > 0; j--) {
  84. for (var i = 1; i < 9; i++) {
  85. (str = (jack.find(el => el.includes([i] + [j])))) ? str = str.replace(/[^a-zA-Z]+/g, ''): str = "";
  86. if (str == "") {
  87. count++;
  88. str = count.toString();
  89. if (!isNaN(str2.charAt(str2.length - 1))) str2 = str2.slice(0, -1);
  90. else {
  91. count = 1;
  92. str = count.toString()
  93. }
  94. }
  95. str2 += str;
  96. if (i == 8) {
  97. count = 0;
  98. str2 += "/";
  99. }
  100. }
  101. }
  102. str2 = str2.slice(0, -1);
  103. //str2=str2+" KQkq - 0"
  104. color = "";
  105. wk = wq = bk = bq = "0";
  106. const move = $('vertical-move-list')
  107. .children();
  108. if (move.length < 2) {
  109. stop_b = stop_w = s_br = s_br2 = s_wr = s_wr2 = 0;
  110. }
  111. if (stop_b != 1) {
  112. if (move.find(".black.node:contains('K')")
  113. .length) {
  114. bk = "";
  115. bq = "";
  116. stop_b = 1;
  117. console.log('debug secb');
  118. }
  119. } else {
  120. bq = "";
  121. bk = "";
  122. }
  123. if (stop_b != 1)(bk = (move.find(".black.node:contains('O-O'):not(:contains('O-O-O'))")
  124. .length) ? "" : "k") ? (bq = (move.find(".black.node:contains('O-O-O')")
  125. .length) ? bk = "" : "q") : bq = "";
  126. if (s_br != 1) {
  127. if (move.find(".black.node:contains('R')")
  128. .text()
  129. .match('[abcd]+')) {
  130. bq = "";
  131. s_br = 1
  132. }
  133. } else bq = "";
  134. if (s_br2 != 1) {
  135. if (move.find(".black.node:contains('R')")
  136. .text()
  137. .match('[hgf]+')) {
  138. bk = "";
  139. s_br2 = 1
  140. }
  141. } else bk = "";
  142. if (stop_b == 0) {
  143. if (s_br == 0)
  144. if (move.find(".white.node:contains('xa8')")
  145. .length > 0) {
  146. bq = "";
  147. s_br = 1;
  148. console.log('debug b castle_r');
  149. }
  150. if (s_br2 == 0)
  151. if (move.find(".white.node:contains('xh8')")
  152. .length > 0) {
  153. bk = "";
  154. s_br2 = 1;
  155. console.log('debug b castle_l');
  156. }
  157. }
  158. if (stop_w != 1) {
  159. if (move.find(".white.node:contains('K')")
  160. .length) {
  161. wk = "";
  162. wq = "";
  163. stop_w = 1;
  164. console.log('debug secw');
  165. }
  166. } else {
  167. wq = "";
  168. wk = "";
  169. }
  170. if (stop_w != 1)(wk = (move.find(".white.node:contains('O-O'):not(:contains('O-O-O'))")
  171. .length) ? "" : "K") ? (wq = (move.find(".white.node:contains('O-O-O')")
  172. .length) ? wk = "" : "Q") : wq = "";
  173. if (s_wr != 1) {
  174. if (move.find(".white.node:contains('R')")
  175. .text()
  176. .match('[abcd]+')) {
  177. wq = "";
  178. s_wr = 1
  179. }
  180. } else wq = "";
  181. if (s_wr2 != 1) {
  182. if (move.find(".white.node:contains('R')")
  183. .text()
  184. .match('[hgf]+')) {
  185. wk = "";
  186. s_wr2 = 1
  187. }
  188. } else wk = "";
  189. if (stop_w == 0) {
  190. if (s_wr == 0)
  191. if (move.find(".black.node:contains('xa1')")
  192. .length > 0) {
  193. wq = "";
  194. s_wr = 1;
  195. console.log('debug w castle_l');
  196. }
  197. if (s_wr2 == 0)
  198. if (move.find(".black.node:contains('xh1')")
  199. .length > 0) {
  200. wk = "";
  201. s_wr2 = 1;
  202. console.log('debug w castle_r');
  203. }
  204. }
  205. if ($('.coordinates')
  206. .children()
  207. .first()
  208. .text() == 1) {
  209. str2 = str2 + " b " + wk + wq + bk + bq;
  210. color = "white";
  211. } else {
  212. str2 = str2 + " w " + wk + wq + bk + bq;
  213. color = "black";
  214. }
  215. //console.log(str2);
  216. return str2;
  217. }
  218. myFunctions.color = function(dat){
  219. response = dat;
  220. var res1 = response.substring(0, 2);
  221. var res2 = response.substring(2, 4);
  222. // Add the move to history
  223. const moveNotation = res1 + '-' + res2;
  224. myFunctions.addMoveToHistory(moveNotation, myVars.currentEvaluation, lastValue);
  225.  
  226. // Clear any existing highlights and arrows before adding new ones
  227. myFunctions.clearHighlights();
  228. myFunctions.clearArrows();
  229.  
  230. if(myVars.autoMove == true){
  231. myFunctions.movePiece(res1, res2);
  232. // After auto move, we need to reset canGo to allow auto run on next turn
  233. setTimeout(() => {
  234. canGo = true;
  235. }, 500);
  236. }
  237. isThinking = false;
  238.  
  239. // Only show move indicators if the option is enabled
  240. if(myVars.showArrows !== false) {
  241. // Convert algebraic notation to numeric coordinates
  242. let fromSquare = res1.replace(/^a/, "1")
  243. .replace(/^b/, "2")
  244. .replace(/^c/, "3")
  245. .replace(/^d/, "4")
  246. .replace(/^e/, "5")
  247. .replace(/^f/, "6")
  248. .replace(/^g/, "7")
  249. .replace(/^h/, "8");
  250. let toSquare = res2.replace(/^a/, "1")
  251. .replace(/^b/, "2")
  252. .replace(/^c/, "3")
  253. .replace(/^d/, "4")
  254. .replace(/^e/, "5")
  255. .replace(/^f/, "6")
  256. .replace(/^g/, "7")
  257. .replace(/^h/, "8");
  258. // Use arrows or highlights based on user preference
  259. if (myVars.moveIndicatorType === 'arrows') {
  260. // Draw an arrow from the source to the destination square
  261. myFunctions.drawArrow(res1, res2, myVars.persistentHighlights);
  262. } else {
  263. // Use the original highlighting method
  264. if (myVars.persistentHighlights) {
  265. // Add highlights with custom class for easier removal later
  266. $(board.nodeName)
  267. .prepend('<div class="highlight square-' + toSquare + ' bro persistent-highlight" style="background-color: rgb(235, 97, 80); opacity: 0.71;" data-test-element="highlight"></div>');
  268. $(board.nodeName)
  269. .prepend('<div class="highlight square-' + fromSquare + ' bro persistent-highlight" style="background-color: rgb(235, 97, 80); opacity: 0.71;" data-test-element="highlight"></div>');
  270. } else {
  271. // Use the original temporary highlights
  272. $(board.nodeName)
  273. .prepend('<div class="highlight square-' + toSquare + ' bro" style="background-color: rgb(235, 97, 80); opacity: 0.71;" data-test-element="highlight"></div>')
  274. .children(':first')
  275. .delay(1800)
  276. .queue(function() {
  277. $(this)
  278. .remove();
  279. });
  280. $(board.nodeName)
  281. .prepend('<div class="highlight square-' + fromSquare + ' bro" style="background-color: rgb(235, 97, 80); opacity: 0.71;" data-test-element="highlight"></div>')
  282. .children(':first')
  283. .delay(1800)
  284. .queue(function() {
  285. $(this)
  286. .remove();
  287. });
  288. }
  289. }
  290. }
  291. }
  292.  
  293. // Add a function to clear highlights
  294. myFunctions.clearHighlights = function() {
  295. // Remove all persistent highlights
  296. $('.persistent-highlight').remove();
  297. }
  298.  
  299. // Add a function to clear arrows
  300. myFunctions.clearArrows = function() {
  301. // Remove all arrows
  302. $('.chess-arrow-svg').remove();
  303. }
  304.  
  305. // Function to draw an arrow on the chess board
  306. myFunctions.drawArrow = function(from, to, isPersistent) {
  307. // Convert algebraic notation to coordinates
  308. const files = {'a': 0, 'b': 1, 'c': 2, 'd': 3, 'e': 4, 'f': 5, 'g': 6, 'h': 7};
  309. const ranks = {'1': 7, '2': 6, '3': 5, '4': 4, '5': 3, '6': 2, '7': 1, '8': 0};
  310. const fromFile = files[from[0]];
  311. const fromRank = ranks[from[1]];
  312. const toFile = files[to[0]];
  313. const toRank = ranks[to[1]];
  314. // Get the board element and its dimensions
  315. const boardElement = $(board.nodeName)[0];
  316. const boardRect = boardElement.getBoundingClientRect();
  317. const squareSize = boardRect.width / 8;
  318. // Calculate the center coordinates of the squares
  319. const fromX = (fromFile + 0.5) * squareSize;
  320. const fromY = (fromRank + 0.5) * squareSize;
  321. const toX = (toFile + 0.5) * squareSize;
  322. const toY = (toRank + 0.5) * squareSize;
  323. // Create SVG element for the arrow
  324. const svgNS = "http://www.w3.org/2000/svg";
  325. const svg = document.createElementNS(svgNS, "svg");
  326. svg.setAttribute("width", boardRect.width);
  327. svg.setAttribute("height", boardRect.height);
  328. svg.setAttribute("class", "chess-arrow-svg");
  329. svg.style.position = "absolute";
  330. svg.style.top = "0";
  331. svg.style.left = "0";
  332. svg.style.pointerEvents = "none";
  333. svg.style.zIndex = "100";
  334. // Calculate the angle and length of the arrow
  335. const dx = toX - fromX;
  336. const dy = toY - fromY;
  337. const angle = Math.atan2(dy, dx);
  338. const length = Math.sqrt(dx * dx + dy * dy);
  339. // Adjust start and end points to not cover the pieces
  340. const margin = squareSize * 0.3;
  341. const startX = fromX + Math.cos(angle) * margin;
  342. const startY = fromY + Math.sin(angle) * margin;
  343. const endX = toX - Math.cos(angle) * margin;
  344. const endY = toY - Math.sin(angle) * margin;
  345. // Create the arrow line
  346. const line = document.createElementNS(svgNS, "line");
  347. line.setAttribute("x1", startX);
  348. line.setAttribute("y1", startY);
  349. line.setAttribute("x2", endX);
  350. line.setAttribute("y2", endY);
  351. line.setAttribute("stroke", "rgb(235, 97, 80)");
  352. line.setAttribute("stroke-width", squareSize / 8);
  353. line.setAttribute("opacity", "0.8");
  354. // Create the arrow head
  355. const arrowHead = document.createElementNS(svgNS, "polygon");
  356. const arrowSize = squareSize / 4;
  357. const arrowAngle = Math.PI / 7;
  358. const point1X = endX;
  359. const point1Y = endY;
  360. const point2X = endX - arrowSize * Math.cos(angle - arrowAngle);
  361. const point2Y = endY - arrowSize * Math.sin(angle - arrowAngle);
  362. const point3X = endX - arrowSize * Math.cos(angle + arrowAngle);
  363. const point3Y = endY - arrowSize * Math.sin(angle + arrowAngle);
  364. arrowHead.setAttribute("points", `${point1X},${point1Y} ${point2X},${point2Y} ${point3X},${point3Y}`);
  365. arrowHead.setAttribute("fill", "rgb(235, 97, 80)");
  366. arrowHead.setAttribute("opacity", "0.8");
  367. // Add elements to SVG
  368. svg.appendChild(line);
  369. svg.appendChild(arrowHead);
  370. // Add the SVG to the board
  371. boardElement.appendChild(svg);
  372. // If not persistent, remove after delay
  373. if (!isPersistent) {
  374. setTimeout(() => {
  375. if (svg.parentNode) {
  376. svg.parentNode.removeChild(svg);
  377. }
  378. }, 1800);
  379. }
  380. }
  381.  
  382. // Modify the movePiece function to clear highlights and arrows when a move is made
  383. myFunctions.movePiece = function(from, to){
  384. // Clear any existing highlights and arrows when a move is made
  385. myFunctions.clearHighlights();
  386. myFunctions.clearArrows();
  387. for (var each=0;each<board.game.getLegalMoves().length;each++){
  388. if(board.game.getLegalMoves()[each].from == from){
  389. if(board.game.getLegalMoves()[each].to == to){
  390. var move = board.game.getLegalMoves()[each];
  391. board.game.move({
  392. ...move,
  393. promotion: 'false',
  394. animate: false,
  395. userGenerated: true
  396. });
  397. }
  398. }
  399. }
  400. }
  401.  
  402. function parser(e){
  403. // Store alternative moves for human-like play
  404. if(e.data.includes('info') && e.data.includes('pv') && !e.data.includes('bestmove')) {
  405. try {
  406. // Extract the move from the principal variation (pv)
  407. const parts = e.data.split(' ');
  408. const pvIndex = parts.indexOf('pv');
  409. if(pvIndex !== -1 && parts[pvIndex + 1]) {
  410. const move = parts[pvIndex + 1];
  411. // Store this move as an alternative
  412. if(!myVars.alternativeMoves) {
  413. myVars.alternativeMoves = [];
  414. }
  415. // Only add if not already in the list
  416. if(!myVars.alternativeMoves.includes(move)) {
  417. myVars.alternativeMoves.push(move);
  418. }
  419. }
  420. } catch (err) {
  421. console.log('Error parsing alternative move:', err);
  422. }
  423. }
  424. if(e.data.includes('bestmove')){
  425. const bestMove = e.data.split(' ')[1];
  426. console.log('Best move:', bestMove);
  427. // If human mode is active, simulate human play
  428. if(myVars.humanMode && myVars.humanMode.active && myVars.alternativeMoves && myVars.alternativeMoves.length > 0) {
  429. // Get the alternative moves (excluding the best move)
  430. const alternatives = myVars.alternativeMoves.filter(move => move !== bestMove);
  431. // Simulate human play with thinking time
  432. const moveToPlay = bestMove; // Default to best move
  433. // Simulate thinking time
  434. const thinkingTime = Math.random() *
  435. (myVars.humanMode.moveTime.max - myVars.humanMode.moveTime.min) +
  436. myVars.humanMode.moveTime.min;
  437. console.log(`Human mode: Thinking for ${thinkingTime.toFixed(1)} seconds...`);
  438. // Delay the move to simulate thinking
  439. setTimeout(() => {
  440. // Select move based on human-like error rates
  441. const selectedMove = simulateHumanPlay(bestMove, alternatives);
  442. // Play the selected move
  443. console.log(`Human mode: Playing ${selectedMove}`);
  444. myFunctions.color(selectedMove);
  445. // Reset alternative moves for next turn
  446. myVars.alternativeMoves = [];
  447. // Update auto run status if auto run is enabled
  448. if (myVars.autoRun) {
  449. myFunctions.updateAutoRunStatus('on');
  450. }
  451. }, thinkingTime * 1000);
  452. // Clear the thinking flag immediately to prevent multiple calls
  453. isThinking = false;
  454. } else {
  455. // Normal engine play (no human simulation)
  456. myFunctions.color(bestMove);
  457. isThinking = false;
  458. // Update auto run status if auto run is enabled
  459. if (myVars.autoRun) {
  460. myFunctions.updateAutoRunStatus('on');
  461. }
  462. // Reset alternative moves for next turn
  463. myVars.alternativeMoves = [];
  464. }
  465. }
  466. // Parse evaluation information
  467. if(e.data.includes('info') && e.data.includes('score cp')) {
  468. try {
  469. const parts = e.data.split(' ');
  470. const cpIndex = parts.indexOf('cp');
  471. if(cpIndex !== -1 && parts[cpIndex + 1]) {
  472. const evalValue = parseInt(parts[cpIndex + 1]) / 100; // Convert centipawns to pawns
  473. myVars.currentEvaluation = evalValue;
  474. // Get depth information
  475. const depthIndex = parts.indexOf('depth');
  476. let currentDepth = '';
  477. if(depthIndex !== -1 && parts[depthIndex + 1]) {
  478. currentDepth = parts[depthIndex + 1];
  479. }
  480. // Update depth info in evaluation text
  481. updateEvalBar(evalValue, null, currentDepth);
  482. }
  483. } catch (err) {
  484. console.log('Error parsing evaluation:', err);
  485. }
  486. }
  487. // Parse mate information
  488. if(e.data.includes('info') && e.data.includes('score mate')) {
  489. try {
  490. const parts = e.data.split(' ');
  491. const mateIndex = parts.indexOf('mate');
  492. if(mateIndex !== -1 && parts[mateIndex + 1]) {
  493. const movesToMate = parseInt(parts[mateIndex + 1]);
  494. const evalText = movesToMate > 0 ? `Mate in ${movesToMate}` : `Mate in ${Math.abs(movesToMate)}`;
  495. myVars.currentEvaluation = evalText; // Store mate text for history
  496. // Get depth information
  497. const depthIndex = parts.indexOf('depth');
  498. let currentDepth = '';
  499. if(depthIndex !== -1 && parts[depthIndex + 1]) {
  500. currentDepth = parts[depthIndex + 1];
  501. }
  502. updateEvalBar(movesToMate > 0 ? 20 : -20, evalText, currentDepth); // Use a large value to show mate
  503. }
  504. } catch (err) {
  505. console.log('Error parsing mate:', err);
  506. }
  507. }
  508. }
  509.  
  510. // Function to update the evaluation bar
  511. function updateEvalBar(evalValue, mateText = null, depth = '') {
  512. if(!evalBar || !evalText) return;
  513. // Clamp the visual representation between -5 and 5
  514. const clampedEval = Math.max(-5, Math.min(5, evalValue));
  515. const percentage = 50 + (clampedEval * 10); // Convert to percentage (0-100)
  516. evalBar.style.height = `${percentage}%`;
  517. // Update color based on who's winning and the selected color theme
  518. if(evalValue > 0.2) {
  519. evalBar.style.backgroundColor = myVars.whiteAdvantageColor || '#4CAF50'; // White advantage
  520. } else if(evalValue < -0.2) {
  521. evalBar.style.backgroundColor = myVars.blackAdvantageColor || '#F44336'; // Black advantage
  522. } else {
  523. evalBar.style.backgroundColor = '#9E9E9E'; // Grey for equal
  524. }
  525. // Update evaluation text
  526. if(mateText) {
  527. evalText.textContent = mateText + (depth ? ` (d${depth})` : '');
  528. } else {
  529. const sign = evalValue > 0 ? '+' : '';
  530. evalText.textContent = `${sign}${evalValue.toFixed(2)}` + (depth ? ` (d${depth})` : '');
  531. }
  532. // Add visual indicators for advantage
  533. const advantageIndicator = document.getElementById('advantage-indicator');
  534. if (!advantageIndicator) {
  535. const indicator = document.createElement('div');
  536. indicator.id = 'advantage-indicator';
  537. indicator.style = `
  538. position: absolute;
  539. right: 0;
  540. width: 100%;
  541. text-align: center;
  542. font-size: 12px;
  543. font-weight: bold;
  544. text-shadow: 1px 1px 2px rgba(0,0,0,0.3);
  545. pointer-events: none;
  546. `;
  547. evalBar.parentElement.appendChild(indicator);
  548. }
  549. const indicator = document.getElementById('advantage-indicator');
  550. // Clear any existing labels
  551. while (indicator.firstChild) {
  552. indicator.removeChild(indicator.firstChild);
  553. }
  554. // Add advantage labels
  555. const addLabel = (position, text, color) => {
  556. const label = document.createElement('div');
  557. label.textContent = text;
  558. label.style = `
  559. position: absolute;
  560. top: ${position}%;
  561. width: 100%;
  562. color: ${color};
  563. transform: translateY(-50%);
  564. `;
  565. indicator.appendChild(label);
  566. };
  567. // Add labels for different advantage levels
  568. addLabel(0, "Black ++", myVars.blackAdvantageColor || '#F44336');
  569. addLabel(25, "Black +", myVars.blackAdvantageColor || '#F44336');
  570. addLabel(50, "Equal", '#9E9E9E');
  571. addLabel(75, "White +", myVars.whiteAdvantageColor || '#4CAF50');
  572. addLabel(100, "White ++", myVars.whiteAdvantageColor || '#4CAF50');
  573. // Add a marker for current evaluation
  574. const marker = document.createElement('div');
  575. marker.style = `
  576. position: absolute;
  577. top: ${percentage}%;
  578. left: -15px;
  579. width: 0;
  580. height: 0;
  581. border-top: 6px solid transparent;
  582. border-bottom: 6px solid transparent;
  583. border-left: 10px solid #FFC107;
  584. transform: translateY(-50%);
  585. `;
  586. indicator.appendChild(marker);
  587. }
  588.  
  589. myFunctions.reloadChessEngine = function() {
  590. console.log(`Reloading the chess engine!`);
  591.  
  592. engine.engine.terminate();
  593. isThinking = false;
  594. myFunctions.loadChessEngine();
  595. }
  596.  
  597. myFunctions.loadChessEngine = async function() {
  598. if (!stockfishObjectURL) {
  599. const stockfishText = await GM.getResourceText('stockfish.js'); // Await the async function
  600. stockfishObjectURL = URL.createObjectURL(new Blob([stockfishText], { type: 'application/javascript' }));
  601. }
  602. console.log(stockfishObjectURL);
  603. if (stockfishObjectURL) {
  604. engine.engine = new Worker(stockfishObjectURL);
  605. engine.engine.onmessage = e => {
  606. parser(e);
  607. };
  608. engine.engine.onerror = e => {
  609. console.log("Worker Error: " + e);
  610. };
  611. engine.engine.postMessage('ucinewgame');
  612. // Set ELO if specified
  613. if (myVars.eloRating) {
  614. setEngineElo(myVars.eloRating);
  615. }
  616. }
  617. console.log('Loaded chess engine');
  618. };
  619.  
  620. // Function to set engine ELO
  621. function setEngineElo(elo) {
  622. if(!engine.engine) return;
  623. // Stockfish supports UCI_Elo option to limit playing strength
  624. engine.engine.postMessage(`setoption name UCI_Elo value ${elo}`);
  625. // Also set UCI_LimitStrength to true to enable ELO limiting
  626. engine.engine.postMessage('setoption name UCI_LimitStrength value true');
  627. // Set Skill Level based on ELO (0-20 scale)
  628. // This helps ensure the engine plays more consistently with the ELO rating
  629. let skillLevel = Math.max(0, Math.min(20, Math.floor((elo - 1000) / 100)));
  630. engine.engine.postMessage(`setoption name Skill Level value ${skillLevel}`);
  631. // Set appropriate depth limits based on ELO
  632. // Lower ELO should use lower max depth to play more consistently
  633. let maxDepth;
  634. if (elo < 1200) {
  635. maxDepth = 5; // Beginner level
  636. } else if (elo < 1500) {
  637. maxDepth = 8; // Intermediate level
  638. } else if (elo < 1800) {
  639. maxDepth = 12; // Advanced level
  640. } else if (elo < 2100) {
  641. maxDepth = 15; // Expert level
  642. } else if (elo < 2400) {
  643. maxDepth = 18; // Master level
  644. } else {
  645. maxDepth = 22; // Grandmaster level
  646. }
  647. // Store the max depth for this ELO
  648. myVars.maxDepthForElo = maxDepth;
  649. // Update the depth slider max value based on ELO
  650. if ($('#depthSlider')[0]) {
  651. // Only update the max if the current value is higher than the new max
  652. if (parseInt($('#depthSlider')[0].value) > maxDepth) {
  653. $('#depthSlider')[0].value = maxDepth;
  654. $('#depthText')[0].innerHTML = "Current Depth: <strong>" + maxDepth + "</strong>";
  655. }
  656. // Update the slider's max attribute
  657. $('#depthSlider')[0].max = maxDepth;
  658. // Add a note about depth limitation
  659. const depthNote = document.getElementById('depthNote');
  660. if (depthNote) {
  661. depthNote.textContent = `(Max depth ${maxDepth} for ELO ${elo})`;
  662. } else if ($('#depthText')[0]) {
  663. const note = document.createElement('span');
  664. note.id = 'depthNote';
  665. note.style = 'font-size: 12px; color: #666; margin-left: 5px;';
  666. note.textContent = `(Max depth ${maxDepth} for ELO ${elo})`;
  667. $('#depthText')[0].appendChild(note);
  668. }
  669. }
  670. console.log(`Engine ELO set to ${elo} with max depth ${maxDepth} and skill level ${skillLevel}`);
  671. }
  672.  
  673. // Function to set human-like play parameters
  674. function setHumanMode(level) {
  675. if(!engine.engine) return;
  676. // Define human-like play characteristics based on level
  677. let elo, moveTime, errorRate, blunderRate;
  678. switch(level) {
  679. case 'beginner':
  680. elo = 800;
  681. moveTime = { min: 1, max: 5 }; // Seconds
  682. errorRate = 0.3; // 30% chance of suboptimal moves
  683. blunderRate = 0.15; // 15% chance of blunders
  684. break;
  685. case 'casual':
  686. elo = 1200;
  687. moveTime = { min: 2, max: 8 };
  688. errorRate = 0.2;
  689. blunderRate = 0.1;
  690. break;
  691. case 'intermediate':
  692. elo = 1600;
  693. moveTime = { min: 3, max: 12 };
  694. errorRate = 0.15;
  695. blunderRate = 0.05;
  696. break;
  697. case 'advanced':
  698. elo = 2000;
  699. moveTime = { min: 5, max: 15 };
  700. errorRate = 0.1;
  701. blunderRate = 0.03;
  702. break;
  703. case 'expert':
  704. elo = 2400;
  705. moveTime = { min: 8, max: 20 };
  706. errorRate = 0.05;
  707. blunderRate = 0.01;
  708. break;
  709. default:
  710. elo = 1600; // Default to intermediate
  711. moveTime = { min: 3, max: 12 };
  712. errorRate = 0.15;
  713. blunderRate = 0.05;
  714. }
  715. // Store human mode settings
  716. myVars.humanMode = {
  717. active: true,
  718. level: level,
  719. elo: elo,
  720. moveTime: moveTime,
  721. errorRate: errorRate,
  722. blunderRate: blunderRate
  723. };
  724. // Set the engine ELO
  725. setEngineElo(elo);
  726. // Update UI to reflect human mode
  727. if ($('#humanModeLevel')[0]) {
  728. $('#humanModeLevel')[0].textContent = level.charAt(0).toUpperCase() + level.slice(1);
  729. }
  730. // Update the human mode info in the UI
  731. const humanModeInfo = document.getElementById('humanModeInfo');
  732. if (humanModeInfo) {
  733. humanModeInfo.textContent = `Playing like a ${level} human (ELO ~${elo})`;
  734. }
  735. console.log(`Human mode set to ${level} (ELO: ${elo}, Error rate: ${errorRate}, Blunder rate: ${blunderRate})`);
  736. }
  737.  
  738. // Function to calculate thinking time based on board position
  739. function calculateThinkingTime(boardState) {
  740. // Example logic: You can customize this based on your evaluation of the board
  741. const complexity = evaluateBoardComplexity(boardState); // Implement this function based on your needs
  742. const minTime = 100; // Minimum thinking time in milliseconds
  743. const maxTime = 2000; // Maximum thinking time in milliseconds
  744.  
  745. // Scale thinking time based on complexity (this is just an example)
  746. return Math.min(maxTime, minTime + complexity * 100); // Adjust the scaling factor as needed
  747. }
  748.  
  749. // Function to simulate human-like play
  750. function simulateHumanPlay(bestMove, alternativeMoves, boardState) {
  751. if (!myVars.humanMode || !myVars.humanMode.active) {
  752. return bestMove; // Return the best move if human mode is not active
  753. }
  754.  
  755. // Validate boardState
  756. if (!Array.isArray(boardState) || boardState.length !== 8 || !boardState.every(row => Array.isArray(row) && row.length === 8)) {
  757. console.error('Invalid boardState:', boardState);
  758. return bestMove; // Return the best move if boardState is invalid
  759. }
  760.  
  761. const { errorRate, blunderRate } = myVars.humanMode;
  762.  
  763. // Calculate thinking time based on the current board state
  764. const thinkingTime = calculateThinkingTime(boardState);
  765.  
  766. // Function to select a move based on human-like error rates
  767. const selectMove = () => {
  768. const random = Math.random();
  769.  
  770. // Simulate a blunder (choosing a bad move)
  771. if (random < blunderRate && alternativeMoves.length > 2) {
  772. // Pick one of the worst moves
  773. const worstMoves = alternativeMoves.slice(-2);
  774. return worstMoves[Math.floor(Math.random() * worstMoves.length)];
  775. }
  776. // Otherwise, return the best move
  777. return bestMove;
  778. };
  779.  
  780. // Return a promise that resolves after the thinking time
  781. return new Promise((resolve) => {
  782. setTimeout(() => {
  783. resolve(selectMove());
  784. }, thinkingTime);
  785. });
  786. }
  787.  
  788. // Function to extract opponent's rating from the page
  789. function extractOpponentRating() {
  790. try {
  791. // Look for user tagline components that contain ratings
  792. const userTaglines = document.querySelectorAll('.user-tagline-component');
  793. if (userTaglines.length === 0) {
  794. console.log('No user taglines found');
  795. return null;
  796. }
  797. // Find the opponent's tagline (not the current user)
  798. let opponentRating = null;
  799. for (const tagline of userTaglines) {
  800. // Extract the rating from the tagline
  801. const ratingSpan = tagline.querySelector('.user-tagline-rating');
  802. if (ratingSpan) {
  803. const ratingText = ratingSpan.textContent.trim();
  804. // Extract the number from format like "(2228)"
  805. const ratingMatch = ratingText.match(/\((\d+)\)/);
  806. if (ratingMatch && ratingMatch[1]) {
  807. opponentRating = parseInt(ratingMatch[1]);
  808. console.log(`Found opponent rating: ${opponentRating}`);
  809. // Update the opponent rating info in the UI
  810. const opponentRatingInfo = document.getElementById('opponentRatingInfo');
  811. if (opponentRatingInfo) {
  812. opponentRatingInfo.textContent = `When enabled, the engine will play at the same rating as your opponent (${opponentRating})`;
  813. }
  814. break;
  815. }
  816. }
  817. }
  818. return opponentRating;
  819. } catch (error) {
  820. console.error('Error extracting opponent rating:', error);
  821. return null;
  822. }
  823. }
  824. // Function to update fusion mode status
  825. myFunctions.updateFusionMode = function(enabled) {
  826. const fusionModeStatus = document.getElementById('fusionModeStatus');
  827. if (fusionModeStatus) {
  828. fusionModeStatus.textContent = enabled ? 'On' : 'Off';
  829. fusionModeStatus.style.color = enabled ? '#4CAF50' : '#666';
  830. }
  831. // Store previous ELO when enabling fusion mode
  832. if (enabled && !myVars.fusionMode) {
  833. myVars.previousEloRating = myVars.eloRating;
  834. }
  835. myVars.fusionMode = enabled;
  836.  
  837. if (enabled) {
  838. // Start polling for opponent ELO changes
  839. myVars.pollingInterval = setInterval(() => {
  840. const currentOpponentRating = extractOpponentRating(); // Get the current opponent rating
  841. if (currentOpponentRating !== myVars.previousOpponentRating) {
  842. myVars.isNewGame = true; // Set new game flag
  843. myVars.previousOpponentRating = currentOpponentRating; // Update previous opponent rating
  844.  
  845. // Update the ELO slider to match opponent rating
  846. if ($('#eloSlider')[0]) {
  847. // Clamp the rating to the slider's min/max values
  848. const clampedRating = Math.max(1000, Math.min(3000, currentOpponentRating));
  849. $('#eloSlider')[0].value = clampedRating;
  850. $('#eloValue')[0].textContent = clampedRating;
  851. myVars.eloRating = clampedRating;
  852.  
  853. // Set the engine ELO
  854. setEngineElo(clampedRating);
  855. }
  856. }
  857. }, 2000); // Check every 2 seconds
  858. } else {
  859. // Stop polling when fusion mode is disabled
  860. clearInterval(myVars.pollingInterval);
  861.  
  862. // Restore previous ELO setting when disabling fusion mode
  863. if (myVars.previousEloRating) {
  864. if ($('#eloSlider')[0]) {
  865. $('#eloSlider')[0].value = myVars.previousEloRating;
  866. $('#eloValue')[0].textContent = myVars.previousEloRating;
  867. myVars.eloRating = myVars.previousEloRating;
  868.  
  869. // Set the engine ELO back to previous value
  870. setEngineElo(myVars.previousEloRating);
  871. }
  872. }
  873.  
  874. // Reset the opponent rating info text
  875. const opponentRatingInfo = document.getElementById('opponentRatingInfo');
  876. if (opponentRatingInfo) {
  877. opponentRatingInfo.textContent = 'When enabled, the engine will play at the same rating as your opponent';
  878. }
  879. }
  880. }
  881.  
  882. // Function to update human mode status
  883. myFunctions.updateHumanMode = function(enabled) {
  884. const humanModeStatus = document.getElementById('humanModeStatus');
  885. if (humanModeStatus) {
  886. humanModeStatus.textContent = enabled ? 'On' : 'Off';
  887. humanModeStatus.style.color = enabled ? '#4CAF50' : '#666';
  888. }
  889. // Store previous ELO when enabling human mode
  890. if (enabled && !myVars.humanMode?.active) {
  891. myVars.previousEloRating = myVars.eloRating;
  892. }
  893. if (enabled) {
  894. // Apply the selected human mode level
  895. const level = $('#humanModeSelect').val() || 'intermediate';
  896. setHumanMode(level);
  897. } else {
  898. // Disable human mode
  899. if (myVars.humanMode) {
  900. myVars.humanMode.active = false;
  901. }
  902. // Restore previous ELO setting when disabling human mode
  903. if (myVars.previousEloRating) {
  904. if ($('#eloSlider')[0]) {
  905. $('#eloSlider')[0].value = myVars.previousEloRating;
  906. $('#eloValue')[0].textContent = myVars.previousEloRating;
  907. myVars.eloRating = myVars.previousEloRating;
  908. // Set the engine ELO back to previous value
  909. setEngineElo(myVars.previousEloRating);
  910. }
  911. }
  912. // Reset the human mode info text
  913. const humanModeInfo = document.getElementById('humanModeInfo');
  914. if (humanModeInfo) {
  915. humanModeInfo.textContent = 'When enabled, the engine will play like a human with realistic mistakes and timing';
  916. }
  917. }
  918. }
  919.  
  920. // Function to update engine ELO from UI
  921. myFunctions.updateEngineElo = function() {
  922. const eloValue = parseInt($('#eloSlider')[0].value);
  923. $('#eloValue')[0].textContent = eloValue;
  924. myVars.eloRating = eloValue;
  925. if(engine.engine) {
  926. setEngineElo(eloValue);
  927. }
  928. // Update the depth slider if it exists
  929. if ($('#depthSlider')[0] && myVars.maxDepthForElo !== undefined) {
  930. // If current depth is higher than max allowed for this ELO, adjust it
  931. if (parseInt($('#depthSlider')[0].value) > myVars.maxDepthForElo) {
  932. $('#depthSlider')[0].value = myVars.maxDepthForElo;
  933. $('#depthText')[0].innerHTML = "Current Depth: <strong>" + myVars.maxDepthForElo + "</strong>";
  934. // Re-add the depth note
  935. const depthNote = document.getElementById('depthNote');
  936. if (depthNote && $('#depthText')[0]) {
  937. $('#depthText')[0].appendChild(depthNote);
  938. }
  939. }
  940. }
  941. }
  942.  
  943. var lastValue = 11;
  944. myFunctions.runChessEngine = function(depth){
  945. // Use the depth from slider if no specific depth is provided
  946. if (depth === undefined) {
  947. depth = parseInt($('#depthSlider')[0].value);
  948. }
  949. // Ensure depth doesn't exceed the max for current ELO
  950. if (myVars.maxDepthForElo !== undefined && depth > myVars.maxDepthForElo) {
  951. depth = myVars.maxDepthForElo;
  952. console.log(`Depth limited to ${depth} based on current ELO setting`);
  953. }
  954. //var fen = myFunctions.rescan();
  955. var fen = board.game.getFEN();
  956. engine.engine.postMessage(`position fen ${fen}`);
  957. console.log('updated: ' + `position fen ${fen}`);
  958. isThinking = true;
  959. engine.engine.postMessage(`go depth ${depth}`);
  960. lastValue = depth;
  961. // Update the depth text
  962. if ($('#depthText')[0]) {
  963. $('#depthText')[0].innerHTML = "Current Depth: <strong>" + depth + "</strong>";
  964. // Re-add the depth note if it exists
  965. const depthNote = document.getElementById('depthNote');
  966. if (depthNote && $('#depthText')[0]) {
  967. $('#depthText')[0].appendChild(depthNote);
  968. }
  969. }
  970. // Update the slider value to match
  971. if ($('#depthSlider')[0]) {
  972. $('#depthSlider')[0].value = depth;
  973. }
  974. }
  975.  
  976. myFunctions.autoRun = function(lstValue){
  977. // Only run if it's the player's turn and not already thinking
  978. if(board.game.getTurn() == board.game.getPlayingAs() && !isThinking){
  979. console.log(`Auto running engine at depth ${lstValue}`);
  980. myFunctions.updateAutoRunStatus('running');
  981. myFunctions.runChessEngine(lstValue);
  982. } else {
  983. console.log("Auto run skipped - not player's turn or engine is already thinking");
  984. if (myVars.autoRun) {
  985. myFunctions.updateAutoRunStatus('waiting');
  986. }
  987. }
  988. }
  989.  
  990. document.onkeydown = function(e) {
  991. switch (e.keyCode) {
  992. case 81:
  993. myFunctions.runChessEngine(1);
  994. break;
  995. case 87:
  996. myFunctions.runChessEngine(2);
  997. break;
  998. case 69:
  999. myFunctions.runChessEngine(3);
  1000. break;
  1001. case 82:
  1002. myFunctions.runChessEngine(4);
  1003. break;
  1004. case 84:
  1005. myFunctions.runChessEngine(5);
  1006. break;
  1007. case 89:
  1008. myFunctions.runChessEngine(6);
  1009. break;
  1010. case 85:
  1011. myFunctions.runChessEngine(7);
  1012. break;
  1013. case 73:
  1014. myFunctions.runChessEngine(8);
  1015. break;
  1016. case 79:
  1017. myFunctions.runChessEngine(9);
  1018. break;
  1019. case 80:
  1020. myFunctions.runChessEngine(10);
  1021. break;
  1022. case 65:
  1023. myFunctions.runChessEngine(11);
  1024. break;
  1025. case 83:
  1026. myFunctions.runChessEngine(12);
  1027. break;
  1028. case 68:
  1029. myFunctions.runChessEngine(13);
  1030. break;
  1031. case 70:
  1032. myFunctions.runChessEngine(14);
  1033. break;
  1034. case 71:
  1035. myFunctions.runChessEngine(15);
  1036. break;
  1037. case 72:
  1038. myFunctions.runChessEngine(16);
  1039. break;
  1040. case 74:
  1041. myFunctions.runChessEngine(17);
  1042. break;
  1043. case 75:
  1044. myFunctions.runChessEngine(18);
  1045. break;
  1046. case 76:
  1047. myFunctions.runChessEngine(19);
  1048. break;
  1049. case 90:
  1050. myFunctions.runChessEngine(20);
  1051. break;
  1052. case 88:
  1053. myFunctions.runChessEngine(21);
  1054. break;
  1055. case 67:
  1056. myFunctions.runChessEngine(22);
  1057. break;
  1058. case 86:
  1059. myFunctions.runChessEngine(23);
  1060. break;
  1061. case 66:
  1062. myFunctions.runChessEngine(24);
  1063. break;
  1064. case 78:
  1065. myFunctions.runChessEngine(25);
  1066. break;
  1067. case 77:
  1068. myFunctions.runChessEngine(26);
  1069. break;
  1070. case 187:
  1071. myFunctions.runChessEngine(100);
  1072. break;
  1073. }
  1074. };
  1075.  
  1076. myFunctions.spinner = function() {
  1077. if(isThinking == true){
  1078. $('#overlay')[0].style.display = 'block';
  1079. }
  1080. if(isThinking == false) {
  1081. $('#overlay')[0].style.display = 'none';
  1082. }
  1083. }
  1084.  
  1085. let dynamicStyles = null;
  1086.  
  1087. function addAnimation(body) {
  1088. if (!dynamicStyles) {
  1089. dynamicStyles = document.createElement('style');
  1090. dynamicStyles.type = 'text/css';
  1091. document.head.appendChild(dynamicStyles);
  1092. }
  1093.  
  1094. dynamicStyles.sheet.insertRule(body, dynamicStyles.length);
  1095. }
  1096.  
  1097.  
  1098. var loaded = false;
  1099. myFunctions.loadEx = function(){
  1100. try{
  1101. var tmpStyle;
  1102. var tmpDiv;
  1103. board = $('chess-board')[0] || $('wc-chess-board')[0];
  1104. myVars.board = board;
  1105.  
  1106. // Create evaluation bar container
  1107. var evalBarContainer = document.createElement('div');
  1108. evalBarContainer.id = 'evalBarContainer';
  1109. evalBarContainer.style = `
  1110. position: absolute;
  1111. left: -30px;
  1112. top: 0;
  1113. width: 20px;
  1114. height: 100%;
  1115. background-color: #f0f0f0;
  1116. border: 1px solid #ccc;
  1117. overflow: hidden;
  1118. z-index: 100;
  1119. `;
  1120. // Create the actual evaluation bar
  1121. evalBar = document.createElement('div');
  1122. evalBar.id = 'evalBar';
  1123. evalBar.style = `
  1124. position: absolute;
  1125. bottom: 0;
  1126. width: 100%;
  1127. height: 50%;
  1128. background-color: #9E9E9E;
  1129. transition: height 0.3s, background-color 0.3s;
  1130. `;
  1131. // Create evaluation text
  1132. evalText = document.createElement('div');
  1133. evalText.id = 'evalText';
  1134. evalText.style = `
  1135. position: absolute;
  1136. top: -25px;
  1137. width: 100%;
  1138. text-align: center;
  1139. font-weight: bold;
  1140. font-size: 14px;
  1141. z-index: 101;
  1142. `;
  1143. evalText.textContent = '0.0';
  1144. // Add elements to the DOM
  1145. evalBarContainer.appendChild(evalBar);
  1146. board.parentElement.style.position = 'relative';
  1147. board.parentElement.appendChild(evalBarContainer);
  1148. board.parentElement.appendChild(evalText);
  1149.  
  1150. // Create main container with header
  1151. var div = document.createElement('div');
  1152. div.setAttribute('style','background-color:white; height:auto; border-radius: 12px; box-shadow: 0 6px 16px rgba(0,0,0,0.15); padding: 0; max-width: 300px; position: relative; font-family: "Segoe UI", Arial, sans-serif;');
  1153. div.setAttribute('id','settingsContainer');
  1154. // Create header with collapse button
  1155. var header = document.createElement('div');
  1156. header.style = `
  1157. background-color: #2196F3;
  1158. color: white;
  1159. padding: 12px 15px;
  1160. border-top-left-radius: 12px;
  1161. border-top-right-radius: 12px;
  1162. cursor: pointer;
  1163. display: flex;
  1164. justify-content: space-between;
  1165. align-items: center;
  1166. font-weight: 600;
  1167. letter-spacing: 0.3px;
  1168. `;
  1169. header.innerHTML = `
  1170. <span style="font-weight: bold; font-size: 15px;">Chess AI Controls</span>
  1171. <span id="collapseBtn" style="transition: transform 0.3s;">▼</span>
  1172. `;
  1173. //div.appendChild(header);
  1174.  
  1175.  
  1176. async function createDraggableHeader(div) {
  1177. // Set initial positioning and z-index
  1178. div.style.position = 'fixed';
  1179. div.style.zIndex = '9999';
  1180. div.style.margin = '0';
  1181. div.style.padding = '0';
  1182. var header = document.createElement('div');
  1183. header.style = `
  1184. background-color: #2196F3;
  1185. color: white;
  1186. padding: 12px 15px;
  1187. border-top-left-radius: 12px;
  1188. border-top-right-radius: 12px;
  1189. cursor: move;
  1190. display: flex;
  1191. justify-content: space-between;
  1192. align-items: center;
  1193. font-weight: 600;
  1194. letter-spacing: 0.3px;
  1195. user-select: none;
  1196. `;
  1197. header.innerHTML = `
  1198. <span id="dragArea" style="font-weight: bold; font-size: 15px; flex-grow: 1; cursor: move;">Chess AI Controls</span>
  1199. <span id="collapseBtn" style="transition: transform 0.3s;">▼</span>
  1200. `;
  1201. div.appendChild(header);
  1202. // Make the entire div draggable
  1203. let isDragging = false;
  1204. let currentX;
  1205. let currentY;
  1206. let initialX;
  1207. let initialY;
  1208. let xOffset = 0;
  1209. let yOffset = 0;
  1210. // Restore previous position on load
  1211. try {
  1212. const savedPosition = await GM.getValue('GUI Position', null);
  1213. if (savedPosition) {
  1214. xOffset = savedPosition.x;
  1215. yOffset = savedPosition.y;
  1216. div.style.transform = `translate3d(${xOffset}px, ${yOffset}px, 0)`;
  1217. }
  1218. } catch (error) {
  1219. console.error('Error loading saved position:', error);
  1220. }
  1221. // Drag area now includes the entire text span
  1222. const dragArea = header.querySelector('#dragArea');
  1223. // Event listeners for dragging
  1224. dragArea.addEventListener('mousedown', dragStart);
  1225. document.addEventListener('mouseup', dragEnd);
  1226. document.addEventListener('mousemove', drag);
  1227. function dragStart(e) {
  1228. // Prevent default to stop text selection and scrolling
  1229. e.preventDefault();
  1230. initialX = e.clientX - xOffset;
  1231. initialY = e.clientY - yOffset;
  1232. isDragging = true;
  1233. }
  1234. function dragEnd(e) {
  1235. // Prevent default to stop any browser scrolling behavior
  1236. e.preventDefault();
  1237. initialX = currentX;
  1238. initialY = currentY;
  1239. isDragging = false;
  1240. // Save the current position
  1241. try {
  1242. GM.setValue('GUI Position', { x: xOffset, y: yOffset });
  1243. } catch (error) {
  1244. console.error('Error saving position:', error);
  1245. }
  1246. }
  1247. function drag(e) {
  1248. if (isDragging) {
  1249. // Prevent default to stop scrolling and text selection
  1250. e.preventDefault();
  1251. // Constrain to viewport
  1252. currentX = Math.max(0, Math.min(e.clientX - initialX, window.innerWidth - div.offsetWidth));
  1253. currentY = Math.max(0, Math.min(e.clientY - initialY, window.innerHeight - div.offsetHeight));
  1254. xOffset = currentX;
  1255. yOffset = currentY;
  1256. setTranslate(currentX, currentY, div);
  1257. }
  1258. }
  1259. function setTranslate(xPos, yPos, el) {
  1260. el.style.transform = `translate3d(${xPos}px, ${yPos}px, 0)`;
  1261. }
  1262. return header;
  1263. }
  1264. // Usage example:
  1265. (async () => {
  1266. //var div = document.createElement('div');
  1267. await createDraggableHeader(div);
  1268. })();
  1269.  
  1270.  
  1271.  
  1272. // Create content container
  1273. var contentContainer = document.createElement('div');
  1274. contentContainer.id = 'aiControlsContent';
  1275. contentContainer.style = 'padding: 15px; font-family: "Segoe UI", Arial, sans-serif; font-size: 14px;';
  1276. // Add CSS for tabs
  1277. var tabStyle = document.createElement('style');
  1278. tabStyle.textContent = `
  1279. .tab-container {
  1280. width: 100%;
  1281. }
  1282. .tab-nav {
  1283. display: flex;
  1284. border-bottom: 2px solid #2196F3;
  1285. margin-bottom: 15px;
  1286. overflow-x: hidden; /* Prevent scrolling */
  1287. flex-wrap: nowrap; /* Keep tabs in a single row */
  1288. justify-content: space-between; /* Distribute space evenly */
  1289. }
  1290. .tab-button {
  1291. padding: 8px 5px; /* Reduce padding to fit all tabs */
  1292. background-color: #f8f8f8;
  1293. border: none;
  1294. border-radius: 8px 8px 0 0;
  1295. margin-right: 1px; /* Reduce margin between tabs */
  1296. cursor: pointer;
  1297. transition: all 0.3s;
  1298. font-weight: bold;
  1299. color: #666;
  1300. flex: 1;
  1301. text-align: center;
  1302. display: flex;
  1303. align-items: center;
  1304. justify-content: center;
  1305. box-shadow: 0 -2px 5px rgba(0,0,0,0.05);
  1306. font-size: 12px; /* Reduce font size to fit better */
  1307. }
  1308. .tab-button:hover {
  1309. background-color: #e9f5ff;
  1310. color: #2196F3;
  1311. transform: translateY(-2px);
  1312. }
  1313. .tab-button.active {
  1314. background-color: #2196F3;
  1315. color: white;
  1316. box-shadow: 0 -2px 5px rgba(33,150,243,0.3);
  1317. transform: translateY(-3px);
  1318. position: relative;
  1319. }
  1320. .tab-button.active::after {
  1321. content: '';
  1322. position: absolute;
  1323. bottom: -2px;
  1324. left: 0;
  1325. width: 100%;
  1326. height: 2px;
  1327. background-color: #2196F3;
  1328. }
  1329. .tab-content {
  1330. display: none;
  1331. padding: 10px 0;
  1332. }
  1333. .tab-content.active {
  1334. display: block;
  1335. animation: fadeIn 0.3s;
  1336. }
  1337. @keyframes fadeIn {
  1338. from { opacity: 0; }
  1339. to { opacity: 1; }
  1340. }
  1341. /* Responsive design for small screens */
  1342. @media (max-width: 500px) {
  1343. .tab-button {
  1344. padding: 8px 5px;
  1345. font-size: 12px;
  1346. }
  1347. }
  1348. /* Toggle switch styles */
  1349. .switch {
  1350. position: relative;
  1351. display: inline-block;
  1352. width: 46px;
  1353. height: 24px;
  1354. }
  1355. .switch input {
  1356. opacity: 0;
  1357. width: 0;
  1358. height: 0;
  1359. }
  1360. .slider {
  1361. position: absolute;
  1362. cursor: pointer;
  1363. top: 0;
  1364. left: 0;
  1365. right: 0;
  1366. bottom: 0;
  1367. background-color: #ccc;
  1368. transition: .3s;
  1369. border-radius: 24px;
  1370. }
  1371. .slider:before {
  1372. position: absolute;
  1373. content: "";
  1374. height: 18px;
  1375. width: 18px;
  1376. left: 3px;
  1377. bottom: 3px;
  1378. background-color: white;
  1379. transition: .3s;
  1380. border-radius: 50%;
  1381. box-shadow: 0 2px 4px rgba(0,0,0,0.2);
  1382. }
  1383. input:checked + .slider {
  1384. background-color: #2196F3;
  1385. }
  1386. input:focus + .slider {
  1387. box-shadow: 0 0 2px #2196F3;
  1388. }
  1389. input:checked + .slider:before {
  1390. transform: translateX(22px);
  1391. }
  1392. /* Button styles */
  1393. button {
  1394. transition: all 0.2s ease;
  1395. }
  1396. button:hover {
  1397. transform: translateY(-2px);
  1398. box-shadow: 0 4px 8px rgba(0,0,0,0.1);
  1399. }
  1400. button:active {
  1401. transform: translateY(0);
  1402. }
  1403. /* Input styles */
  1404. input[type="range"] {
  1405. -webkit-appearance: none;
  1406. height: 8px;
  1407. border-radius: 4px;
  1408. background: #e0e0e0;
  1409. outline: none;
  1410. }
  1411. input[type="range"]::-webkit-slider-thumb {
  1412. -webkit-appearance: none;
  1413. appearance: none;
  1414. width: 18px;
  1415. height: 18px;
  1416. border-radius: 50%;
  1417. background: #2196F3;
  1418. cursor: pointer;
  1419. box-shadow: 0 2px 4px rgba(0,0,0,0.2);
  1420. }
  1421. input[type="range"]::-moz-range-thumb {
  1422. width: 18px;
  1423. height: 18px;
  1424. border-radius: 50%;
  1425. background: #2196F3;
  1426. cursor: pointer;
  1427. box-shadow: 0 2px 4px rgba(0,0,0,0.2);
  1428. }
  1429. /* Select styles */
  1430. select {
  1431. appearance: none;
  1432. background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23333' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");
  1433. background-repeat: no-repeat;
  1434. background-position: right 10px center;
  1435. background-size: 12px;
  1436. padding-right: 30px !important;
  1437. transition: all 0.2s;
  1438. }
  1439. select:focus {
  1440. border-color: #2196F3;
  1441. box-shadow: 0 0 0 2px rgba(33, 150, 243, 0.2);
  1442. }
  1443. /* Tooltip styles */
  1444. [title] {
  1445. position: relative;
  1446. }
  1447. [title]:hover::after {
  1448. content: attr(title);
  1449. position: absolute;
  1450. bottom: 100%;
  1451. left: 50%;
  1452. transform: translateX(-50%);
  1453. background-color: #333;
  1454. color: white;
  1455. padding: 5px 10px;
  1456. border-radius: 4px;
  1457. white-space: nowrap;
  1458. z-index: 1000;
  1459. font-size: 12px;
  1460. }
  1461. `;
  1462. document.head.appendChild(tabStyle);
  1463. var content = `<div style="margin: 0;">
  1464. <!-- Tab Navigation -->
  1465. <div class="tab-container">
  1466. <div class="tab-nav">
  1467. <button class="tab-button active" data-tab="engine">
  1468. <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 3px;">
  1469. <circle cx="12" cy="12" r="10"></circle>
  1470. <line x1="12" y1="8" x2="12" y2="12"></line>
  1471. <line x1="12" y1="16" x2="12.01" y2="16"></line>
  1472. </svg>
  1473. Engine
  1474. </button>
  1475. <button class="tab-button" data-tab="actions">
  1476. <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 3px;">
  1477. <polygon points="5 3 19 12 5 21 5 3"></polygon>
  1478. </svg>
  1479. Actions
  1480. </button>
  1481. <button class="tab-button" data-tab="visual">
  1482. <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 3px;">
  1483. <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
  1484. <circle cx="12" cy="12" r="3"></circle>
  1485. </svg>
  1486. Visual
  1487. </button>
  1488. <button class="tab-button" data-tab="playstyle">
  1489. <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 3px;">
  1490. <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
  1491. <circle cx="12" cy="7" r="4"></circle>
  1492. </svg>
  1493. Play
  1494. </button>
  1495. <button class="tab-button" data-tab="auto">
  1496. <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 3px;">
  1497. <path d="M12 2L2 7l10 5 10-5-10-5z"></path>
  1498. <path d="M2 17l10 5 10-5"></path>
  1499. <path d="M2 12l10 5 10-5"></path>
  1500. </svg>
  1501. Auto
  1502. </button>
  1503. </div>
  1504. <!-- Engine Tab -->
  1505. <div id="engine-tab" class="tab-content active">
  1506. <div style="margin-bottom: 15px;">
  1507. <p id="depthText" style="margin: 0 0 5px 0;">Current Depth: <strong>11</strong></p>
  1508. <div style="display: flex; align-items: center;">
  1509. <div style="flex-grow: 1;">
  1510. <label for="depthSlider" style="display: block; margin-bottom: 5px;">Adjust Depth (1-30):</label>
  1511. <input type="range" id="depthSlider" name="depthSlider" min="1" max="30" step="1" value="11"
  1512. oninput="document.getElementById('depthText').innerHTML = 'Current Depth: <strong>' + this.value + '</strong>';"
  1513. style="width: 100%;" title="Higher depth = stronger analysis but slower calculation">
  1514. </div>
  1515. <button id="applyDepth" style="margin-left: 10px; padding: 5px 10px; background-color: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; box-shadow: 0 2px 4px rgba(76, 175, 80, 0.2);" title="Apply the selected depth to the engine">Apply</button>
  1516. </div>
  1517. </div>
  1518. <div style="margin-bottom: 15px;">
  1519. <div style="display: flex; align-items: center; margin-bottom: 5px;">
  1520. <label for="eloSlider" style="margin-right: 5px;">Engine ELO Rating: <span id="eloValue">1500</span></label>
  1521. <button id="eloInfoBtn" title="ELO rating determines the playing strength of the engine" style="margin-left: 5px; padding: 0 5px; background-color: #2196F3; color: white; border: none; border-radius: 50%; cursor: pointer; font-size: 12px;">?</button>
  1522. </div>
  1523. <input type="range" id="eloSlider" name="eloSlider" min="1000" max="3000" step="50" value="1500"
  1524. oninput="document.myFunctions.updateEngineElo()" style="width: 100%;">
  1525. <div id="eloDepthInfo" style="font-size: 12px; color: #666; margin-top: 5px; font-style: italic;">
  1526. Note: Lower ELO settings will limit the maximum search depth
  1527. </div>
  1528. </div>
  1529. </div>
  1530. <!-- Play Style Tab -->
  1531. <div id="playstyle-tab" class="tab-content">
  1532. <div style="display: flex; flex-direction: column; gap: 15px;">
  1533. <!-- Fusion Mode -->
  1534. <div style="border-left: 3px solid #2196F3; padding-left: 10px;">
  1535. <div style="display: flex; align-items: center; margin-bottom: 5px;">
  1536. <label for="fusionModeToggle" style="margin-right: 10px; font-weight: bold;">Fusion Mode:</label>
  1537. <label class="switch">
  1538. <input type="checkbox" id="fusionMode" name="fusionMode" value="false">
  1539. <span class="slider"></span>
  1540. </label>
  1541. <span id="fusionModeStatus" style="margin-left: 10px; font-size: 12px; color: #666;">Off</span>
  1542. </div>
  1543. <div id="opponentRatingInfo" style="font-size: 12px; color: #666; margin-top: 5px;">
  1544. When enabled, the engine will match your opponent's rating
  1545. </div>
  1546. </div>
  1547. <!-- Human Mode -->
  1548. <div style="border-left: 3px solid #9C27B0; padding-left: 10px;">
  1549. <div style="display: flex; align-items: center; margin-bottom: 5px;">
  1550. <label for="humanModeToggle" style="margin-right: 10px; font-weight: bold;">Human Mode:</label>
  1551. <label class="switch">
  1552. <input type="checkbox" id="humanMode" name="humanMode" value="false">
  1553. <span class="slider"></span>
  1554. </label>
  1555. <span id="humanModeStatus" style="margin-left: 10px; font-size: 12px; color: #666;">Off</span>
  1556. </div>
  1557. <div style="margin-top: 10px;">
  1558. <div style="display: flex; align-items: center; margin-bottom: 5px;">
  1559. <label for="humanModeSelect" style="margin-right: 5px;">Human Skill Level: <span id="humanModeLevel">Intermediate</span></label>
  1560. <button id="humanModeInfoBtn" title="Choose how the engine mimics human play" style="margin-left: 5px; padding: 0 5px; background-color: #9C27B0; color: white; border: none; border-radius: 50%; cursor: pointer; font-size: 12px;">?</button>
  1561. </div>
  1562. <select id="humanModeSelect" style="width: 100%; padding: 8px; margin-top: 5px; border-radius: 4px; border: 1px solid #ddd;">
  1563. <option value="beginner">Beginner (ELO ~800)</option>
  1564. <option value="casual">Casual (ELO ~1200)</option>
  1565. <option value="intermediate" selected>Intermediate (ELO ~1600)</option>
  1566. <option value="advanced">Advanced (ELO ~2000)</option>
  1567. <option value="expert">Expert (ELO ~2400)</option>
  1568. </select>
  1569. </div>
  1570. <div id="humanModeInfo" style="font-size: 12px; color: #666; margin-top: 5px; font-style: italic;">
  1571. When enabled, the engine will play like a human with realistic mistakes and timing
  1572. </div>
  1573. </div>
  1574. </div>
  1575. </div>
  1576. <!-- Visual Settings Tab -->
  1577. <div id="visual-tab" class="tab-content">
  1578. <div style="margin-bottom: 15px;">
  1579. <label for="evalBarColor" style="display: block; margin-bottom: 5px;">Evaluation Bar Color Theme:</label>
  1580. <select id="evalBarColor" style="width: 100%; padding: 8px; border-radius: 4px; border: 1px solid #ddd;">
  1581. <option value="default">Default (Green/Red)</option>
  1582. <option value="blue">Blue/Orange</option>
  1583. <option value="purple">Purple/Yellow</option>
  1584. <option value="custom">Custom</option>
  1585. </select>
  1586. </div>
  1587. <div id="customColorContainer" style="display: none; margin-bottom: 15px; padding: 10px; border: 1px dashed #ccc; border-radius: 4px;">
  1588. <div style="display: flex; gap: 10px; align-items: center; margin-bottom: 10px;">
  1589. <label for="whiteAdvantageColor">White Advantage:</label>
  1590. <input type="color" id="whiteAdvantageColor" value="#4CAF50" style="width: 40px; height: 30px;">
  1591. </div>
  1592. <div style="display: flex; gap: 10px; align-items: center;">
  1593. <label for="blackAdvantageColor">Black Advantage:</label>
  1594. <input type="color" id="blackAdvantageColor" value="#F44336" style="width: 40px; height: 30px;">
  1595. </div>
  1596. </div>
  1597. <div style="margin-bottom: 15px;">
  1598. <div style="display: flex; align-items: center; margin-bottom: 8px;">
  1599. <input type="checkbox" id="showArrows" name="showArrows" value="true" checked style="margin-right: 8px;">
  1600. <label for="showArrows"> Show move arrows</label>
  1601. </div>
  1602. <div style="display: flex; align-items: center; margin-bottom: 12px;">
  1603. <input type="checkbox" id="persistentHighlights" name="persistentHighlights" value="true" checked style="margin-right: 8px;">
  1604. <label for="persistentHighlights"> Keep highlights until next move</label>
  1605. </div>
  1606.  
  1607. <div style="margin-top: 10px; border-top: 1px solid #eee; padding-top: 10px;">
  1608. <label style="display: block; margin-bottom: 8px; font-weight: bold;">Move Indicator Style:</label>
  1609. <div style="display: flex; align-items: center; margin-bottom: 8px;">
  1610. <input type="radio" id="moveIndicatorHighlights" name="moveIndicatorType" value="highlights" checked style="margin-right: 8px;">
  1611. <label for="moveIndicatorHighlights"> Highlights</label>
  1612. </div>
  1613. <div style="display: flex; align-items: center;">
  1614. <input type="radio" id="moveIndicatorArrows" name="moveIndicatorType" value="arrows" style="margin-right: 8px;">
  1615. <label for="moveIndicatorArrows"> Arrows</label>
  1616. </div>
  1617. </div>
  1618. </div>
  1619. </div>
  1620. <!-- Automation Tab -->
  1621. <div id="auto-tab" class="tab-content">
  1622. <div style="margin-bottom: 20px; border-left: 3px solid #FF9800; padding-left: 12px;">
  1623. <div style="display: flex; align-items: center; margin-bottom: 10px;">
  1624. <label for="autoRunToggle" style="margin-right: 10px; font-weight: bold; color: #FF9800;">Auto Run:</label>
  1625. <label class="switch">
  1626. <input type="checkbox" id="autoRun" name="autoRun" value="false">
  1627. <span class="slider" style="background-color: #ccc;"></span>
  1628. </label>
  1629. <span id="autoRunStatus" style="margin-left: 10px; font-size: 12px; color: #666;">Off</span>
  1630. </div>
  1631. <div style="font-size: 12px; color: #666; margin-bottom: 10px;">
  1632. Automatically runs the engine when it's your turn
  1633. </div>
  1634. </div>
  1635. <div style="margin-bottom: 20px; border-left: 3px solid #4CAF50; padding-left: 12px;">
  1636. <div style="display: flex; align-items: center; margin-bottom: 10px;">
  1637. <label for="autoMove" style="margin-right: 10px; font-weight: bold; color: #4CAF50;">Auto Move:</label>
  1638. <label class="switch">
  1639. <input type="checkbox" id="autoMove" name="autoMove" value="false">
  1640. <span class="slider" style="background-color: #ccc;"></span>
  1641. </label>
  1642. <span id="autoMoveStatus" style="margin-left: 10px; font-size: 12px; color: #666;">Off</span>
  1643. </div>
  1644. <div style="font-size: 12px; color: #666; margin-bottom: 10px;">
  1645. Automatically plays the best move for you
  1646. </div>
  1647. </div>
  1648. <div style="margin-top: 15px; background-color: #f8f8f8; padding: 12px; border-radius: 6px;">
  1649. <label style="display: block; margin-bottom: 10px; font-weight: bold;">Auto Run Delay (Seconds):</label>
  1650. <div style="display: flex; align-items: center; gap: 10px;">
  1651. <div style="flex: 1;">
  1652. <label for="timeDelayMin" style="display: block; font-size: 12px; margin-bottom: 3px;">Minimum:</label>
  1653. <input type="number" id="timeDelayMin" name="timeDelayMin" min="0.1" value="0.1" style="width: 100%; padding: 8px; border-radius: 4px; border: 1px solid #ddd;">
  1654. </div>
  1655. <span style="color: #666;">to</span>
  1656. <div style="flex: 1;">
  1657. <label for="timeDelayMax" style="display: block; font-size: 12px; margin-bottom: 3px;">Maximum:</label>
  1658. <input type="number" id="timeDelayMax" name="timeDelayMax" min="0.1" value="1" style="width: 100%; padding: 8px; border-radius: 4px; border: 1px solid #ddd;">
  1659. </div>
  1660. </div>
  1661. <div style="font-size: 12px; color: #666; margin-top: 8px; font-style: italic;">
  1662. Random delay between min and max to simulate human thinking time
  1663. </div>
  1664. </div>
  1665. </div>
  1666. </div>
  1667. <!-- Actions Tab -->
  1668. <div id="actions-tab" class="tab-content">
  1669. <div style="display: flex; gap: 10px; margin-bottom: 15px;">
  1670. <button id="runEngineBtn" style="flex: 1; padding: 10px; background-color: #4CAF50; color: white; border: none; border-radius: 6px; cursor: pointer; font-weight: bold; box-shadow: 0 2px 5px rgba(76, 175, 80, 0.3);">
  1671. <span style="display: flex; align-items: center; justify-content: center;">
  1672. <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 5px;">
  1673. <polygon points="5 3 19 12 5 21 5 3"></polygon>
  1674. </svg>
  1675. Run Engine
  1676. </span>
  1677. </button>
  1678. <button id="stopEngineBtn" style="flex: 1; padding: 10px; background-color: #F44336; color: white; border: none; border-radius: 6px; cursor: pointer; font-weight: bold; box-shadow: 0 2px 5px rgba(244, 67, 54, 0.3);">
  1679. <span style="display: flex; align-items: center; justify-content: center;">
  1680. <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 5px;">
  1681. <rect x="6" y="6" width="12" height="12"></rect>
  1682. </svg>
  1683. Stop Engine
  1684. </span>
  1685. </button>
  1686. </div>
  1687. <button id="saveSettingsBtn" style="width: 100%; padding: 10px; background-color: #2196F3; color: white; border: none; border-radius: 6px; cursor: pointer; font-weight: bold; margin-bottom: 10px; box-shadow: 0 2px 5px rgba(33, 150, 243, 0.3);">
  1688. <span style="display: flex; align-items: center; justify-content: center;">
  1689. <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 5px;">
  1690. <path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"></path>
  1691. <polyline points="17 21 17 13 7 13 7 21"></polyline>
  1692. <polyline points="7 3 7 8 15 8"></polyline>
  1693. </svg>
  1694. Save Settings
  1695. </span>
  1696. </button>
  1697. <button id="showKeyboardShortcuts" style="width: 100%; padding: 10px; background-color: #9C27B0; color: white; border: none; border-radius: 6px; cursor: pointer; font-weight: bold; box-shadow: 0 2px 5px rgba(156, 39, 176, 0.3);">
  1698. <span style="display: flex; align-items: center; justify-content: center;">
  1699. <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 5px;">
  1700. <rect x="2" y="4" width="20" height="16" rx="2" ry="2"></rect>
  1701. <line x1="6" y1="8" x2="6" y2="8"></line>
  1702. <line x1="10" y1="8" x2="10" y2="8"></line>
  1703. <line x1="14" y1="8" x2="14" y2="8"></line>
  1704. <line x1="18" y1="8" x2="18" y2="8"></line>
  1705. <line x1="8" y1="12" x2="16" y2="12"></line>
  1706. <line x1="6" y1="16" x2="6" y2="16"></line>
  1707. <line x1="18" y1="16" x2="18" y2="16"></line>
  1708. <line x1="10" y1="16" x2="14" y2="16"></line>
  1709. </svg>
  1710. Keyboard Shortcuts
  1711. </span>
  1712. </button>
  1713. </div>
  1714. </div>
  1715. </div>`;
  1716. contentContainer.innerHTML = content;
  1717. div.appendChild(contentContainer);
  1718. // Move history will be added later in the code
  1719. // Create keyboard shortcuts modal with improved styling
  1720. var keyboardModal = document.createElement('div');
  1721. keyboardModal.id = 'keyboardShortcutsModal';
  1722. keyboardModal.style = `
  1723. display: none;
  1724. position: fixed;
  1725. top: 0;
  1726. left: 0;
  1727. width: 100%;
  1728. height: 100%;
  1729. background-color: rgba(0,0,0,0.7);
  1730. z-index: 2000;
  1731. justify-content: center;
  1732. align-items: center;
  1733. `;
  1734. var modalContent = document.createElement('div');
  1735. modalContent.style = `
  1736. background-color: white;
  1737. padding: 20px;
  1738. border-radius: 8px;
  1739. max-width: 600px;
  1740. max-height: 80vh;
  1741. overflow-y: auto;
  1742. position: relative;
  1743. box-shadow: 0 4px 20px rgba(0,0,0,0.2);
  1744. `;
  1745. var closeBtn = document.createElement('span');
  1746. closeBtn.innerHTML = '&times;';
  1747. closeBtn.style = `
  1748. position: absolute;
  1749. top: 10px;
  1750. right: 15px;
  1751. font-size: 24px;
  1752. cursor: pointer;
  1753. color: #333;
  1754. transition: color 0.2s;
  1755. `;
  1756. closeBtn.onmouseover = function() {
  1757. this.style.color = '#F44336';
  1758. };
  1759. closeBtn.onmouseout = function() {
  1760. this.style.color = '#333';
  1761. };
  1762. closeBtn.onclick = function() {
  1763. keyboardModal.style.display = 'none';
  1764. };
  1765. modalContent.appendChild(closeBtn);
  1766. var shortcutsTitle = document.createElement('h2');
  1767. shortcutsTitle.textContent = 'Keyboard Shortcuts';
  1768. shortcutsTitle.style = 'margin-top: 0; color: #2196F3; border-bottom: 2px solid #eee; padding-bottom: 10px;';
  1769. modalContent.appendChild(shortcutsTitle);
  1770. // Add a brief description
  1771. var shortcutsDescription = document.createElement('p');
  1772. shortcutsDescription.textContent = 'Press any of these keys to quickly run the engine at different depths. Keys are organized by strength level.';
  1773. shortcutsDescription.style = 'margin-bottom: 20px; color: #666;';
  1774. modalContent.appendChild(shortcutsDescription);
  1775. // Add visual keyboard layout
  1776. var keyboardLayout = document.createElement('div');
  1777. keyboardLayout.style = `
  1778. background-color: #f5f5f5;
  1779. border-radius: 8px;
  1780. padding: 15px;
  1781. margin-bottom: 20px;
  1782. text-align: center;
  1783. font-family: monospace;
  1784. `;
  1785. keyboardLayout.innerHTML = `
  1786. <div style="margin-bottom: 10px; font-weight: bold; color: #666;">Visual Keyboard Guide</div>
  1787. <div style="display: flex; justify-content: center; margin-bottom: 8px;">
  1788. <div style="width: 40px; height: 40px; background-color: #e0e0e0; border: 1px solid #ccc; border-radius: 4px; display: flex; justify-content: center; align-items: center; margin: 0 2px; position: relative;">Q<span style="position: absolute; bottom: 2px; right: 2px; font-size: 8px; color: #F44336;">1</span></div>
  1789. <div style="width: 40px; height: 40px; background-color: #e0e0e0; border: 1px solid #ccc; border-radius: 4px; display: flex; justify-content: center; align-items: center; margin: 0 2px; position: relative;">W<span style="position: absolute; bottom: 2px; right: 2px; font-size: 8px; color: #F44336;">2</span></div>
  1790. <div style="width: 40px; height: 40px; background-color: #e0e0e0; border: 1px solid #ccc; border-radius: 4px; display: flex; justify-content: center; align-items: center; margin: 0 2px; position: relative;">E<span style="position: absolute; bottom: 2px; right: 2px; font-size: 8px; color: #F44336;">3</span></div>
  1791. <div style="width: 40px; height: 40px; background-color: #e0e0e0; border: 1px solid #ccc; border-radius: 4px; display: flex; justify-content: center; align-items: center; margin: 0 2px; position: relative;">R<span style="position: absolute; bottom: 2px; right: 2px; font-size: 8px; color: #FF9800;">4</span></div>
  1792. <div style="width: 40px; height: 40px; background-color: #e0e0e0; border: 1px solid #ccc; border-radius: 4px; display: flex; justify-content: center; align-items: center; margin: 0 2px; position: relative;">T<span style="position: absolute; bottom: 2px; right: 2px; font-size: 8px; color: #FF9800;">5</span></div>
  1793. <div style="width: 40px; height: 40px; background-color: #e0e0e0; border: 1px solid #ccc; border-radius: 4px; display: flex; justify-content: center; align-items: center; margin: 0 2px; position: relative;">Y<span style="position: absolute; bottom: 2px; right: 2px; font-size: 8px; color: #FF9800;">6</span></div>
  1794. <div style="width: 40px; height: 40px; background-color: #e0e0e0; border: 1px solid #ccc; border-radius: 4px; display: flex; justify-content: center; align-items: center; margin: 0 2px; position: relative;">U<span style="position: absolute; bottom: 2px; right: 2px; font-size: 8px; color: #FF9800;">7</span></div>
  1795. <div style="width: 40px; height: 40px; background-color: #e0e0e0; border: 1px solid #ccc; border-radius: 4px; display: flex; justify-content: center; align-items: center; margin: 0 2px; position: relative;">I<span style="position: absolute; bottom: 2px; right: 2px; font-size: 8px; color: #FF9800;">8</span></div>
  1796. <div style="width: 40px; height: 40px; background-color: #e0e0e0; border: 1px solid #ccc; border-radius: 4px; display: flex; justify-content: center; align-items: center; margin: 0 2px; position: relative;">O<span style="position: absolute; bottom: 2px; right: 2px; font-size: 8px; color: #FF9800;">9</span></div>
  1797. <div style="width: 40px; height: 40px; background-color: #e0e0e0; border: 1px solid #ccc; border-radius: 4px; display: flex; justify-content: center; align-items: center; margin: 0 2px; position: relative;">P<span style="position: absolute; bottom: 2px; right: 2px; font-size: 8px; color: #4CAF50;">10</span></div>
  1798. </div>
  1799. <div style="display: flex; justify-content: center; margin-bottom: 8px; margin-left: 20px;">
  1800. <div style="width: 40px; height: 40px; background-color: #e0e0e0; border: 1px solid #ccc; border-radius: 4px; display: flex; justify-content: center; align-items: center; margin: 0 2px; position: relative;">A<span style="position: absolute; bottom: 2px; right: 2px; font-size: 8px; color: #4CAF50;">11</span></div>
  1801. <div style="width: 40px; height: 40px; background-color: #e0e0e0; border: 1px solid #ccc; border-radius: 4px; display: flex; justify-content: center; align-items: center; margin: 0 2px; position: relative;">S<span style="position: absolute; bottom: 2px; right: 2px; font-size: 8px; color: #4CAF50;">12</span></div>
  1802. <div style="width: 40px; height: 40px; background-color: #e0e0e0; border: 1px solid #ccc; border-radius: 4px; display: flex; justify-content: center; align-items: center; margin: 0 2px; position: relative;">D<span style="position: absolute; bottom: 2px; right: 2px; font-size: 8px; color: #4CAF50;">13</span></div>
  1803. <div style="width: 40px; height: 40px; background-color: #e0e0e0; border: 1px solid #ccc; border-radius: 4px; display: flex; justify-content: center; align-items: center; margin: 0 2px; position: relative;">F<span style="position: absolute; bottom: 2px; right: 2px; font-size: 8px; color: #4CAF50;">14</span></div>
  1804. <div style="width: 40px; height: 40px; background-color: #e0e0e0; border: 1px solid #ccc; border-radius: 4px; display: flex; justify-content: center; align-items: center; margin: 0 2px; position: relative;">G<span style="position: absolute; bottom: 2px; right: 2px; font-size: 8px; color: #4CAF50;">15</span></div>
  1805. <div style="width: 40px; height: 40px; background-color: #e0e0e0; border: 1px solid #ccc; border-radius: 4px; display: flex; justify-content: center; align-items: center; margin: 0 2px; position: relative;">H<span style="position: absolute; bottom: 2px; right: 2px; font-size: 8px; color: #2196F3;">16</span></div>
  1806. <div style="width: 40px; height: 40px; background-color: #e0e0e0; border: 1px solid #ccc; border-radius: 4px; display: flex; justify-content: center; align-items: center; margin: 0 2px; position: relative;">J<span style="position: absolute; bottom: 2px; right: 2px; font-size: 8px; color: #2196F3;">17</span></div>
  1807. <div style="width: 40px; height: 40px; background-color: #e0e0e0; border: 1px solid #ccc; border-radius: 4px; display: flex; justify-content: center; align-items: center; margin: 0 2px; position: relative;">K<span style="position: absolute; bottom: 2px; right: 2px; font-size: 8px; color: #2196F3;">18</span></div>
  1808. <div style="width: 40px; height: 40px; background-color: #e0e0e0; border: 1px solid #ccc; border-radius: 4px; display: flex; justify-content: center; align-items: center; margin: 0 2px; position: relative;">L<span style="position: absolute; bottom: 2px; right: 2px; font-size: 8px; color: #2196F3;">19</span></div>
  1809. </div>
  1810. <div style="display: flex; justify-content: center; margin-left: 40px;">
  1811. <div style="width: 40px; height: 40px; background-color: #e0e0e0; border: 1px solid #ccc; border-radius: 4px; display: flex; justify-content: center; align-items: center; margin: 0 2px; position: relative;">Z<span style="position: absolute; bottom: 2px; right: 2px; font-size: 8px; color: #9C27B0;">20</span></div>
  1812. <div style="width: 40px; height: 40px; background-color: #e0e0e0; border: 1px solid #ccc; border-radius: 4px; display: flex; justify-content: center; align-items: center; margin: 0 2px; position: relative;">X<span style="position: absolute; bottom: 2px; right: 2px; font-size: 8px; color: #9C27B0;">21</span></div>
  1813. <div style="width: 40px; height: 40px; background-color: #e0e0e0; border: 1px solid #ccc; border-radius: 4px; display: flex; justify-content: center; align-items: center; margin: 0 2px; position: relative;">C<span style="position: absolute; bottom: 2px; right: 2px; font-size: 8px; color: #9C27B0;">22</span></div>
  1814. <div style="width: 40px; height: 40px; background-color: #e0e0e0; border: 1px solid #ccc; border-radius: 4px; display: flex; justify-content: center; align-items: center; margin: 0 2px; position: relative;">V<span style="position: absolute; bottom: 2px; right: 2px; font-size: 8px; color: #9C27B0;">23</span></div>
  1815. <div style="width: 40px; height: 40px; background-color: #e0e0e0; border: 1px solid #ccc; border-radius: 4px; display: flex; justify-content: center; align-items: center; margin: 0 2px; position: relative;">B<span style="position: absolute; bottom: 2px; right: 2px; font-size: 8px; color: #9C27B0;">24</span></div>
  1816. <div style="width: 40px; height: 40px; background-color: #e0e0e0; border: 1px solid #ccc; border-radius: 4px; display: flex; justify-content: center; align-items: center; margin: 0 2px; position: relative;">N<span style="position: absolute; bottom: 2px; right: 2px; font-size: 8px; color: #9C27B0;">25</span></div>
  1817. <div style="width: 40px; height: 40px; background-color: #e0e0e0; border: 1px solid #ccc; border-radius: 4px; display: flex; justify-content: center; align-items: center; margin: 0 2px; position: relative;">M<span style="position: absolute; bottom: 2px; right: 2px; font-size: 8px; color: #9C27B0;">26</span></div>
  1818. </div>
  1819. <div style="margin-top: 15px; display: flex; justify-content: center;">
  1820. <div style="width: 80px; height: 40px; background-color: #e0e0e0; border: 1px solid #ccc; border-radius: 4px; display: flex; justify-content: center; align-items: center; margin: 0 2px; position: relative;">=<span style="position: absolute; bottom: 2px; right: 2px; font-size: 8px; color: #E91E63;">MAX</span></div>
  1821. </div>
  1822. <div style="margin-top: 15px; font-size: 12px;">
  1823. <span style="color: #F44336;">■</span> Beginner &nbsp;
  1824. <span style="color: #FF9800;">■</span> Intermediate &nbsp;
  1825. <span style="color: #4CAF50;">■</span> Advanced &nbsp;
  1826. <span style="color: #2196F3;">■</span> Expert &nbsp;
  1827. <span style="color: #9C27B0;">■</span> Master &nbsp;
  1828. <span style="color: #E91E63;">■</span> Maximum
  1829. </div>
  1830. `;
  1831. modalContent.appendChild(keyboardLayout);
  1832. var shortcutsTable = document.createElement('table');
  1833. shortcutsTable.style = 'width: 100%; border-collapse: collapse;';
  1834. // Create table header
  1835. var tableHeader = document.createElement('thead');
  1836. tableHeader.innerHTML = `
  1837. <tr style="background-color: #f5f5f5;">
  1838. <th style="text-align: left; padding: 12px; border-bottom: 2px solid #ddd; width: 20%;">Key</th>
  1839. <th style="text-align: left; padding: 12px; border-bottom: 2px solid #ddd;">Function</th>
  1840. <th style="text-align: left; padding: 12px; border-bottom: 2px solid #ddd;">Strength</th>
  1841. </tr>
  1842. `;
  1843. shortcutsTable.appendChild(tableHeader);
  1844. // Create table body with all keyboard shortcuts
  1845. var tableBody = document.createElement('tbody');
  1846. // Define all shortcuts with strength categories
  1847. const shortcuts = [
  1848. { key: 'Q', function: 'Run engine at depth 1', strength: 'Beginner' },
  1849. { key: 'W', function: 'Run engine at depth 2', strength: 'Beginner' },
  1850. { key: 'E', function: 'Run engine at depth 3', strength: 'Beginner' },
  1851. { key: 'R', function: 'Run engine at depth 4', strength: 'Intermediate' },
  1852. { key: 'T', function: 'Run engine at depth 5', strength: 'Intermediate' },
  1853. { key: 'Y', function: 'Run engine at depth 6', strength: 'Intermediate' },
  1854. { key: 'U', function: 'Run engine at depth 7', strength: 'Intermediate' },
  1855. { key: 'I', function: 'Run engine at depth 8', strength: 'Intermediate' },
  1856. { key: 'O', function: 'Run engine at depth 9', strength: 'Intermediate' },
  1857. { key: 'P', function: 'Run engine at depth 10', strength: 'Advanced' },
  1858. { key: 'A', function: 'Run engine at depth 11', strength: 'Advanced' },
  1859. { key: 'S', function: 'Run engine at depth 12', strength: 'Advanced' },
  1860. { key: 'D', function: 'Run engine at depth 13', strength: 'Advanced' },
  1861. { key: 'F', function: 'Run engine at depth 14', strength: 'Advanced' },
  1862. { key: 'G', function: 'Run engine at depth 15', strength: 'Advanced' },
  1863. { key: 'H', function: 'Run engine at depth 16', strength: 'Expert' },
  1864. { key: 'J', function: 'Run engine at depth 17', strength: 'Expert' },
  1865. { key: 'K', function: 'Run engine at depth 18', strength: 'Expert' },
  1866. { key: 'L', function: 'Run engine at depth 19', strength: 'Expert' },
  1867. { key: 'Z', function: 'Run engine at depth 20', strength: 'Master' },
  1868. { key: 'X', function: 'Run engine at depth 21', strength: 'Master' },
  1869. { key: 'C', function: 'Run engine at depth 22', strength: 'Master' },
  1870. { key: 'V', function: 'Run engine at depth 23', strength: 'Master' },
  1871. { key: 'B', function: 'Run engine at depth 24', strength: 'Master' },
  1872. { key: 'N', function: 'Run engine at depth 25', strength: 'Master' },
  1873. { key: 'M', function: 'Run engine at depth 26', strength: 'Master' },
  1874. { key: '=', function: 'Run engine at maximum depth', strength: 'Maximum' }
  1875. ];
  1876. // Add rows for each shortcut
  1877. shortcuts.forEach((shortcut, index) => {
  1878. const row = document.createElement('tr');
  1879. row.style = index % 2 === 0 ? '' : 'background-color: #f9f9f9;';
  1880. // Set color based on strength
  1881. let strengthColor = '#333';
  1882. switch(shortcut.strength) {
  1883. case 'Beginner': strengthColor = '#F44336'; break;
  1884. case 'Intermediate': strengthColor = '#FF9800'; break;
  1885. case 'Advanced': strengthColor = '#4CAF50'; break;
  1886. case 'Expert': strengthColor = '#2196F3'; break;
  1887. case 'Master': strengthColor = '#9C27B0'; break;
  1888. case 'Maximum': strengthColor = '#E91E63'; break;
  1889. }
  1890. row.innerHTML = `
  1891. <td style="padding: 10px; border-bottom: 1px solid #eee;">
  1892. <kbd style="background-color: #f1f1f1; border: 1px solid #ccc; border-radius: 4px; padding: 2px 6px; font-family: monospace;">${shortcut.key}</kbd>
  1893. </td>
  1894. <td style="padding: 10px; border-bottom: 1px solid #eee;">${shortcut.function}</td>
  1895. <td style="padding: 10px; border-bottom: 1px solid #eee; color: ${strengthColor};">${shortcut.strength}</td>
  1896. `;
  1897. tableBody.appendChild(row);
  1898. });
  1899. shortcutsTable.appendChild(tableBody);
  1900. modalContent.appendChild(shortcutsTable);
  1901. // Add a note at the bottom
  1902. var shortcutsNote = document.createElement('p');
  1903. shortcutsNote.innerHTML = '<strong>Note:</strong> Higher depths provide stronger analysis but take longer to calculate. For casual play, depths 1-10 are usually sufficient. For serious analysis, try depths 15+.';
  1904. shortcutsNote.style = 'margin-top: 20px; color: #666; font-size: 13px; background-color: #f5f5f5; padding: 10px; border-radius: 4px;';
  1905. modalContent.appendChild(shortcutsNote);
  1906. keyboardModal.appendChild(modalContent);
  1907. document.body.appendChild(keyboardModal);
  1908. // Add JavaScript for tab switching
  1909. setTimeout(function() {
  1910. const tabButtons = document.querySelectorAll('.tab-button');
  1911. const collapseBtn = document.getElementById('collapseBtn');
  1912. const aiControlsContent = document.getElementById('aiControlsContent');
  1913. const header = document.querySelector('#settingsContainer > div:first-child');
  1914. // Function to toggle content visibility
  1915. const toggleContent = () => {
  1916. if (aiControlsContent.style.display === 'none') {
  1917. aiControlsContent.style.display = 'block';
  1918. collapseBtn.style.transform = 'rotate(0deg)';
  1919. } else {
  1920. aiControlsContent.style.display = 'none';
  1921. collapseBtn.style.transform = 'rotate(180deg)';
  1922. }
  1923. };
  1924. // Add collapse functionality to button
  1925. if (collapseBtn && aiControlsContent) {
  1926. collapseBtn.addEventListener('click', function(e) {
  1927. e.stopPropagation(); // Prevent header click event
  1928. toggleContent();
  1929. });
  1930. }
  1931. // Make header clickable
  1932. if (header && aiControlsContent) {
  1933. header.addEventListener('click', toggleContent);
  1934. }
  1935. // Handle Auto Move toggle
  1936. const autoMoveCheckbox = document.getElementById('autoMove');
  1937. const autoMoveStatus = document.getElementById('autoMoveStatus');
  1938. if (autoMoveCheckbox && autoMoveStatus) {
  1939. autoMoveCheckbox.addEventListener('change', function() {
  1940. autoMoveStatus.textContent = this.checked ? 'On' : 'Off';
  1941. autoMoveStatus.style.color = this.checked ? '#4CAF50' : '#666';
  1942. });
  1943. }
  1944. // Handle Auto Run toggle
  1945. const autoRunCheckbox = document.getElementById('autoRun');
  1946. const autoRunStatus = document.getElementById('autoRunStatus');
  1947. if (autoRunCheckbox && autoRunStatus) {
  1948. autoRunCheckbox.addEventListener('change', function() {
  1949. autoRunStatus.textContent = this.checked ? 'On' : 'Off';
  1950. autoRunStatus.style.color = this.checked ? '#FF9800' : '#666';
  1951. });
  1952. }
  1953. tabButtons.forEach(button => {
  1954. button.addEventListener('click', function() {
  1955. // Remove active class from all tabs
  1956. document.querySelectorAll('.tab-button').forEach(btn => btn.classList.remove('active'));
  1957. document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active'));
  1958. // Add active class to clicked tab
  1959. this.classList.add('active');
  1960. document.getElementById(this.dataset.tab + '-tab').classList.add('active');
  1961. });
  1962. });
  1963. }, 500);
  1964.  
  1965. board.parentElement.parentElement.appendChild(div);
  1966. // Add move history display
  1967. const moveHistoryDisplay = myFunctions.createMoveHistoryDisplay();
  1968. contentContainer.appendChild(moveHistoryDisplay);
  1969.  
  1970. //spinnerContainer
  1971. var spinCont = document.createElement('div');
  1972. spinCont.setAttribute('style','display:none; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.5); z-index: 1000; display: flex; justify-content: center; align-items: center;');
  1973. spinCont.setAttribute('id','overlay');
  1974. div.prepend(spinCont);
  1975. //spinner
  1976. var spinr = document.createElement('div')
  1977. spinr.setAttribute('style',`
  1978. margin: 0 auto;
  1979. height: 64px;
  1980. width: 64px;
  1981. animation: rotate 0.8s infinite linear;
  1982. border: 5px solid firebrick;
  1983. border-right-color: transparent;
  1984. border-radius: 50%;
  1985. `);
  1986. spinCont.appendChild(spinr);
  1987. addAnimation(`@keyframes rotate {
  1988. 0% {
  1989. transform: rotate(0deg);
  1990. }
  1991. 100% {
  1992. transform: rotate(360deg);
  1993. }
  1994. }`);
  1995.  
  1996.  
  1997. //Reload Button
  1998. var reSty = `
  1999. #relButDiv {
  2000. position: relative;
  2001. text-align: center;
  2002. margin: 0 0 8px 0;
  2003. }
  2004. #relEngBut {
  2005. position: relative;
  2006. color: #ffffff;
  2007. background-color: #3cba2c;
  2008. font-size: 16px;
  2009. border: none;
  2010. border-radius: 4px;
  2011. padding: 10px 20px;
  2012. letter-spacing: 1px;
  2013. cursor: pointer;
  2014. transition: background-color 0.3s;
  2015. }
  2016. #relEngBut:hover {
  2017. background-color: #2d8c22;
  2018. }
  2019. #relEngBut:active {
  2020. background-color: #2d8c22;
  2021. transform: translateY(2px);
  2022. }`;
  2023. var reBut = `<button type="button" name="reloadEngine" id="relEngBut" onclick="document.myFunctions.reloadChessEngine()">Reload Chess Engine</button>`;
  2024. tmpDiv = document.createElement('div');
  2025. var relButDiv = document.createElement('div');
  2026. relButDiv.id = 'relButDiv';
  2027. tmpDiv.innerHTML = reBut;
  2028. reBut = tmpDiv.firstChild;
  2029.  
  2030. tmpStyle = document.createElement('style');
  2031. tmpStyle.innerHTML = reSty;
  2032. document.head.append(tmpStyle);
  2033.  
  2034. relButDiv.append(reBut);
  2035. contentContainer.append(relButDiv);
  2036.  
  2037. // Issue Button
  2038. // var isBut = `<button type="button" name="isBut" onclick="window.confirm('Can I take you to the issues page?') ? document.location = 'https://github.com/Auzgame/userscripts/issues' : console.log('cancled')">Got An Issue/Bug?</button>`;
  2039. // tmpDiv = document.createElement('div');
  2040. // var isButDiv = document.createElement('div');
  2041. // isButDiv.style = `
  2042. // position: relative;
  2043. // text-align: center;
  2044. // margin: 0 0 8px 0;
  2045. // `;
  2046. // tmpDiv.innerHTML = isBut;
  2047. // isBut = tmpDiv.firstChild;
  2048. // isBut.id = 'isBut';
  2049. // isBut.style = `
  2050. // position: relative;
  2051. // color: #ffffff;
  2052. // background-color: #919191;
  2053. // font-size: 16px;
  2054. // border: none;
  2055. // border-radius: 4px;
  2056. // padding: 10px 20px;
  2057. // letter-spacing: 1px;
  2058. // cursor: pointer;
  2059. // transition: background-color 0.3s;
  2060. // `;
  2061. // isButDiv.append(isBut);
  2062. // contentContainer.append(isButDiv);
  2063.  
  2064. // Add event listeners for the new buttons and controls
  2065. $('#applyDepth').on('click', function() {
  2066. myFunctions.runChessEngine();
  2067. });
  2068. $('#runEngineBtn').on('click', function() {
  2069. myFunctions.runChessEngine();
  2070. });
  2071. $('#stopEngineBtn').on('click', function() {
  2072. if (engine.engine) {
  2073. engine.engine.postMessage('stop');
  2074. isThinking = false;
  2075. myFunctions.spinner();
  2076. }
  2077. });
  2078. $('#saveSettingsBtn').on('click', function() {
  2079. myFunctions.saveSettings();
  2080. });
  2081. $('#showKeyboardShortcuts').on('click', function() {
  2082. document.getElementById('keyboardShortcutsModal').style.display = 'flex';
  2083. });
  2084. // Add collapse functionality
  2085. header.onclick = function() {
  2086. const content = document.getElementById('aiControlsContent');
  2087. const collapseBtn = document.getElementById('collapseBtn');
  2088. if (content.style.display === 'none') {
  2089. content.style.display = 'block';
  2090. collapseBtn.textContent = '▼';
  2091. } else {
  2092. content.style.display = 'none';
  2093. collapseBtn.textContent = '▲';
  2094. }
  2095. };
  2096. $('#evalBarColor').on('change', function() {
  2097. const theme = $(this).val();
  2098. if (theme === 'custom') {
  2099. $('#customColorContainer').show();
  2100. } else {
  2101. $('#customColorContainer').hide();
  2102. // Apply predefined color themes
  2103. let whiteColor, blackColor;
  2104. switch(theme) {
  2105. case 'blue':
  2106. whiteColor = '#2196F3'; // Blue
  2107. blackColor = '#FF9800'; // Orange
  2108. break;
  2109. case 'purple':
  2110. whiteColor = '#9C27B0'; // Purple
  2111. blackColor = '#FFEB3B'; // Yellow
  2112. break;
  2113. default: // default
  2114. whiteColor = '#4CAF50'; // Green
  2115. blackColor = '#F44336'; // Red
  2116. }
  2117. // Store colors in variables for the updateEvalBar function to use
  2118. myVars.whiteAdvantageColor = whiteColor;
  2119. myVars.blackAdvantageColor = blackColor;
  2120. // Update the evaluation bar with current value but new colors
  2121. updateEvalBar(myVars.currentEvaluation);
  2122. }
  2123. });
  2124. $('#whiteAdvantageColor, #blackAdvantageColor').on('change', function() {
  2125. myVars.whiteAdvantageColor = $('#whiteAdvantageColor').val();
  2126. myVars.blackAdvantageColor = $('#blackAdvantageColor').val();
  2127. updateEvalBar(myVars.currentEvaluation);
  2128. });
  2129. // Initialize color theme variables
  2130. myVars.whiteAdvantageColor = '#4CAF50';
  2131. myVars.blackAdvantageColor = '#F44336';
  2132. // Initialize fusion mode
  2133. myVars.fusionMode = false;
  2134. // Load saved settings
  2135. myFunctions.loadSettings();
  2136. // Update fusion mode status based on saved settings
  2137. if (myVars.fusionMode) {
  2138. myFunctions.updateFusionMode(true);
  2139. $('#fusionMode').prop('checked', true);
  2140. $('#eloSlider').prop('disabled', true);
  2141. }
  2142. // Periodically check for opponent rating changes when fusion mode is enabled
  2143. setInterval(function() {
  2144. if (myVars.fusionMode) {
  2145. extractOpponentRating();
  2146. }
  2147. }, 10000); // Check every 10 seconds
  2148. // Create ELO info modal
  2149. var eloInfoModal = document.createElement('div');
  2150. eloInfoModal.id = 'eloInfoModal';
  2151. eloInfoModal.style = `
  2152. display: none;
  2153. position: fixed;
  2154. top: 0;
  2155. left: 0;
  2156. width: 100%;
  2157. height: 100%;
  2158. background-color: rgba(0,0,0,0.7);
  2159. z-index: 2000;
  2160. justify-content: center;
  2161. align-items: center;
  2162. `;
  2163. var eloModalContent = document.createElement('div');
  2164. eloModalContent.style = `
  2165. background-color: white;
  2166. padding: 20px;
  2167. border-radius: 8px;
  2168. max-width: 500px;
  2169. max-height: 80vh;
  2170. overflow-y: auto;
  2171. position: relative;
  2172. `;
  2173. var eloCloseBtn = document.createElement('span');
  2174. eloCloseBtn.innerHTML = '&times;';
  2175. eloCloseBtn.style = `
  2176. position: absolute;
  2177. top: 10px;
  2178. right: 15px;
  2179. font-size: 24px;
  2180. cursor: pointer;
  2181. color: #333;
  2182. `;
  2183. eloCloseBtn.onclick = function() {
  2184. eloInfoModal.style.display = 'none';
  2185. };
  2186. eloModalContent.appendChild(eloCloseBtn);
  2187. var eloInfoTitle = document.createElement('h2');
  2188. eloInfoTitle.textContent = 'ELO Rating and Depth Relationship';
  2189. eloInfoTitle.style = 'margin-top: 0; color: #2196F3;';
  2190. eloModalContent.appendChild(eloInfoTitle);
  2191. var eloInfoText = document.createElement('div');
  2192. eloInfoText.innerHTML = `
  2193. <p>The ELO rating setting affects how strong the chess engine plays. Lower ELO ratings make the engine play more like a beginner, while higher ratings make it play more like a master.</p>
  2194. <p>To ensure the engine plays consistently with its ELO rating, the maximum search depth is limited based on the selected ELO:</p>
  2195. <ul>
  2196. <li><strong>1000-1199 ELO:</strong> Maximum depth 5 (Beginner level)</li>
  2197. <li><strong>1200-1499 ELO:</strong> Maximum depth 8 (Intermediate level)</li>
  2198. <li><strong>1500-1799 ELO:</strong> Maximum depth 12 (Advanced level)</li>
  2199. <li><strong>1800-2099 ELO:</strong> Maximum depth 15 (Expert level)</li>
  2200. <li><strong>2100-2399 ELO:</strong> Maximum depth 18 (Master level)</li>
  2201. <li><strong>2400+ ELO:</strong> Maximum depth 22 (Grandmaster level)</li>
  2202. </ul>
  2203. <p>If you set a depth higher than the maximum for the current ELO, it will be automatically limited to the maximum allowed depth.</p>
  2204. <p>This ensures that the engine plays consistently with its ELO rating and doesn't make moves that are too strong for the selected rating.</p>
  2205. `;
  2206. eloModalContent.appendChild(eloInfoText);
  2207. eloInfoModal.appendChild(eloModalContent);
  2208. document.body.appendChild(eloInfoModal);
  2209. $('#eloInfoBtn').on('click', function() {
  2210. document.getElementById('eloInfoModal').style.display = 'flex';
  2211. });
  2212.  
  2213. // Create Human Mode info modal
  2214. var humanModeInfoModal = document.createElement('div');
  2215. humanModeInfoModal.id = 'humanModeInfoModal';
  2216. humanModeInfoModal.style = `
  2217. display: none;
  2218. position: fixed;
  2219. top: 0;
  2220. left: 0;
  2221. width: 100%;
  2222. height: 100%;
  2223. background-color: rgba(0,0,0,0.7);
  2224. z-index: 2000;
  2225. justify-content: center;
  2226. align-items: center;
  2227. `;
  2228. var humanModeModalContent = document.createElement('div');
  2229. humanModeModalContent.style = `
  2230. background-color: white;
  2231. padding: 20px;
  2232. border-radius: 8px;
  2233. max-width: 500px;
  2234. max-height: 80vh;
  2235. overflow-y: auto;
  2236. position: relative;
  2237. `;
  2238. var humanModeCloseBtn = document.createElement('span');
  2239. humanModeCloseBtn.innerHTML = '&times;';
  2240. humanModeCloseBtn.style = `
  2241. position: absolute;
  2242. top: 10px;
  2243. right: 15px;
  2244. font-size: 24px;
  2245. cursor: pointer;
  2246. color: #333;
  2247. `;
  2248. humanModeCloseBtn.onclick = function() {
  2249. humanModeInfoModal.style.display = 'none';
  2250. };
  2251. humanModeModalContent.appendChild(humanModeCloseBtn);
  2252. var humanModeInfoTitle = document.createElement('h2');
  2253. humanModeInfoTitle.textContent = 'Human Mode: Realistic Chess Play';
  2254. humanModeInfoTitle.style = 'margin-top: 0; color: #2196F3;';
  2255. humanModeModalContent.appendChild(humanModeInfoTitle);
  2256. var humanModeInfoText = document.createElement('div');
  2257. humanModeInfoText.innerHTML = `
  2258. <p>Human Mode makes the chess engine play more like a real human player by introducing:</p>
  2259. <ul>
  2260. <li><strong>Realistic thinking time</strong> - varies based on skill level</li>
  2261. <li><strong>Occasional mistakes</strong> - humans don't always find the best move</li>
  2262. <li><strong>Rare blunders</strong> - even good players make serious mistakes sometimes</li>
  2263. </ul>
  2264. <p>Choose from five different skill levels:</p>
  2265. <table style="width: 100%; border-collapse: collapse; margin-top: 10px;">
  2266. <tr style="background-color: #f2f2f2;">
  2267. <th style="padding: 8px; text-align: left; border: 1px solid #ddd;">Skill Level</th>
  2268. <th style="padding: 8px; text-align: left; border: 1px solid #ddd;">ELO Range</th>
  2269. <th style="padding: 8px; text-align: left; border: 1px solid #ddd;">Characteristics</th>
  2270. </tr>
  2271. <tr>
  2272. <td style="padding: 8px; border: 1px solid #ddd;"><strong>Beginner</strong></td>
  2273. <td style="padding: 8px; border: 1px solid #ddd;">~800</td>
  2274. <td style="padding: 8px; border: 1px solid #ddd;">Quick moves, frequent mistakes, occasional blunders</td>
  2275. </tr>
  2276. <tr>
  2277. <td style="padding: 8px; border: 1px solid #ddd;"><strong>Casual</strong></td>
  2278. <td style="padding: 8px; border: 1px solid #ddd;">~1200</td>
  2279. <td style="padding: 8px; border: 1px solid #ddd;">Moderate thinking time, common mistakes</td>
  2280. </tr>
  2281. <tr>
  2282. <td style="padding: 8px; border: 1px solid #ddd;"><strong>Intermediate</strong></td>
  2283. <td style="padding: 8px; border: 1px solid #ddd;">~1600</td>
  2284. <td style="padding: 8px; border: 1px solid #ddd;">Longer thinking on complex positions, occasional mistakes</td>
  2285. </tr>
  2286. <tr>
  2287. <td style="padding: 8px; border: 1px solid #ddd;"><strong>Advanced</strong></td>
  2288. <td style="padding: 8px; border: 1px solid #ddd;">~2000</td>
  2289. <td style="padding: 8px; border: 1px solid #ddd;">Careful consideration, infrequent mistakes</td>
  2290. </tr>
  2291. <tr>
  2292. <td style="padding: 8px; border: 1px solid #ddd;"><strong>Expert</strong></td>
  2293. <td style="padding: 8px; border: 1px solid #ddd;">~2400</td>
  2294. <td style="padding: 8px; border: 1px solid #ddd;">Deep analysis, rare mistakes, very rare blunders</td>
  2295. </tr>
  2296. </table>
  2297. <p style="margin-top: 15px;"><strong>Note:</strong> Human Mode and Fusion Mode cannot be active at the same time.</p>
  2298. `;
  2299. humanModeModalContent.appendChild(humanModeInfoText);
  2300. humanModeInfoModal.appendChild(humanModeModalContent);
  2301. document.body.appendChild(humanModeInfoModal);
  2302.  
  2303. // Add CSS for toggle switch
  2304. var toggleStyle = document.createElement('style');
  2305. toggleStyle.innerHTML = `
  2306. /* The switch - the box around the slider */
  2307. .switch {
  2308. position: relative;
  2309. display: inline-block;
  2310. width: 50px;
  2311. height: 24px;
  2312. }
  2313. /* Hide default HTML checkbox */
  2314. .switch input {
  2315. opacity: 0;
  2316. width: 0;
  2317. height: 0;
  2318. }
  2319. /* The slider */
  2320. .slider {
  2321. position: absolute;
  2322. cursor: pointer;
  2323. top: 0;
  2324. left: 0;
  2325. right: 0;
  2326. bottom: 0;
  2327. background-color: #ccc;
  2328. transition: .4s;
  2329. }
  2330. .slider:before {
  2331. position: absolute;
  2332. content: "";
  2333. height: 16px;
  2334. width: 16px;
  2335. left: 4px;
  2336. bottom: 4px;
  2337. background-color: white;
  2338. transition: .4s;
  2339. }
  2340. input:checked + .slider {
  2341. background-color: #4CAF50;
  2342. }
  2343. input:focus + .slider {
  2344. box-shadow: 0 0 1px #4CAF50;
  2345. }
  2346. input:checked + .slider:before {
  2347. transform: translateX(26px);
  2348. }
  2349. /* Rounded sliders */
  2350. .slider.round {
  2351. border-radius: 24px;
  2352. }
  2353. .slider.round:before {
  2354. border-radius: 50%;
  2355. }
  2356. `;
  2357. document.head.appendChild(toggleStyle);
  2358. // Update auto run status when checkbox changes
  2359. $('#autoRun').on('change', function() {
  2360. const isChecked = this.checked;
  2361. myVars.autoRun = isChecked;
  2362. myFunctions.updateAutoRunStatus(isChecked ? 'on' : 'off');
  2363. });
  2364.  
  2365. $('#fusionMode').on('change', function() {
  2366. const isChecked = this.checked;
  2367. myFunctions.updateFusionMode(isChecked);
  2368. // Disable the ELO slider when fusion mode is enabled
  2369. $('#eloSlider').prop('disabled', isChecked);
  2370. // Extract opponent rating immediately when enabled
  2371. if (isChecked) {
  2372. extractOpponentRating();
  2373. }
  2374. // Disable human mode when fusion mode is enabled
  2375. if (isChecked && $('#humanMode').prop('checked')) {
  2376. $('#humanMode').prop('checked', false);
  2377. myFunctions.updateHumanMode(false);
  2378. }
  2379. });
  2380.  
  2381. // Human mode toggle event listener
  2382. $('#humanMode').on('change', function() {
  2383. const isChecked = this.checked;
  2384. myFunctions.updateHumanMode(isChecked);
  2385. // Disable the ELO slider when human mode is enabled
  2386. $('#eloSlider').prop('disabled', isChecked);
  2387. // Disable fusion mode when human mode is enabled
  2388. if (isChecked && $('#fusionMode').prop('checked')) {
  2389. $('#fusionMode').prop('checked', false);
  2390. myFunctions.updateFusionMode(false);
  2391. }
  2392. // Apply the selected human mode level
  2393. if (isChecked) {
  2394. const level = $('#humanModeSelect').val();
  2395. setHumanMode(level);
  2396. }
  2397. });
  2398. // Human mode level select event listener
  2399. $('#humanModeSelect').on('change', function() {
  2400. const level = $(this).val();
  2401. // Only apply if human mode is active
  2402. if ($('#humanMode').prop('checked')) {
  2403. setHumanMode(level);
  2404. }
  2405. });
  2406. // Human mode info button event listener
  2407. $('#humanModeInfoBtn').on('click', function() {
  2408. document.getElementById('humanModeInfoModal').style.display = 'flex';
  2409. });
  2410. $('#autoMove').on('change', function() {
  2411. myVars.autoMove = this.checked;
  2412. // Visual feedback for auto move toggle
  2413. if (this.checked) {
  2414. $(this).parent().append('<span id="autoMoveStatus" style="margin-left: 10px; font-size: 12px; color: #4CAF50;">On</span>');
  2415. } else {
  2416. $('#autoMoveStatus').remove();
  2417. }
  2418. });
  2419.  
  2420. $('#showArrows').on('change', function() {
  2421. myVars.showArrows = this.checked;
  2422. });
  2423.  
  2424. $('#persistentHighlights').on('change', function() {
  2425. myVars.persistentHighlights = this.checked;
  2426. // If turning off persistent highlights, clear any existing ones
  2427. if (!myVars.persistentHighlights) {
  2428. myFunctions.clearHighlights();
  2429. }
  2430. });
  2431. // Add event listeners for the move indicator type radio buttons
  2432. $('input[name="moveIndicatorType"]').on('change', function() {
  2433. myVars.moveIndicatorType = this.value;
  2434. // Clear any existing highlights and arrows when changing the indicator type
  2435. myFunctions.clearHighlights();
  2436. myFunctions.clearArrows();
  2437. });
  2438. // Improved visual feedback for toggle switches
  2439. $('.switch input[type="checkbox"]').each(function() {
  2440. const statusElement = $('#' + this.id + 'Status');
  2441. if (statusElement.length) {
  2442. if (this.checked) {
  2443. statusElement.text('On');
  2444. statusElement.css('color', '#4CAF50');
  2445. } else {
  2446. statusElement.text('Off');
  2447. statusElement.css('color', '#666');
  2448. }
  2449. }
  2450. });
  2451. // Add visual feedback to buttons
  2452. $('#runEngineBtn, #stopEngineBtn, #saveSettingsBtn, #showKeyboardShortcuts, #applyDepth').each(function() {
  2453. $(this).css('transition', 'all 0.2s ease');
  2454. $(this).hover(
  2455. function() {
  2456. $(this).css({
  2457. 'opacity': '0.9',
  2458. 'transform': 'translateY(-1px)',
  2459. 'box-shadow': '0 2px 5px rgba(0,0,0,0.2)'
  2460. });
  2461. },
  2462. function() {
  2463. $(this).css({
  2464. 'opacity': '1',
  2465. 'transform': 'translateY(0)',
  2466. 'box-shadow': 'none'
  2467. });
  2468. }
  2469. );
  2470. $(this).mousedown(function() {
  2471. $(this).css('transform', 'translateY(1px)');
  2472. });
  2473. $(this).mouseup(function() {
  2474. $(this).css('transform', 'translateY(-1px)');
  2475. });
  2476. });
  2477. // Improve color theme selector
  2478. $('#evalBarColor').on('change', function() {
  2479. if (this.value === 'custom') {
  2480. $('#customColorContainer').slideDown(200);
  2481. } else {
  2482. $('#customColorContainer').slideUp(200);
  2483. }
  2484. });
  2485. // Add tooltips to buttons and controls
  2486. $('#runEngineBtn').attr('title', 'Analyze the current position with the chess engine');
  2487. $('#stopEngineBtn').attr('title', 'Stop the engine analysis');
  2488. $('#saveSettingsBtn').attr('title', 'Save your current settings for future sessions');
  2489. $('#depthSlider').attr('title', 'Higher depth = stronger analysis but slower calculation');
  2490. $('#showArrows').attr('title', 'Display arrows showing the best moves on the board');
  2491. $('#persistentHighlights').attr('title', 'Keep move highlights visible until the next move is made');
  2492. $('#autoRun').attr('title', 'Automatically run the engine after each move');
  2493. $('#autoMove').attr('title', 'Automatically make the best move for your side');
  2494. $('#timeDelayMin').attr('title', 'Minimum delay before auto-running the engine');
  2495. $('#timeDelayMax').attr('title', 'Maximum delay before auto-running the engine');
  2496. // Close modals when clicking outside
  2497. $('.modal-container').on('click', function(event) {
  2498. if (event.target === this) {
  2499. $(this).css('display', 'none');
  2500. }
  2501. });
  2502. // Add escape key to close modals
  2503. $(document).on('keydown', function(event) {
  2504. if (event.key === 'Escape') {
  2505. $('.modal-container').css('display', 'none');
  2506. }
  2507. });
  2508. // Add class to modals for easier selection
  2509. $('#keyboardShortcutsModal, #eloInfoModal, #humanModeInfoModal').addClass('modal-container');
  2510.  
  2511. loaded = true;
  2512. } catch (error) {console.log(error)}
  2513. }
  2514.  
  2515.  
  2516. function other(delay){
  2517. console.log(`Scheduling next auto run in ${delay/1000} seconds`);
  2518. myFunctions.updateAutoRunStatus('waiting');
  2519. // Use setTimeout instead of setInterval with constant checking
  2520. setTimeout(() => {
  2521. // Only proceed if auto run is still enabled
  2522. if(myVars.autoRun && myTurn && !isThinking) {
  2523. myFunctions.autoRun(lastValue);
  2524. }
  2525. canGo = true;
  2526. }, delay);
  2527. }
  2528.  
  2529.  
  2530. async function getVersion(){
  2531. try {
  2532. const response = await fetch('https://gf.qytechs.cn/en/scripts/531171-chess-ai');
  2533. const html = await response.text();
  2534. const parser = new DOMParser();
  2535. const doc = parser.parseFromString(html, 'text/html');
  2536. const versionElement = doc.querySelector('dd.script-show-version span');
  2537. const version = versionElement.textContent;
  2538.  
  2539. console.log("Fetched version:", version);
  2540. console.log("Current version:", currentVersion);
  2541.  
  2542. if(currentVersion !== version){
  2543. console.log("Version mismatch detected!");
  2544. if (document.hasFocus()) {
  2545. alert('UPDATE THIS SCRIPT IN ORDER TO PROCEED!');
  2546. window.open('https://gf.qytechs.cn/en/scripts/531171-chess-ai', '_blank');
  2547. }
  2548. // Recursive call to keep displaying the popup
  2549. setTimeout(getVersion, 1000); // Call again after 1 second
  2550. } else {
  2551. console.log("Version check passed");
  2552. }
  2553. } catch (error) {
  2554. console.error("Error fetching version:", error);
  2555. // Recursive call to keep trying to fetch the version
  2556. setTimeout(getVersion, 1000); // Call again after 1 second
  2557. }
  2558. }
  2559.  
  2560. getVersion();
  2561.  
  2562.  
  2563. const waitForChessBoard = setInterval(() => {
  2564. if(loaded) {
  2565. board = $('chess-board')[0] || $('wc-chess-board')[0];
  2566. // Only update these values when needed, not every 100ms
  2567. if($('#autoRun')[0]) myVars.autoRun = $('#autoRun')[0].checked;
  2568. if($('#autoMove')[0]) myVars.autoMove = $('#autoMove')[0].checked;
  2569. if($('#showArrows')[0]) myVars.showArrows = $('#showArrows')[0].checked;
  2570. // Update move indicator type if radio buttons exist
  2571. if($('input[name="moveIndicatorType"]:checked')[0]) {
  2572. myVars.moveIndicatorType = $('input[name="moveIndicatorType"]:checked')[0].value;
  2573. }
  2574. // Check if turn has changed
  2575. const currentTurn = board.game.getTurn() == board.game.getPlayingAs();
  2576. const turnChanged = currentTurn !== myTurn;
  2577. myTurn = currentTurn;
  2578. // Only update delay values when needed
  2579. if($('#timeDelayMin')[0] && $('#timeDelayMax')[0]) {
  2580. let minDel = parseFloat($('#timeDelayMin')[0].value);
  2581. let maxDel = parseFloat($('#timeDelayMax')[0].value);
  2582. myVars.delay = Math.random() * (maxDel - minDel) + minDel;
  2583. }
  2584. myVars.isThinking = isThinking;
  2585. myFunctions.spinner();
  2586. // If turn has changed to player's turn and auto run is enabled, trigger auto run
  2587. if(turnChanged && myTurn && myVars.autoRun && canGo && !isThinking) {
  2588. console.log("Turn changed to player's turn, triggering auto run");
  2589. canGo = false;
  2590. var currentDelay = myVars.delay != undefined ? myVars.delay * 1000 : 10;
  2591. other(currentDelay);
  2592. }
  2593. // Update evaluation bar position if board size changes
  2594. if(evalBar && evalText && board) {
  2595. evalText.style.left = `${evalBar.offsetLeft}px`;
  2596. }
  2597. } else {
  2598. myFunctions.loadEx();
  2599. }
  2600.  
  2601.  
  2602. if(!engine.engine){
  2603. myFunctions.loadChessEngine();
  2604. }
  2605.  
  2606. // Check if the board exists and we haven't set up the move listener yet
  2607. if (board && !board._highlightListenerAdded) {
  2608. // Try to add a listener for moves
  2609. try {
  2610. // Store the current position FEN to detect changes
  2611. myVars.lastPositionFEN = board.game.getFEN();
  2612. // Mark that we've added the listener
  2613. board._highlightListenerAdded = true;
  2614. } catch (err) {
  2615. console.log('Error setting up move listener:', err);
  2616. }
  2617. }
  2618. // Check if the position has changed (a move was made)
  2619. if (board && myVars.lastPositionFEN) {
  2620. const currentFEN = board.game.getFEN();
  2621. if (currentFEN !== myVars.lastPositionFEN) {
  2622. // Position changed, clear highlights and arrows
  2623. myFunctions.clearHighlights();
  2624. myFunctions.clearArrows();
  2625. myVars.lastPositionFEN = currentFEN;
  2626. }
  2627. }
  2628. }, 100);
  2629.  
  2630. // Function to save user settings using GM.setValue asynchronously
  2631. myFunctions.saveSettings = async function() {
  2632. const settings = {
  2633. eloRating: myVars.eloRating,
  2634. depth: parseInt($('#depthSlider')[0].value),
  2635. showArrows: $('#showArrows')[0].checked,
  2636. persistentHighlights: $('#persistentHighlights')[0].checked,
  2637. moveIndicatorType: myVars.moveIndicatorType || 'highlights',
  2638. autoRun: $('#autoRun')[0].checked,
  2639. autoMove: $('#autoMove')[0].checked,
  2640. timeDelayMin: parseFloat($('#timeDelayMin')[0].value),
  2641. timeDelayMax: parseFloat($('#timeDelayMax')[0].value),
  2642. evalBarTheme: $('#evalBarColor').val(),
  2643. whiteAdvantageColor: $('#whiteAdvantageColor').val(),
  2644. blackAdvantageColor: $('#blackAdvantageColor').val(),
  2645. fusionMode: myVars.fusionMode,
  2646. humanMode: myVars.humanMode ? {
  2647. active: myVars.humanMode.active,
  2648. level: myVars.humanMode.level
  2649. } : { active: false, level: 'intermediate' }
  2650. };
  2651.  
  2652. try {
  2653. await GM.setValue('chessAISettings', JSON.stringify(settings));
  2654. // Show saved notification (same as before)
  2655. const notification = document.createElement('div');
  2656. notification.textContent = 'Settings saved!';
  2657. notification.style = `
  2658. position: fixed;
  2659. bottom: 20px;
  2660. right: 20px;
  2661. background-color: #4CAF50;
  2662. color: white;
  2663. padding: 10px 20px;
  2664. border-radius: 4px;
  2665. z-index: 9999;
  2666. opacity: 0;
  2667. transition: opacity 0.3s;
  2668. `;
  2669. document.body.appendChild(notification);
  2670.  
  2671. setTimeout(() => {
  2672. notification.style.opacity = '1';
  2673. }, 10);
  2674.  
  2675. setTimeout(() => {
  2676. notification.style.opacity = '0';
  2677. setTimeout(() => {
  2678. document.body.removeChild(notification);
  2679. }, 300);
  2680. }, 2000);
  2681. } catch (error) {
  2682. console.error('Error saving settings:', error);
  2683. // Handle error as needed, e.g., show an error notification
  2684. }
  2685. };
  2686. // Function to load user settings using await GM.getValue
  2687. myFunctions.loadSettings = async function() {
  2688. try {
  2689. // First try to load settings from the combined JSON
  2690. const savedSettings = await GM.getValue('chessAISettings', null);
  2691. if (savedSettings) {
  2692. // If settings exist as JSON, parse and apply them
  2693. const settings = JSON.parse(savedSettings);
  2694. // Apply saved settings to myVars
  2695. myVars.eloRating = settings.eloRating || 1500;
  2696. myVars.depth = settings.depth || 11;
  2697. myVars.showArrows = settings.showArrows !== undefined ? settings.showArrows : true;
  2698. myVars.persistentHighlights = settings.persistentHighlights !== undefined ? settings.persistentHighlights : true;
  2699. myVars.moveIndicatorType = settings.moveIndicatorType || 'highlights';
  2700. myVars.autoRun = settings.autoRun !== undefined ? settings.autoRun : false;
  2701. myVars.autoMove = settings.autoMove !== undefined ? settings.autoMove : false;
  2702. myVars.fusionMode = settings.fusionMode !== undefined ? settings.fusionMode : false;
  2703. myVars.whiteAdvantageColor = settings.whiteAdvantageColor || '#4CAF50';
  2704. myVars.blackAdvantageColor = settings.blackAdvantageColor || '#F44336';
  2705. // Set humanMode
  2706. if (settings.humanMode) {
  2707. myVars.humanMode = {
  2708. active: settings.humanMode.active,
  2709. level: settings.humanMode.level
  2710. };
  2711. } else {
  2712. myVars.humanMode = { active: false, level: 'intermediate' };
  2713. }
  2714. // Update UI elements
  2715. if ($('#depthSlider')[0]) {
  2716. $('#depthSlider')[0].value = myVars.depth;
  2717. $('#depthText').html('Current Depth: <strong>' + myVars.depth + '</strong>');
  2718. }
  2719. if ($('#eloSlider')[0]) {
  2720. $('#eloSlider')[0].value = myVars.eloRating;
  2721. $('#eloText').html('Current ELO: <strong>' + myVars.eloRating + '</strong>');
  2722. }
  2723. if ($('#autoMove')[0]) {
  2724. $('#autoMove')[0].checked = myVars.autoMove;
  2725. }
  2726. if ($('#autoRun')[0]) {
  2727. $('#autoRun')[0].checked = myVars.autoRun;
  2728. }
  2729. if ($('#showArrows')[0]) {
  2730. $('#showArrows')[0].checked = myVars.showArrows;
  2731. }
  2732. if ($('#persistentHighlights')[0]) {
  2733. $('#persistentHighlights')[0].checked = myVars.persistentHighlights;
  2734. }
  2735. if ($('input[name="moveIndicatorType"]').length) {
  2736. $('input[name="moveIndicatorType"][value="' + myVars.moveIndicatorType + '"]').prop('checked', true);
  2737. }
  2738. if ($('#humanMode')[0] && myVars.humanMode) {
  2739. $('#humanMode')[0].checked = myVars.humanMode.active;
  2740. }
  2741. if ($('#humanLevelSelect')[0] && myVars.humanMode) {
  2742. $('#humanLevelSelect')[0].value = myVars.humanMode.level;
  2743. }
  2744. if ($('#fusionMode')[0]) {
  2745. $('#fusionMode')[0].checked = myVars.fusionMode;
  2746. }
  2747. if ($('#whiteAdvantageColor')[0]) {
  2748. $('#whiteAdvantageColor')[0].value = myVars.whiteAdvantageColor;
  2749. }
  2750. if ($('#blackAdvantageColor')[0]) {
  2751. $('#blackAdvantageColor')[0].value = myVars.blackAdvantageColor;
  2752. }
  2753. if (settings.timeDelayMin !== undefined && $('#timeDelayMin')[0]) {
  2754. $('#timeDelayMin')[0].value = settings.timeDelayMin;
  2755. }
  2756. if (settings.timeDelayMax !== undefined && $('#timeDelayMax')[0]) {
  2757. $('#timeDelayMax')[0].value = settings.timeDelayMax;
  2758. }
  2759. if (settings.evalBarTheme && $('#evalBarColor')[0]) {
  2760. $('#evalBarColor').val(settings.evalBarTheme);
  2761. if (settings.evalBarTheme === 'custom') {
  2762. $('#customColorContainer').show();
  2763. }
  2764. }
  2765. } else {
  2766. // Fallback to old method for backward compatibility
  2767. const savedDepth = await GM.getValue('depth', 11);
  2768. const savedElo = await GM.getValue('elo', 1500);
  2769. const savedAutoMove = await GM.getValue('autoMove', false);
  2770. const savedAutoRun = await GM.getValue('autoRun', false);
  2771. const savedShowArrows = await GM.getValue('showArrows', true);
  2772. const savedPersistentHighlights = await GM.getValue('persistentHighlights', true);
  2773. const savedMoveIndicatorType = await GM.getValue('moveIndicatorType', 'highlights');
  2774. const savedHumanMode = await GM.getValue('humanMode', false);
  2775. const savedHumanLevel = await GM.getValue('humanLevel', 'intermediate');
  2776. const savedFusionMode = await GM.getValue('fusionMode', false);
  2777. const savedWhiteAdvantageColor = await GM.getValue('whiteAdvantageColor', '#4CAF50');
  2778. const savedBlackAdvantageColor = await GM.getValue('blackAdvantageColor', '#F44336');
  2779. // Apply saved settings
  2780. myVars.depth = savedDepth;
  2781. myVars.eloRating = savedElo;
  2782. myVars.autoMove = savedAutoMove;
  2783. myVars.autoRun = savedAutoRun;
  2784. myVars.showArrows = savedShowArrows;
  2785. myVars.persistentHighlights = savedPersistentHighlights;
  2786. myVars.moveIndicatorType = savedMoveIndicatorType;
  2787. myVars.humanMode = { active: savedHumanMode, level: savedHumanLevel };
  2788. myVars.fusionMode = savedFusionMode;
  2789. myVars.whiteAdvantageColor = savedWhiteAdvantageColor;
  2790. myVars.blackAdvantageColor = savedBlackAdvantageColor;
  2791. // Update UI elements to match saved settings
  2792. if ($('#depthSlider')[0]) {
  2793. $('#depthSlider')[0].value = savedDepth;
  2794. $('#depthText').html('Current Depth: <strong>' + savedDepth + '</strong>');
  2795. }
  2796. if ($('#eloSlider')[0]) {
  2797. $('#eloSlider')[0].value = savedElo;
  2798. $('#eloText').html('Current ELO: <strong>' + savedElo + '</strong>');
  2799. }
  2800. if ($('#autoMove')[0]) {
  2801. $('#autoMove')[0].checked = savedAutoMove;
  2802. }
  2803. if ($('#autoRun')[0]) {
  2804. $('#autoRun')[0].checked = savedAutoRun;
  2805. }
  2806. if ($('#showArrows')[0]) {
  2807. $('#showArrows')[0].checked = savedShowArrows;
  2808. }
  2809. if ($('#persistentHighlights')[0]) {
  2810. $('#persistentHighlights')[0].checked = savedPersistentHighlights;
  2811. }
  2812. if ($('input[name="moveIndicatorType"]').length) {
  2813. $('input[name="moveIndicatorType"][value="' + savedMoveIndicatorType + '"]').prop('checked', true);
  2814. }
  2815. if ($('#humanMode')[0]) {
  2816. $('#humanMode')[0].checked = savedHumanMode;
  2817. }
  2818. if ($('#humanLevelSelect')[0]) {
  2819. $('#humanLevelSelect')[0].value = savedHumanLevel;
  2820. }
  2821. if ($('#fusionMode')[0]) {
  2822. $('#fusionMode')[0].checked = savedFusionMode;
  2823. }
  2824. if ($('#whiteAdvantageColor')[0]) {
  2825. $('#whiteAdvantageColor')[0].value = savedWhiteAdvantageColor;
  2826. }
  2827. if ($('#blackAdvantageColor')[0]) {
  2828. $('#blackAdvantageColor')[0].value = savedBlackAdvantageColor;
  2829. }
  2830. // After loading the settings from individual values, save them as a combined object
  2831. // This will migrate users to the new format
  2832. myFunctions.saveSettings();
  2833. }
  2834. // Check for first run (always use individual setting for this)
  2835. const savedFirstRun = await GM.getValue('firstRun', true);
  2836. // Show welcome modal for first-time users
  2837. if (savedFirstRun) {
  2838. setTimeout(() => {
  2839. myFunctions.showWelcomeModal();
  2840. GM.setValue('firstRun', false);
  2841. }, 1000);
  2842. }
  2843. console.log('Settings loaded successfully');
  2844. } catch (error) {
  2845. console.error('Error loading settings:', error);
  2846. }
  2847. }
  2848. // Function to show welcome modal for first-time users
  2849. function showWelcomeModal() {
  2850. // Create welcome modal
  2851. const welcomeModal = document.createElement('div');
  2852. welcomeModal.id = 'welcomeModal';
  2853. welcomeModal.style = `
  2854. display: flex;
  2855. position: fixed;
  2856. top: 0;
  2857. left: 0;
  2858. width: 100%;
  2859. height: 100%;
  2860. background-color: rgba(0,0,0,0.7);
  2861. z-index: 2000;
  2862. justify-content: center;
  2863. align-items: center;
  2864. `;
  2865. const modalContent = document.createElement('div');
  2866. modalContent.style = `
  2867. background-color: white;
  2868. padding: 30px;
  2869. border-radius: 8px;
  2870. max-width: 600px;
  2871. max-height: 80vh;
  2872. overflow-y: auto;
  2873. position: relative;
  2874. box-shadow: 0 4px 20px rgba(0,0,0,0.2);
  2875. `;
  2876. const closeBtn = document.createElement('span');
  2877. closeBtn.innerHTML = '&times;';
  2878. closeBtn.style = `
  2879. position: absolute;
  2880. top: 10px;
  2881. right: 15px;
  2882. font-size: 24px;
  2883. cursor: pointer;
  2884. color: #333;
  2885. transition: color 0.2s;
  2886. `;
  2887. closeBtn.onmouseover = function() {
  2888. this.style.color = '#F44336';
  2889. };
  2890. closeBtn.onmouseout = function() {
  2891. this.style.color = '#333';
  2892. };
  2893. closeBtn.onclick = function() {
  2894. welcomeModal.style.display = 'none';
  2895. };
  2896. modalContent.appendChild(closeBtn);
  2897. // Welcome content
  2898. const welcomeTitle = document.createElement('h2');
  2899. welcomeTitle.textContent = 'Welcome to Chess AI!';
  2900. welcomeTitle.style = 'margin-top: 0; color: #2196F3; border-bottom: 2px solid #eee; padding-bottom: 10px;';
  2901. modalContent.appendChild(welcomeTitle);
  2902. const welcomeText = document.createElement('p');
  2903. welcomeText.textContent = 'Thank you for installing Chess AI. This tool helps you analyze chess positions and find the best moves during your games on Chess.com.';
  2904. welcomeText.style = 'margin-bottom: 20px; color: #666;';
  2905. modalContent.appendChild(welcomeText);
  2906. // Quick start guide
  2907. const quickStartTitle = document.createElement('h3');
  2908. quickStartTitle.textContent = 'Quick Start Guide';
  2909. quickStartTitle.style = 'color: #4CAF50; margin-bottom: 15px;';
  2910. modalContent.appendChild(quickStartTitle);
  2911. const steps = [
  2912. { title: 'Run the Engine', content: 'Press any key from Q to M to run the engine at different depths. Higher depths give stronger analysis but take longer.' },
  2913. { title: 'View Best Moves', content: 'The best moves will be highlighted on the board, and the evaluation bar will show who has the advantage.' },
  2914. { title: 'Adjust Settings', content: 'Click the settings icon to customize the engine strength, visual indicators, and auto-play options.' },
  2915. { title: 'Keyboard Shortcuts', content: 'Use keyboard shortcuts for quick access. Press the "Keyboard Shortcuts" button to see all available shortcuts.' }
  2916. ];
  2917. const stepsList = document.createElement('div');
  2918. stepsList.style = 'margin-bottom: 25px;';
  2919. steps.forEach((step, index) => {
  2920. const stepItem = document.createElement('div');
  2921. stepItem.style = 'margin-bottom: 15px; display: flex;';
  2922. const stepNumber = document.createElement('div');
  2923. stepNumber.textContent = (index + 1);
  2924. stepNumber.style = `
  2925. width: 25px;
  2926. height: 25px;
  2927. background-color: #2196F3;
  2928. color: white;
  2929. border-radius: 50%;
  2930. display: flex;
  2931. justify-content: center;
  2932. align-items: center;
  2933. margin-right: 15px;
  2934. flex-shrink: 0;
  2935. font-weight: bold;
  2936. `;
  2937. const stepContent = document.createElement('div');
  2938. const stepTitle = document.createElement('div');
  2939. stepTitle.textContent = step.title;
  2940. stepTitle.style = 'font-weight: bold; margin-bottom: 5px;';
  2941. const stepDescription = document.createElement('div');
  2942. stepDescription.textContent = step.content;
  2943. stepDescription.style = 'color: #666;';
  2944. stepContent.appendChild(stepTitle);
  2945. stepContent.appendChild(stepDescription);
  2946. stepItem.appendChild(stepNumber);
  2947. stepItem.appendChild(stepContent);
  2948. stepsList.appendChild(stepItem);
  2949. });
  2950. modalContent.appendChild(stepsList);
  2951. // Tips section
  2952. const tipsTitle = document.createElement('h3');
  2953. tipsTitle.textContent = 'Pro Tips';
  2954. tipsTitle.style = 'color: #FF9800; margin-bottom: 15px;';
  2955. modalContent.appendChild(tipsTitle);
  2956. const tipsList = document.createElement('ul');
  2957. tipsList.style = 'margin-bottom: 25px; padding-left: 20px;';
  2958. const tips = [
  2959. 'Use depths 1-10 for quick analysis and casual play.',
  2960. 'Use depths 15+ for serious analysis and difficult positions.',
  2961. 'Enable "Auto Move" to automatically play the best move.',
  2962. 'Try "Human Mode" to get more natural, human-like suggestions.',
  2963. 'Customize the evaluation bar colors in the Visual tab.'
  2964. ];
  2965. tips.forEach(tip => {
  2966. const tipItem = document.createElement('li');
  2967. tipItem.textContent = tip;
  2968. tipItem.style = 'margin-bottom: 8px; color: #666;';
  2969. tipsList.appendChild(tipItem);
  2970. });
  2971. modalContent.appendChild(tipsList);
  2972. // Get started button
  2973. const getStartedBtn = document.createElement('button');
  2974. getStartedBtn.textContent = 'Get Started';
  2975. getStartedBtn.style = `
  2976. width: 100%;
  2977. padding: 12px;
  2978. background-color: #4CAF50;
  2979. color: white;
  2980. border: none;
  2981. border-radius: 4px;
  2982. cursor: pointer;
  2983. font-weight: bold;
  2984. font-size: 16px;
  2985. transition: background-color 0.2s;
  2986. `;
  2987. getStartedBtn.onmouseover = function() {
  2988. this.style.backgroundColor = '#45a049';
  2989. };
  2990. getStartedBtn.onmouseout = function() {
  2991. this.style.backgroundColor = '#4CAF50';
  2992. };
  2993. getStartedBtn.onclick = function() {
  2994. welcomeModal.style.display = 'none';
  2995. };
  2996. modalContent.appendChild(getStartedBtn);
  2997. welcomeModal.appendChild(modalContent);
  2998. document.body.appendChild(welcomeModal);
  2999. }
  3000.  
  3001. // Create a move history display
  3002. myFunctions.createMoveHistoryDisplay = function() {
  3003. // Create container for move history
  3004. const moveHistoryContainer = document.createElement('div');
  3005. moveHistoryContainer.id = 'moveHistoryContainer';
  3006. moveHistoryContainer.style = `
  3007. margin-top: 15px;
  3008. border: 1px solid #ccc;
  3009. border-radius: 4px;
  3010. padding: 10px;
  3011. max-height: 200px;
  3012. overflow-y: auto;
  3013. `;
  3014. // Create header
  3015. const header = document.createElement('h3');
  3016. header.textContent = 'Engine Move History';
  3017. header.style = `
  3018. margin-top: 0;
  3019. margin-bottom: 10px;
  3020. font-size: 16px;
  3021. color: #333;
  3022. `;
  3023. // Create table for moves
  3024. const moveTable = document.createElement('table');
  3025. moveTable.id = 'moveHistoryTable';
  3026. moveTable.style = `
  3027. width: 100%;
  3028. border-collapse: collapse;
  3029. `;
  3030. // Create table header
  3031. const tableHeader = document.createElement('thead');
  3032. tableHeader.innerHTML = `
  3033. <tr>
  3034. <th style="text-align: left; padding: 5px; border-bottom: 1px solid #ddd;">Move</th>
  3035. <th style="text-align: left; padding: 5px; border-bottom: 1px solid #ddd;">Eval</th>
  3036. <th style="text-align: left; padding: 5px; border-bottom: 1px solid #ddd;">Depth</th>
  3037. </tr>
  3038. `;
  3039. // Create table body
  3040. const tableBody = document.createElement('tbody');
  3041. tableBody.id = 'moveHistoryTableBody';
  3042. // Assemble the components
  3043. moveTable.appendChild(tableHeader);
  3044. moveTable.appendChild(tableBody);
  3045. moveHistoryContainer.appendChild(header);
  3046. moveHistoryContainer.appendChild(moveTable);
  3047. // Add clear history button
  3048. const clearButton = document.createElement('button');
  3049. clearButton.textContent = 'Clear History';
  3050. clearButton.style = `
  3051. margin-top: 10px;
  3052. padding: 5px 10px;
  3053. background-color: #f44336;
  3054. color: white;
  3055. border: none;
  3056. border-radius: 4px;
  3057. cursor: pointer;
  3058. `;
  3059. clearButton.onclick = function() {
  3060. document.getElementById('moveHistoryTableBody').innerHTML = '';
  3061. };
  3062. moveHistoryContainer.appendChild(clearButton);
  3063. return moveHistoryContainer;
  3064. };
  3065. // Add a move to the history
  3066. myFunctions.addMoveToHistory = function(move, evaluation, depth) {
  3067. const tableBody = document.getElementById('moveHistoryTableBody');
  3068. if (!tableBody) return;
  3069. const row = document.createElement('tr');
  3070. // Format the evaluation
  3071. let evalText = '';
  3072. if (typeof evaluation === 'string' && evaluation.includes('Mate')) {
  3073. evalText = evaluation;
  3074. } else {
  3075. const sign = evaluation > 0 ? '+' : '';
  3076. evalText = `${sign}${parseFloat(evaluation).toFixed(2)}`;
  3077. }
  3078. row.innerHTML = `
  3079. <td style="padding: 5px; border-bottom: 1px solid #ddd;">${move}</td>
  3080. <td style="padding: 5px; border-bottom: 1px solid #ddd;">${evalText}</td>
  3081. <td style="padding: 5px; border-bottom: 1px solid #ddd;">${depth}</td>
  3082. `;
  3083. // Add the new row at the top
  3084. if (tableBody.firstChild) {
  3085. tableBody.insertBefore(row, tableBody.firstChild);
  3086. } else {
  3087. tableBody.appendChild(row);
  3088. }
  3089. // Limit the number of rows to 50
  3090. while (tableBody.children.length > 50) {
  3091. tableBody.removeChild(tableBody.lastChild);
  3092. }
  3093. };
  3094.  
  3095. // Function to update the auto run status indicator
  3096. myFunctions.updateAutoRunStatus = function(status) {
  3097. if (!$('#autoRunStatus')[0]) return;
  3098. switch(status) {
  3099. case 'on':
  3100. $('#autoRunStatus').text('On');
  3101. $('#autoRunStatus').css('color', '#4CAF50');
  3102. break;
  3103. case 'off':
  3104. $('#autoRunStatus').text('Off');
  3105. $('#autoRunStatus').css('color', '#666');
  3106. break;
  3107. case 'waiting':
  3108. $('#autoRunStatus').text('Waiting...');
  3109. $('#autoRunStatus').css('color', '#FFA500');
  3110. break;
  3111. case 'running':
  3112. $('#autoRunStatus').text('Running...');
  3113. $('#autoRunStatus').css('color', '#2196F3');
  3114. break;
  3115. }
  3116. };
  3117.  
  3118. // Function to evaluate the complexity of the board position
  3119. function evaluateBoardComplexity(boardState) {
  3120. let complexity = 0;
  3121.  
  3122. // Example evaluation criteria
  3123. const pieceValues = {
  3124. 'p': 1, // Pawn
  3125. 'r': 5, // Rook
  3126. 'n': 3, // Knight
  3127. 'b': 3, // Bishop
  3128. 'q': 9, // Queen
  3129. 'k': 0, // King (not counted)
  3130. };
  3131.  
  3132. // Loop through the board state to evaluate material balance
  3133. for (let row = 0; row < 8; row++) {
  3134. for (let col = 0; col < 8; col++) {
  3135. const piece = boardState[row][col];
  3136. if (piece) {
  3137. const value = pieceValues[piece.toLowerCase()] || 0;
  3138. complexity += piece === piece.toUpperCase() ? value : -value; // Add for white, subtract for black
  3139. }
  3140. }
  3141. }
  3142.  
  3143. // Additional complexity factors can be added here
  3144. // For example, consider piece activity, control of the center, etc.
  3145. // This is a simple example; you can expand it based on your needs
  3146.  
  3147. // Normalize complexity to a reasonable range
  3148. complexity = Math.abs(complexity); // Ensure it's positive
  3149. return Math.floor(complexity / 10); // Scale down for thinking time calculation
  3150. }
  3151.  
  3152. myFunctions.extractOpponentRating = function() {
  3153. // Try to find the opponent's rating
  3154. try {
  3155. const ratingElements = document.querySelectorAll('.user-tagline-rating');
  3156. if (ratingElements.length >= 2) {
  3157. // Find the element that doesn't match the player's username
  3158. const playerUsername = document.querySelector('.user-username-component')?.textContent.trim();
  3159. for (const element of ratingElements) {
  3160. const usernameElement = element.closest('.user-tagline')?.querySelector('.user-username-component');
  3161. if (usernameElement && usernameElement.textContent.trim() !== playerUsername) {
  3162. const rating = parseInt(element.textContent.trim());
  3163. if (!isNaN(rating)) {
  3164. console.log(`Opponent rating detected: ${rating}`);
  3165. return rating;
  3166. }
  3167. }
  3168. }
  3169. }
  3170. } catch (error) {
  3171. console.error('Error extracting opponent rating:', error);
  3172. }
  3173. return null;
  3174. }
  3175. // Function to show welcome modal for first-time users
  3176. myFunctions.showWelcomeModal = function() {
  3177. // Create welcome modal
  3178. const welcomeModal = document.createElement('div');
  3179. welcomeModal.id = 'welcomeModal';
  3180. welcomeModal.style = `
  3181. display: flex;
  3182. position: fixed;
  3183. top: 0;
  3184. left: 0;
  3185. width: 100%;
  3186. height: 100%;
  3187. background-color: rgba(0,0,0,0.7);
  3188. z-index: 2000;
  3189. justify-content: center;
  3190. align-items: center;
  3191. `;
  3192. const modalContent = document.createElement('div');
  3193. modalContent.style = `
  3194. background-color: white;
  3195. padding: 30px;
  3196. border-radius: 8px;
  3197. max-width: 600px;
  3198. max-height: 80vh;
  3199. overflow-y: auto;
  3200. position: relative;
  3201. box-shadow: 0 4px 20px rgba(0,0,0,0.2);
  3202. `;
  3203. const closeBtn = document.createElement('span');
  3204. closeBtn.innerHTML = '&times;';
  3205. closeBtn.style = `
  3206. position: absolute;
  3207. top: 10px;
  3208. right: 15px;
  3209. font-size: 24px;
  3210. cursor: pointer;
  3211. color: #333;
  3212. transition: color 0.2s;
  3213. `;
  3214. closeBtn.onmouseover = function() {
  3215. this.style.color = '#F44336';
  3216. };
  3217. closeBtn.onmouseout = function() {
  3218. this.style.color = '#333';
  3219. };
  3220. closeBtn.onclick = function() {
  3221. welcomeModal.style.display = 'none';
  3222. };
  3223. modalContent.appendChild(closeBtn);
  3224. // Welcome content
  3225. const welcomeTitle = document.createElement('h2');
  3226. welcomeTitle.textContent = 'Welcome to Chess AI!';
  3227. welcomeTitle.style = 'margin-top: 0; color: #2196F3; border-bottom: 2px solid #eee; padding-bottom: 10px;';
  3228. modalContent.appendChild(welcomeTitle);
  3229. const welcomeText = document.createElement('p');
  3230. welcomeText.textContent = 'Thank you for installing Chess AI. This tool helps you analyze chess positions and find the best moves during your games on Chess.com.';
  3231. welcomeText.style = 'margin-bottom: 20px; color: #666;';
  3232. modalContent.appendChild(welcomeText);
  3233. // Quick start guide
  3234. const quickStartTitle = document.createElement('h3');
  3235. quickStartTitle.textContent = 'Quick Start Guide';
  3236. quickStartTitle.style = 'color: #4CAF50; margin-bottom: 15px;';
  3237. modalContent.appendChild(quickStartTitle);
  3238. const steps = [
  3239. { title: 'Run the Engine', content: 'Press any key from Q to M to run the engine at different depths. Higher depths give stronger analysis but take longer.' },
  3240. { title: 'View Best Moves', content: 'The best moves will be highlighted on the board, and the evaluation bar will show who has the advantage.' },
  3241. { title: 'Adjust Settings', content: 'Click the settings icon to customize the engine strength, visual indicators, and auto-play options.' },
  3242. { title: 'Keyboard Shortcuts', content: 'Use keyboard shortcuts for quick access. Press the "Keyboard Shortcuts" button to see all available shortcuts.' }
  3243. ];
  3244. const stepsList = document.createElement('div');
  3245. stepsList.style = 'margin-bottom: 25px;';
  3246. steps.forEach((step, index) => {
  3247. const stepItem = document.createElement('div');
  3248. stepItem.style = 'margin-bottom: 15px; display: flex;';
  3249. const stepNumber = document.createElement('div');
  3250. stepNumber.textContent = (index + 1);
  3251. stepNumber.style = `
  3252. width: 25px;
  3253. height: 25px;
  3254. background-color: #2196F3;
  3255. color: white;
  3256. border-radius: 50%;
  3257. display: flex;
  3258. justify-content: center;
  3259. align-items: center;
  3260. margin-right: 15px;
  3261. flex-shrink: 0;
  3262. font-weight: bold;
  3263. `;
  3264. const stepContent = document.createElement('div');
  3265. const stepTitle = document.createElement('div');
  3266. stepTitle.textContent = step.title;
  3267. stepTitle.style = 'font-weight: bold; margin-bottom: 5px;';
  3268. const stepDescription = document.createElement('div');
  3269. stepDescription.textContent = step.content;
  3270. stepDescription.style = 'color: #666;';
  3271. stepContent.appendChild(stepTitle);
  3272. stepContent.appendChild(stepDescription);
  3273. stepItem.appendChild(stepNumber);
  3274. stepItem.appendChild(stepContent);
  3275. stepsList.appendChild(stepItem);
  3276. });
  3277. modalContent.appendChild(stepsList);
  3278. // Tips section
  3279. const tipsTitle = document.createElement('h3');
  3280. tipsTitle.textContent = 'Pro Tips';
  3281. tipsTitle.style = 'color: #FF9800; margin-bottom: 15px;';
  3282. modalContent.appendChild(tipsTitle);
  3283. const tipsList = document.createElement('ul');
  3284. tipsList.style = 'margin-bottom: 25px; padding-left: 20px;';
  3285. const tips = [
  3286. 'Use depths 1-10 for quick analysis and casual play.',
  3287. 'Use depths 15+ for serious analysis and difficult positions.',
  3288. 'Enable "Auto Move" to automatically play the best move.',
  3289. 'Try "Human Mode" to get more natural, human-like suggestions.',
  3290. 'Customize the evaluation bar colors in the Visual tab.'
  3291. ];
  3292. tips.forEach(tip => {
  3293. const tipItem = document.createElement('li');
  3294. tipItem.textContent = tip;
  3295. tipItem.style = 'margin-bottom: 8px; color: #666;';
  3296. tipsList.appendChild(tipItem);
  3297. });
  3298. modalContent.appendChild(tipsList);
  3299. // Get started button
  3300. const getStartedBtn = document.createElement('button');
  3301. getStartedBtn.textContent = 'Get Started';
  3302. getStartedBtn.style = `
  3303. width: 100%;
  3304. padding: 12px;
  3305. background-color: #4CAF50;
  3306. color: white;
  3307. border: none;
  3308. border-radius: 4px;
  3309. cursor: pointer;
  3310. font-weight: bold;
  3311. font-size: 16px;
  3312. transition: background-color 0.2s;
  3313. `;
  3314. getStartedBtn.onmouseover = function() {
  3315. this.style.backgroundColor = '#45a049';
  3316. };
  3317. getStartedBtn.onmouseout = function() {
  3318. this.style.backgroundColor = '#4CAF50';
  3319. };
  3320. getStartedBtn.onclick = function() {
  3321. welcomeModal.style.display = 'none';
  3322. };
  3323. modalContent.appendChild(getStartedBtn);
  3324. welcomeModal.appendChild(modalContent);
  3325. document.body.appendChild(welcomeModal);
  3326. }
  3327. }
  3328.  
  3329. //Touching below may break the script
  3330.  
  3331. var isThinking = false
  3332. var canGo = true;
  3333. var myTurn = false;
  3334. var board;
  3335.  
  3336. window.addEventListener("load", (event) => {
  3337. // Start the main application
  3338. main();
  3339. });

QingJ © 2025

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