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