Better UI Smart Chess Bot

Our chess analysis system, modified with a better UI, is designed to give players the edge they need to win. By using advanced algorithms and cutting-edge technology, our system can analyze any chess position and suggest the best possible move, helping players to make smarter and more informed decisions on the board.

当前为 2024-05-02 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Better UI Smart Chess Bot
  3. // @name:fr Better UI Smart Chess bot
  4. // @namespace TropicalFrog3, sayfpack13
  5. // @author TropicalFrog3, sayfpack13
  6. // @version 1.0
  7. // @supportURL https://mmgc.life/
  8. // @match https://www.chess.com/*
  9. // @match https://lichess.org/*
  10. // @grant GM_getValue
  11. // @grant GM_setValue
  12. // @grant GM_xmlhttpRequest
  13. // @grant GM_getResourceText
  14. // @grant GM_registerMenuCommand
  15. // @description Our chess analysis system, modified with a better UI, is designed to give players the edge they need to win. By using advanced algorithms and cutting-edge technology, our system can analyze any chess position and suggest the best possible move, helping players to make smarter and more informed decisions on the board.
  16. // @description:fr Notre système d'analyse d'échecs, modifié avec une meilleure interface utilisateur (UI), est conçu pour donner aux joueurs l'avantage dont ils ont besoin pour gagner. En utilisant des algorithmes avancés et une technologie de pointe, notre système peut analyser n'importe quelle position d'échecs et suggérer le meilleur coup possible, aidant ainsi les joueurs à prendre des décisions plus intelligentes et plus éclairées sur l'échiquier.
  17. // @require https://greasyfork.org/scripts/460400-usergui-js/code/userguijs.js?version=1157130
  18. // @resource jquery.js https://cdn.jsdelivr.net/npm/jquery@3.6.3/dist/jquery.min.js
  19. // @resource chessboard.js https://raw.githubusercontent.com/sayfpack13/chess-analysis-bot/main/tampermonkey%20script/content/chessboard.js
  20. // @resource chessboard.css https://raw.githubusercontent.com/sayfpack13/chess-analysis-bot/main/tampermonkey%20script/content/chessboard.css
  21. // @resource lozza.js https://raw.githubusercontent.com/sayfpack13/chess-analysis-bot/main/tampermonkey%20script/content/lozza.js
  22. // @resource stockfish-5.js https://raw.githubusercontent.com/sayfpack13/chess-analysis-bot/main/tampermonkey%20script/content/stockfish-5.js
  23. // @resource stockfish-2018.js https://raw.githubusercontent.com/sayfpack13/chess-analysis-bot/main/tampermonkey%20script/content/stockfish-2018.js
  24. // @run-at document-start
  25. // @license MIT
  26. // ==/UserScript==
  27.  
  28. // VARS
  29. const repositoryRawURL = 'https://raw.githubusercontent.com/sayfpack13/chess-analysis-bot/main/tampermonkey%20script';
  30. const LICHESS_API = "https://lichess.org/api/cloud-eval";
  31. const CHESS_COM = 0;
  32. const LICHESS_ORG = 1;
  33. const TURN_UPDATE_FIX = false;
  34.  
  35.  
  36. const MAX_DEPTH = 20;
  37. const MIN_DEPTH = 1;
  38. const MAX_MOVETIME = 2000;
  39. const MIN_MOVETIME = 50;
  40. const MAX_ELO = 3500;
  41. const DEPTH_MODE = 0;
  42. const MOVETIME_MODE = 1;
  43. const rank = ["Beginner", "Intermediate", "Advanced", "Expert", "Master", "Grand Master"];
  44.  
  45.  
  46.  
  47.  
  48. var nightMode = true;
  49. var engineMode = 0;// engine mode (0:depth / 1:movetime)
  50. var engineIndex = 0;// engine index (lozza => 0, stockfish => 1...)
  51. var reload_every = 10;// reload engine after x moves
  52. var reload_engine = false;// reload engine
  53. var enableUserLog = false;// enable interface log
  54. var enableEngineLog = false;// enable engine log
  55. var displayMovesOnSite = false;// display moves on chess board
  56. var show_opposite_moves = false;// show opponent best moves if available
  57. var use_book_moves = false;// use lichess api to get book moves
  58. var node_engine_url = "http://localhost:5000";// node server api url
  59. var node_engine_name = "stockfish-15.exe";// default engine name (node server engine only)
  60. var current_depth = Math.round(MAX_DEPTH / 2);// current engine depth
  61. var current_movetime = Math.round(MAX_MOVETIME / 3);// current engine move time
  62. var max_best_moves = 1;
  63.  
  64. var lastBestMoveID = 0;
  65.  
  66.  
  67.  
  68. const dbValues = {
  69. nightMode: 'nightMode',
  70. engineMode: 'engineMode',
  71. engineIndex: 'engineIndex',
  72. reload_every: 'reload_every',
  73. reload_engine: 'reload_engine',
  74. enableUserLog: 'enableUserLog',
  75. enableEngineLog: 'enableEngineLog',
  76. displayMovesOnSite: 'displayMovesOnSite',
  77. show_opposite_moves: "show_opposite_moves",
  78. use_book_moves: "use_book_moves",
  79. node_engine_url: "node_engine_url",
  80. node_engine_name: "node_engine_name",
  81. current_depth: "current_depth",
  82. current_movetime: "current_movetime",
  83. max_best_moves: "max_best_moves"
  84. };
  85.  
  86.  
  87. var Gui;
  88. var closedGui = false;
  89. var reload_count = 1;
  90. var node_engine_id = 3;
  91. var Interface = null;
  92. var LozzaUtils = null;
  93. var CURRENT_SITE = null;
  94. var boardElem = null;
  95. var firstPieceElem = null;
  96. const MAX_LOGS = 50;
  97.  
  98.  
  99. var initialized = false;
  100. var firstMoveMade = false;
  101.  
  102. var forcedBestMove = false;
  103. var engine = null;
  104. var engineObjectURL = null;
  105. var lastEngine = engineIndex;
  106.  
  107. var chessBoardElem = null;
  108. var turn = '-';
  109. var last_turn = null;
  110. var playerColor = null;
  111. var lastPlayerColor = null;
  112. var isPlayerTurn = null;
  113. var lastFen = null;
  114.  
  115. var uiChessBoard = null;
  116.  
  117. var activeGuiMoveHighlights = [];
  118. var activeSiteMoveHighlights = [];
  119.  
  120. var engineLogNum = 1;
  121. var userscriptLogNum = 1;
  122. var enemyScore = 0;
  123. var myScore = 0;
  124.  
  125. var possible_moves = [];
  126.  
  127.  
  128. // style
  129. const best_move_color = [0, 0, 250, 0.5];
  130. const opposite_best_move_color = [250, 0, 0, 0.5];
  131. const possible_moves_colors = [
  132. //[r,g,b,a]
  133. [200, 180, 0, 0.9],
  134. [150, 180, 0, 0.9],
  135. [100, 180, 0, 0.9],
  136. [50, 180, 0, 0.9]
  137. ]
  138. const opposite_possible_moves_colors = [
  139. //[r,g,b,a]
  140. [250, 200, 200, 0.9],
  141. [250, 150, 150, 0.9],
  142. [250, 100, 100, 0.9],
  143. [250, 50, 50, 0.9]
  144. ]
  145. const defaultFromSquareStyle = 'border: 4px solid rgb(0 0 0 / 50%);';
  146. const defaultToSquareStyle = 'border: 4px dashed rgb(0 0 0 / 50%);';
  147.  
  148. let guiVisible = true;
  149. let lastGuiWidth = "";
  150. let lastGuiHeight = "";
  151.  
  152. function toggleGUI() {
  153. const iframes = document.querySelectorAll('iframe');
  154. if (iframes.length > 0) {
  155. const guiFrame = iframes[0];
  156. if (guiVisible) {
  157. guiFrame.style.visibility = 'hidden';
  158. lastGuiWidth = guiFrame.style.width;
  159. guiFrame.style.width = '0px';
  160. lastGuiHeight = guiFrame.style.height ;
  161. guiFrame.style.height = '0px';
  162. guiVisible = false;
  163. } else {
  164. guiFrame.style.visibility = 'visible';
  165. guiFrame.style.width = lastGuiWidth;
  166. guiFrame.style.height = lastGuiHeight;
  167. guiVisible = true;
  168. }
  169. } else {
  170. console.error('No iframe found in the document.');
  171. }
  172. }
  173.  
  174. function moveResult(from, to, power, clear = true) {
  175. if (from.length < 2 || to.length < 2) {
  176. return;
  177. }
  178.  
  179. if (clear) {
  180. clearBoard();
  181. }
  182.  
  183.  
  184. if (!forcedBestMove) {
  185. if (isPlayerTurn) // my turn
  186. {
  187. myScore = myScore + Number(power);
  188. }
  189. else
  190. {
  191. enemyScore = enemyScore + Number(power);
  192. }
  193. Interface.boardUtils.updateBoardPower(myScore, enemyScore);
  194. } else {
  195. forcedBestMove = false;
  196. Gui.document.querySelector('#bestmove-btn').disabled = false;
  197. }
  198.  
  199.  
  200.  
  201. // remove duplicated best moves
  202. possible_moves = removeDuplicates(possible_moves).slice(0, max_best_moves - 1);
  203.  
  204. for (let a = 0; a < possible_moves.length; a++) {
  205. Interface.boardUtils.markMove(possible_moves[a].slice(0, 2), possible_moves[a].slice(2, 4), (isPlayerTurn ? possible_moves_colors[a] : opposite_possible_moves_colors[a]));
  206. }
  207.  
  208. Interface.boardUtils.markMove(from, to, (isPlayerTurn ? best_move_color : opposite_best_move_color));
  209.  
  210.  
  211.  
  212. Interface.stopBestMoveProcessingAnimation();
  213. }
  214.  
  215.  
  216.  
  217. function getBookMoves(request) {
  218.  
  219. GM_xmlhttpRequest({
  220. method: "GET",
  221. url: LICHESS_API + "?fen=" + request.fen + "&multiPv=1&variant=fromPosition",
  222. headers: {
  223. "Content-Type": "application/json"
  224. },
  225. onload: function (response) {
  226. if (response.response.includes("error") || !response.ok) {
  227. if (lastBestMoveID != request.id) {
  228. return;
  229. }
  230. getBestMoves(request);
  231. } else {
  232. if (lastBestMoveID != request.id) {
  233. return;
  234. }
  235.  
  236. let data = JSON.parse(response.response);
  237. let nextMove = data.pvs[0].moves.split(' ')[0];
  238.  
  239.  
  240.  
  241. moveResult(nextMove.slice(0, 2), nextMove.slice(2, 4), current_depth, true);
  242. }
  243.  
  244.  
  245. }, onerror: function (error) {
  246. if (lastBestMoveID != request.id) {
  247. return;
  248. }
  249. getBestMoves(request);
  250.  
  251. }
  252. });
  253.  
  254. }
  255.  
  256. function getNodeBestMoves(request) {
  257. GM_xmlhttpRequest({
  258. method: "GET",
  259. url: node_engine_url + "/getBestMove?fen=" + request.fen + "&engine_mode=" + engineMode + "&depth=" + current_depth + "&movetime=" + current_movetime + "&turn=" + (last_turn || turn) + "&engine_name=" + node_engine_name,
  260. headers: {
  261. "Content-Type": "application/json"
  262. },
  263. onload: function (response) {
  264. if (response.response == "false") {
  265. forcedBestMove = false;
  266. Gui.document.querySelector('#bestmove-btn').disabled = false;
  267. return Interface.log("check node server logs !!");
  268. }
  269.  
  270. if (lastBestMoveID != request.id) {
  271. return;
  272. }
  273.  
  274.  
  275. let data = JSON.parse(response.response);
  276. let server_fen = data.fen;
  277. let depth = data.depth;
  278. let movetime = data.movetime;
  279. let power = data.score;
  280. let move = data.move;
  281.  
  282.  
  283.  
  284. if (engineMode == DEPTH_MODE) {
  285. Interface.updateBestMoveProgress(`Depth: ${depth}`);
  286. } else {
  287. Interface.updateBestMoveProgress(`Move time: ${movetime} ms`);
  288. }
  289.  
  290.  
  291.  
  292. moveResult(move.slice(0, 2), move.slice(2, 4), power, true);
  293. }, onerror: function (error) {
  294. forcedBestMove = false;
  295. Gui.document.querySelector('#bestmove-btn').disabled = false;
  296. Interface.log("make sure node server is running !!");
  297. }
  298. });
  299.  
  300. }
  301.  
  302. function getElo() {
  303. let elo;
  304. if (engineMode == DEPTH_MODE) {
  305. elo = MAX_ELO / MAX_DEPTH;
  306. elo *= current_depth;
  307. } else {
  308. elo = MAX_ELO / MAX_MOVETIME;
  309. elo *= current_movetime;
  310. }
  311. elo = Math.round(elo);
  312.  
  313. return elo;
  314. }
  315.  
  316. function getRank() {
  317. let part;
  318. if (engineMode == DEPTH_MODE) {
  319. part = current_depth / (MAX_DEPTH / rank.length);
  320. } else {
  321. part = current_movetime / (MAX_MOVETIME / rank.length);
  322. }
  323. part = Math.round(part);
  324.  
  325. if (part >= rank.length) {
  326. part = rank.length - 1;
  327. }
  328.  
  329. return rank[part];
  330. }
  331.  
  332.  
  333.  
  334. function setEloDescription(eloElem) {
  335. eloElem.querySelector("#value").innerText = `Elo: ${getElo()}`;
  336. eloElem.querySelector("#rank").innerText = `Rank: ${getRank()}`;
  337. eloElem.querySelector("#power").innerText = engineMode == DEPTH_MODE ? `Depth: ${current_depth}` : `Move Time: ${current_movetime}`;
  338. }
  339.  
  340.  
  341.  
  342.  
  343. function isNotCompatibleBrowser() {
  344. return navigator.userAgent.toLowerCase().includes("firefox")
  345. }
  346.  
  347. onload = function () {
  348. if (isNotCompatibleBrowser()) {
  349. Gui = new UserGui;
  350. }
  351.  
  352. }
  353.  
  354. if (!isNotCompatibleBrowser()) {
  355. Gui = new UserGui;
  356. } else {
  357. onload();
  358. }
  359.  
  360.  
  361. Gui.settings.window.title = 'Better UI Smart Chess Bot - Press [G] to open / close';
  362. Gui.settings.window.external = false;
  363. Gui.settings.window.size.width = 500;
  364. Gui.settings.gui.external.popup = false;
  365. Gui.settings.gui.external.style += GM_getResourceText('chessboard.css');
  366. Gui.settings.gui.external.style += `
  367. div[class^='board'] {
  368. background-color: black;
  369. }
  370. body {
  371. display: block;
  372. margin-left: auto;
  373. margin-right: auto;
  374. width: 360px;
  375. }
  376. #fen {
  377. margin-left: 10px;
  378. }
  379. #engine-log-container {
  380. max-height: 35vh;
  381. overflow: auto!important;
  382. }
  383. #userscript-log-container {
  384. max-height: 35vh;
  385. overflow: auto!important;
  386. }
  387. .sideways-card {
  388. display: flex;
  389. align-items: center;
  390. justify-content: space-between;
  391. }
  392. .rendered-form .card {
  393. margin-bottom: 10px;
  394. }
  395. .hidden {
  396. display: none;
  397. }
  398. .main-title-bar {
  399. display: flex;
  400. justify-content: space-between;
  401. }
  402. @keyframes wiggle {
  403. 0% { transform: scale(1); }
  404. 80% { transform: scale(1); }
  405. 85% { transform: scale(1.1); }
  406. 95% { transform: scale(1); }
  407. 100% { transform: scale(1); }
  408. }
  409.  
  410. .wiggle {
  411. display: inline-block;
  412. animation: wiggle 1s infinite;
  413. }
  414. `;
  415.  
  416.  
  417. function alphabetPosition(text) {
  418. return text.charCodeAt(0) - 97;
  419. }
  420.  
  421.  
  422. function FenUtils() {
  423. this.board = [
  424. [1, 1, 1, 1, 1, 1, 1, 1],
  425. [1, 1, 1, 1, 1, 1, 1, 1],
  426. [1, 1, 1, 1, 1, 1, 1, 1],
  427. [1, 1, 1, 1, 1, 1, 1, 1],
  428. [1, 1, 1, 1, 1, 1, 1, 1],
  429. [1, 1, 1, 1, 1, 1, 1, 1],
  430. [1, 1, 1, 1, 1, 1, 1, 1],
  431. [1, 1, 1, 1, 1, 1, 1, 1],
  432. ];
  433.  
  434. this.pieceCodeToFen = pieceStr => {
  435. let [pieceColor, pieceName] = pieceStr.split('');
  436.  
  437. return pieceColor == 'w' ? pieceName.toUpperCase() : pieceName.toLowerCase();
  438. }
  439.  
  440. this.getFenCodeFromPieceElem = pieceElem => {
  441. if (CURRENT_SITE == CHESS_COM) {
  442. return this.pieceCodeToFen([...pieceElem.classList].find(x => x.match(/^(b|w)[prnbqk]{1}$/)));
  443. } else if (CURRENT_SITE == LICHESS_ORG) {
  444. let [pieceColor, pieceName] = pieceElem.cgPiece.split(' ');
  445.  
  446. // fix pieceName
  447. if (pieceName == "knight") {
  448. pieceName = "n"
  449. }
  450.  
  451. let pieceText = pieceColor[0] + pieceName[0];
  452. return this.pieceCodeToFen(pieceText)
  453. }
  454. }
  455.  
  456. this.getPieceColor = pieceFenStr => {
  457. return pieceFenStr == pieceFenStr.toUpperCase() ? 'w' : 'b';
  458. }
  459.  
  460. this.getPieceOppositeColor = pieceFenStr => {
  461. return this.getPieceColor(pieceFenStr) == 'w' ? 'b' : 'w';
  462. }
  463.  
  464. this.squeezeEmptySquares = fenStr => {
  465. return fenStr.replace(/11111111/g, '8')
  466. .replace(/1111111/g, '7')
  467. .replace(/111111/g, '6')
  468. .replace(/11111/g, '5')
  469. .replace(/1111/g, '4')
  470. .replace(/111/g, '3')
  471. .replace(/11/g, '2');
  472. }
  473.  
  474. this.posToIndex = pos => {
  475. let [x, y] = pos.split('');
  476.  
  477. return { 'y': 8 - y, 'x': 'abcdefgh'.indexOf(x) };
  478. }
  479.  
  480. this.getBoardPiece = pos => {
  481. let indexObj = this.posToIndex(pos);
  482.  
  483. return this.board[indexObj.y][indexObj.x];
  484. }
  485.  
  486. this.getRights = () => {
  487. let rights = '';
  488.  
  489. // check for white
  490. let e1 = this.getBoardPiece('e1'),
  491. h1 = this.getBoardPiece('h1'),
  492. a1 = this.getBoardPiece('a1');
  493.  
  494. if (e1 == 'K' && h1 == 'R') rights += 'K';
  495. if (e1 == 'K' && a1 == 'R') rights += 'Q';
  496.  
  497. //check for black
  498. let e8 = this.getBoardPiece('e8'),
  499. h8 = this.getBoardPiece('h8'),
  500. a8 = this.getBoardPiece('a8');
  501.  
  502. if (e8 == 'k' && h8 == 'r') rights += 'k';
  503. if (e8 == 'k' && a8 == 'r') rights += 'q';
  504.  
  505. return rights ? rights : '-';
  506. }
  507.  
  508.  
  509.  
  510.  
  511. this.getBasicFen = () => {
  512. let pieceElems = null;
  513.  
  514. if (CURRENT_SITE == CHESS_COM) {
  515. pieceElems = [...chessBoardElem.querySelectorAll('.piece')];
  516. } else if (CURRENT_SITE == LICHESS_ORG) {
  517. pieceElems = [...chessBoardElem.querySelectorAll('piece')];
  518. }
  519.  
  520.  
  521. pieceElems.filter(pieceElem => !pieceElem.classList.contains("ghost")).forEach(pieceElem => {
  522. let pieceFenCode = this.getFenCodeFromPieceElem(pieceElem);
  523.  
  524.  
  525.  
  526. if (CURRENT_SITE == CHESS_COM) {
  527. let [xPos, yPos] = pieceElem.classList.toString().match(/square-(\d)(\d)/).slice(1);
  528.  
  529. this.board[8 - yPos][xPos - 1] = pieceFenCode;
  530. } else if (CURRENT_SITE == LICHESS_ORG) {
  531. let [xPos, yPos] = pieceElem.cgKey.split('');
  532.  
  533. this.board[8 - yPos][alphabetPosition(xPos)] = pieceFenCode;
  534. }
  535. });
  536.  
  537. let basicFen = this.squeezeEmptySquares(this.board.map(x => x.join('')).join('/'));
  538.  
  539.  
  540.  
  541. return basicFen;
  542. }
  543.  
  544.  
  545. this.getFen = () => {
  546. let basicFen = this.getBasicFen();
  547. let rights = this.getRights();
  548.  
  549. return `${basicFen} ${last_turn || turn} ${rights} - 0 1`;
  550. }
  551. }
  552.  
  553.  
  554.  
  555. function InterfaceUtils() {
  556. this.boardUtils = {
  557. findSquareElem: (squareCode) => {
  558. if (!Gui?.document) return;
  559.  
  560. return Gui.document.querySelector(`.square-${squareCode}`);
  561. },
  562. markMove: (fromSquare, toSquare, rgba_color) => {
  563. if (!Gui?.document) return;
  564.  
  565. if (CURRENT_SITE == CHESS_COM && fromSquare != "" && toSquare != "") {
  566.  
  567. if (!isNotCompatibleBrowser()) {
  568. [fromElem, toElem] = [this.boardUtils.findSquareElem(fromSquare), this.boardUtils.findSquareElem(toSquare)];
  569. fromElem.style.scale = 0.8;
  570. toElem.style.scale = 0.9;
  571. fromElem.style.backgroundColor = `rgb(${rgba_color[0]},${rgba_color[1]},${rgba_color[2]})`;
  572. toElem.style.backgroundColor = `rgb(${rgba_color[0]},${rgba_color[1]},${rgba_color[2]})`;
  573.  
  574.  
  575.  
  576. activeGuiMoveHighlights.push(fromElem);
  577. activeGuiMoveHighlights.push(toElem);
  578. }
  579.  
  580. }
  581.  
  582. if (displayMovesOnSite || (!isPlayerTurn && show_opposite_moves)) {
  583. markMoveToSite(fromSquare, toSquare, rgba_color);
  584. }
  585. },
  586. removeBestMarkings: () => {
  587. if (!Gui?.document) return;
  588.  
  589. activeGuiMoveHighlights.forEach(elem => {
  590. elem.style.scale = 1.0;
  591. elem.style.backgroundColor = "";
  592. });
  593.  
  594. activeGuiMoveHighlights = [];
  595. },
  596. updateBoardFen: fen => {
  597. if (!Gui?.document) return;
  598.  
  599. Gui.document.querySelector('#fen').textContent = fen.slice(0, fen.lastIndexOf('-') - 1);
  600. },
  601. updateBoardPower: (myScore, enemyScore) => {
  602. if (!Gui?.document) return;
  603.  
  604. Gui.document.querySelector('#enemy-score').textContent = enemyScore;
  605. Gui.document.querySelector('#my-score').textContent = myScore;
  606. },
  607. updateBoardOrientation: orientation => {
  608. if (!Gui?.document) return;
  609.  
  610. const orientationElem = Gui?.document?.querySelector('#orientation');
  611.  
  612. if (orientationElem) {
  613. orientationElem.textContent = orientation;
  614. }
  615. }
  616. }
  617.  
  618. this.engineLog = str => {
  619. if (!Gui?.document || enableEngineLog == false) return;
  620.  
  621. const logElem = document.createElement('div');
  622. logElem.classList.add('list-group-item');
  623.  
  624. if (str.includes('info')) logElem.classList.add('list-group-item-info');
  625. if (str.includes('bestmove')) logElem.classList.add('list-group-item-success');
  626.  
  627. logElem.innerText = `#${engineLogNum++} ${str}`;
  628.  
  629. if (engineLogNum > MAX_LOGS) {
  630. Gui.document.querySelector('#engine-log-container').lastChild.remove();
  631. }
  632.  
  633. Gui.document.querySelector('#engine-log-container').prepend(logElem);
  634.  
  635.  
  636. }
  637.  
  638. this.log = str => {
  639. if (!Gui?.document || enableUserLog == false) return;
  640.  
  641. const logElem = document.createElement('div');
  642. logElem.classList.add('list-group-item');
  643.  
  644. if (str.includes('info')) logElem.classList.add('list-group-item-info');
  645. if (str.includes('bestmove')) logElem.classList.add('list-group-item-success');
  646.  
  647. const container = Gui?.document?.querySelector('#userscript-log-container');
  648.  
  649. if (container) {
  650. logElem.innerText = `#${userscriptLogNum++} ${str}`;
  651.  
  652. if (userscriptLogNum > MAX_LOGS) {
  653. container.lastChild.remove();
  654. }
  655.  
  656.  
  657. container.prepend(logElem);
  658. }
  659. }
  660.  
  661. this.getBoardOrientation = () => {
  662. if (CURRENT_SITE == CHESS_COM) {
  663. return document.querySelector('.board.flipped') ? 'b' : 'w';
  664. } else if (CURRENT_SITE == LICHESS_ORG) {
  665. return document.querySelector(".orientation-white") !== null ? 'w' : 'b'
  666. }
  667.  
  668. }
  669.  
  670. this.updateBestMoveProgress = text => {
  671. if (!Gui?.document || isNotCompatibleBrowser() || CURRENT_SITE === LICHESS_ORG) return;
  672.  
  673. const progressBarElem = Gui.document.querySelector('#best-move-progress');
  674.  
  675. progressBarElem.innerText = text;
  676.  
  677. progressBarElem.classList.remove('hidden');
  678. progressBarElem.classList.add('wiggle');
  679. }
  680.  
  681. this.stopBestMoveProcessingAnimation = () => {
  682. if (!Gui?.document || isNotCompatibleBrowser() || CURRENT_SITE === LICHESS_ORG) return;
  683.  
  684. const progressBarElem = Gui.document.querySelector('#best-move-progress');
  685.  
  686. progressBarElem.classList.remove('wiggle');
  687. }
  688.  
  689. this.hideBestMoveProgress = () => {
  690. if (!Gui?.document || isNotCompatibleBrowser() || CURRENT_SITE === LICHESS_ORG) return;
  691.  
  692. const progressBarElem = Gui.document.querySelector('#best-move-progress');
  693.  
  694. if (!progressBarElem.classList.contains('hidden')) {
  695. progressBarElem.classList.add('hidden');
  696. this.stopBestMoveProcessingAnimation();
  697. }
  698. }
  699. }
  700.  
  701. function LozzaUtility() {
  702. this.separateMoveCodes = moveCode => {
  703. moveCode = moveCode.trim();
  704.  
  705. let move = moveCode.split(' ')[1];
  706.  
  707. return [move.slice(0, 2), move.slice(2, 4)];
  708. }
  709.  
  710. this.extractInfo = str => {
  711.  
  712. const keys = ['time', 'nps', 'depth', 'pv'];
  713.  
  714. return keys.reduce((acc, key) => {
  715. let match = str.match(`${key} (\\d+)`);
  716.  
  717.  
  718. if (match) {
  719. acc[key] = (match[1]);
  720. }
  721.  
  722. return acc;
  723. }, {});
  724. }
  725. }
  726.  
  727. function markMoveToSite(fromSquare, toSquare) {
  728. const highlight = (fromSquare, toSquare) => {
  729. const arrowId = `arrow-${fromSquare}${toSquare}`;
  730. const arrowData = `${fromSquare}${toSquare}`;
  731.  
  732. const points = calculateArrowPoints(fromSquare, toSquare);
  733. const pointsString = points.map(point => `${point.x} ${point.y},`).join(' ');
  734.  
  735. const rotArrow = CalculateArrowRotation(fromSquare, toSquare);
  736.  
  737. const arrowElem = document.createElementNS("http://www.w3.org/2000/svg", "polygon");
  738. arrowElem.setAttribute("id", arrowId);
  739. arrowElem.setAttribute("data-arrow", arrowData);
  740. arrowElem.setAttribute("class", "arrow");
  741. arrowElem.setAttribute("points", pointsString);
  742. arrowElem.setAttribute("transform",`rotate(${rotArrow.deg} ${rotArrow.param1} ${rotArrow.param2})`);
  743. arrowElem.setAttribute("style", `fill: rgba(255, 170, 0, 0.8); opacity: 0.8;`);
  744.  
  745. activeSiteMoveHighlights.push(arrowElem);
  746.  
  747. const existingArrow = document.getElementById(arrowId);
  748. if (existingArrow) {
  749. existingArrow.remove();
  750. }
  751.  
  752.  
  753. let arrowsSVG = chessBoardElem.querySelector('svg.arrows');
  754. if (!arrowsSVG) {
  755. arrowsSVG = document.createElementNS("http://www.w3.org/2000/svg", "svg");
  756. arrowsSVG.setAttribute("class", "arrows");
  757. chessBoardElem.appendChild(arrowsSVG);
  758. }
  759.  
  760. arrowsSVG.appendChild(arrowElem);
  761. }
  762.  
  763. highlight(fromSquare, toSquare);
  764. }
  765.  
  766. function CalculateArrowRotation(fromSquare, toSquare) {
  767. const startAngle = -90;
  768.  
  769. const fromCoords = squareToCoords(fromSquare, true);
  770. const toCoords = squareToCoords(toSquare, true);
  771. const deltaX = toCoords.x - fromCoords.x;
  772. const deltaY = toCoords.y - fromCoords.y;
  773. const angleRad = Math.atan2(deltaY, deltaX);
  774. const angleDeg = angleRad * (180 / Math.PI);
  775.  
  776. const rotationAngle = angleDeg + startAngle;
  777.  
  778. const centerX = fromCoords.x;
  779. const centerY = fromCoords.y;
  780.  
  781. return { deg: rotationAngle, param1: centerX, param2: centerY };
  782. }
  783.  
  784.  
  785. function calculateArrowPoints(fromSquare, toSquare) {
  786.  
  787. const fromCoords = squareToCoords(fromSquare, true);
  788. const toCoords = squareToCoords(toSquare, false);
  789.  
  790. const lengthX = Math.abs(fromCoords.x - toCoords.x);
  791. const lengthY = Math.abs(fromCoords.y - toCoords.y);
  792. const lengthArrow = Math.sqrt(Math.pow(lengthX, 2) + Math.pow(lengthY, 2));
  793.  
  794. const arrowPoints = [];
  795.  
  796. arrowPoints.push({ x: fromCoords.x - 1.375, y: fromCoords.y + 4.5}); // top left
  797. arrowPoints.push({ x: fromCoords.x - 1.375, y: fromCoords.y + lengthArrow - 4.25 }); // bottom left
  798. arrowPoints.push({ x: fromCoords.x - 3.25, y: fromCoords.y + lengthArrow - 4.25 }); // bottom left left
  799.  
  800. arrowPoints.push({ x: fromCoords.x, y: fromCoords.y + lengthArrow }); // bottom
  801.  
  802. arrowPoints.push({ x: fromCoords.x + 3.25, y: fromCoords.y + lengthArrow - 4.25}); // bottom right right
  803. arrowPoints.push({ x: fromCoords.x - 1.375 + 2.75, y: fromCoords.y + lengthArrow - 4.25}); // bottom right
  804. arrowPoints.push({ x: fromCoords.x - 1.375 + 2.75, y: fromCoords.y + 4.5 }); // top right
  805. return arrowPoints;
  806. }
  807.  
  808. function squareToCoords(square, start) {
  809. var xLettre = square.charAt(0);
  810. var yLettre = square.charAt(1);
  811. var y = 0;
  812. var x = 0;
  813.  
  814. switch (xLettre) {
  815. case 'a':
  816. x = 6.25;
  817. break;
  818. case 'b':
  819. x = 18.75;
  820. break;
  821. case 'c':
  822. x = 31.25;
  823. break;
  824. case 'd':
  825. x = 43.75;
  826. break;
  827. case 'e':
  828. x = 56.25;
  829. break;
  830. case 'f':
  831. x = 68.75;
  832. break;
  833. case 'g':
  834. x = 81.25;
  835. break;
  836. case 'h':
  837. x = 93.75;
  838. break;
  839. default:
  840. x = 0;
  841. }
  842.  
  843. switch (yLettre) {
  844. case '8':
  845. y = 6.25;
  846. break;
  847. case '7':
  848. y = 18.75;
  849. break;
  850. case '6':
  851. y = 31.25;
  852. break;
  853. case '5':
  854. y = 43.75;
  855. break;
  856. case '4':
  857. y = 56.25;
  858. break;
  859. case '3':
  860. y = 68.75;
  861. break;
  862. case '2':
  863. y = 81.25;
  864. break;
  865. case '1':
  866. y = 93.75;
  867. break;
  868. default:
  869. y = 0;
  870. }
  871.  
  872. if(!start)
  873. {
  874. x-=1.375;
  875. }
  876. return { x, y };
  877. }
  878.  
  879. function removeSiteMoveMarkings() {
  880. activeSiteMoveHighlights.forEach(elem => {
  881. elem?.remove();
  882. });
  883.  
  884. activeSiteMoveHighlights = [];
  885. }
  886.  
  887.  
  888.  
  889.  
  890. function getTurn() {
  891.  
  892. const getSquareNumber = (elem) => {
  893. for (var a = 0; a < elem.classList.length; a++) {
  894. if (elem.classList[a].includes("square")) {
  895. return elem.classList[a];
  896. }
  897. }
  898. return "";
  899. }
  900.  
  901. const getSimiliarChessPiece = (squareNumber) => {
  902. let similiarChessPieces = chessBoardElem.querySelectorAll("." + squareNumber);
  903.  
  904. for (var a = 0; a < similiarChessPieces.length; a++) {
  905. if (similiarChessPieces[a].classList.contains("piece") == true) {
  906. return similiarChessPieces[a];
  907. }
  908. }
  909. return null;
  910. }
  911.  
  912. const getChessPieceColor = (elem) => {
  913. for (var a = 0; a < elem.classList.length; a++) {
  914. if (elem.classList[a].length <= 2) {
  915. if (elem.classList[a][0] == "b") {
  916. return "b";
  917. } else {
  918. return "w";
  919. }
  920. }
  921. }
  922. return "";
  923. }
  924.  
  925.  
  926.  
  927. Interface.boardUtils.removeBestMarkings();
  928.  
  929. removeSiteMoveMarkings();
  930.  
  931.  
  932. let chessPieces = chessBoardElem.querySelectorAll(".highlight");
  933.  
  934. for (var a = 0; a < chessPieces.length; a++) {
  935.  
  936. // exclude custom highlighted squares
  937. if (chessPieces[a].classList.contains("custom") == true) {
  938. continue;
  939. }
  940.  
  941. let squareNumber = getSquareNumber(chessPieces[a]);
  942. if (squareNumber == "") {
  943. return "";
  944. }
  945.  
  946. let similiarChessPiece = getSimiliarChessPiece(squareNumber);
  947.  
  948. if (similiarChessPiece == null) {
  949. continue;
  950. }
  951.  
  952. let chessPieceColor = getChessPieceColor(similiarChessPiece);
  953.  
  954. if (chessPieceColor == "") {
  955. return "";
  956. }
  957.  
  958. if (chessPieceColor == "b") {
  959. return "w";
  960. } else if (chessPieceColor == "w") {
  961. return "b";
  962. }
  963. }
  964.  
  965. return "";
  966. }
  967.  
  968. function updateBestMove(mutationArr) {
  969. const FenUtil = new FenUtils();
  970. let currentFen = FenUtil.getFen();
  971.  
  972.  
  973. if (currentFen != lastFen) {
  974. lastFen = currentFen;
  975.  
  976.  
  977. if (mutationArr) {
  978. let attributeMutationArr
  979.  
  980. if (CURRENT_SITE == CHESS_COM) {
  981. attributeMutationArr = mutationArr.filter(m => m.target.classList.contains('piece') && m.attributeName == 'class');
  982. } else if (CURRENT_SITE == LICHESS_ORG) {
  983. attributeMutationArr = mutationArr.filter(m => m.target.tagName == 'PIECE' && !m.target.classList.contains('fading') && m.attributeName == 'class');
  984. }
  985.  
  986.  
  987.  
  988. if (attributeMutationArr?.length) {
  989. turn = FenUtil.getPieceOppositeColor(FenUtil.getFenCodeFromPieceElem(attributeMutationArr[0].target));
  990.  
  991. last_turn = turn;
  992. // fix turn method
  993. if (TURN_UPDATE_FIX && getTurn() != "") {
  994. turn = getTurn();
  995. }
  996.  
  997. Interface.log(`Turn updated to ${turn}!`);
  998. }
  999. }
  1000.  
  1001. updateBoard();
  1002. sendBestMove();
  1003. }
  1004. }
  1005.  
  1006.  
  1007. function sendBestMove() {
  1008. if (!isPlayerTurn && !show_opposite_moves) {
  1009. return;
  1010. }
  1011.  
  1012. sendBestMoveRequest();
  1013. }
  1014.  
  1015. function sendBestMoveRequest() {
  1016. const FenUtil = new FenUtils();
  1017. let currentFen = FenUtil.getFen();
  1018. possible_moves = [];
  1019.  
  1020. reloadChessEngine(false, () => {
  1021. Interface.log('Sending best move request to the engine!');
  1022.  
  1023. lastBestMoveID++;
  1024.  
  1025.  
  1026. if (use_book_moves) {
  1027. getBookMoves({ id: lastBestMoveID, fen: currentFen });
  1028. } else {
  1029. getBestMoves({ id: lastBestMoveID, fen: currentFen });
  1030. }
  1031. });
  1032. }
  1033.  
  1034.  
  1035. function clearBoard() {
  1036. Interface.stopBestMoveProcessingAnimation();
  1037.  
  1038. Interface.boardUtils.removeBestMarkings();
  1039.  
  1040. removeSiteMoveMarkings();
  1041. }
  1042. function updateBoard(clear = true) {
  1043. if (clear)
  1044. clearBoard();
  1045.  
  1046. const FenUtil = new FenUtils();
  1047. let currentFen = FenUtil.getFen();
  1048.  
  1049. if (currentFen == ("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1") || currentFen == ("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR b KQkq - 0 1")) {
  1050. enemyScore = 0;
  1051. myScore = 0;
  1052. Interface.boardUtils.updateBoardPower(myScore, enemyScore);
  1053. }
  1054.  
  1055. isPlayerTurn = playerColor == null || last_turn == null || last_turn == playerColor;
  1056.  
  1057.  
  1058. Interface.boardUtils.updateBoardFen(currentFen);
  1059. }
  1060.  
  1061.  
  1062. function removeDuplicates(arr) {
  1063. return arr.filter((item,
  1064. index) => arr.indexOf(item) === index);
  1065. }
  1066.  
  1067.  
  1068. function sleep(ms) {
  1069. return new Promise(resolve => setTimeout(resolve, ms));
  1070. }
  1071.  
  1072. function getBestMoves(request) {
  1073.  
  1074.  
  1075. if (engineIndex != node_engine_id && CURRENT_SITE !== LICHESS_ORG) {
  1076. // local engines
  1077. while (!engine) {
  1078. sleep(100);
  1079. }
  1080.  
  1081. engine.postMessage(`position fen ${request.fen}`);
  1082.  
  1083. if (engineMode == DEPTH_MODE) {
  1084. engine.postMessage('go depth ' + current_depth);
  1085. } else {
  1086. engine.postMessage('go movetime ' + current_movetime);
  1087. }
  1088.  
  1089. engine.onmessage = e => {
  1090. if (lastBestMoveID != request.id) {
  1091. return;
  1092. }
  1093. if (e.data.includes('bestmove')) {
  1094. let move = e.data.split(' ')[1];
  1095.  
  1096. moveResult(move.slice(0, 2), move.slice(2, 4), current_depth, true);
  1097. }
  1098.  
  1099. else if (e.data.includes('info')) {
  1100.  
  1101. const infoObj = LozzaUtils.extractInfo(e.data);
  1102. let depth = infoObj.depth || current_depth;
  1103. let move_time = infoObj.time || current_movetime;
  1104.  
  1105. let possible_move = e.data.slice(e.data.lastIndexOf("pv"), e.data.length).split(" ")[1];
  1106. possible_moves.push(possible_move);
  1107.  
  1108.  
  1109.  
  1110. if (engineMode == DEPTH_MODE) {
  1111. Interface.updateBestMoveProgress(`Depth: ${depth}`);
  1112. } else {
  1113. Interface.updateBestMoveProgress(`Move time: ${move_time} ms`);
  1114. }
  1115.  
  1116. }
  1117. Interface.engineLog(e.data);
  1118. };
  1119.  
  1120. } else {
  1121. getNodeBestMoves(request);
  1122. }
  1123. }
  1124.  
  1125. var updatingBestMove = false;
  1126.  
  1127. function observeNewMoves() {
  1128. updateBestMove();
  1129.  
  1130. const boardObserver = new MutationObserver(mutationArr => {
  1131. lastPlayerColor = playerColor;
  1132.  
  1133. updatePlayerColor(() => {
  1134. if (playerColor != lastPlayerColor) {
  1135. Interface.log(`Player color changed from ${lastPlayerColor} to ${playerColor}!`);
  1136.  
  1137. updateBestMove();
  1138. } else {
  1139. updateBestMove(mutationArr);
  1140. }
  1141. });
  1142.  
  1143.  
  1144. });
  1145.  
  1146. boardObserver.observe(chessBoardElem, { childList: true, subtree: true, attributes: true });
  1147. }
  1148.  
  1149. function addGuiPages() {
  1150. if (Gui?.document) return;
  1151.  
  1152. Gui.addPage("Main", `
  1153. <div class="rendered-form" id="main-tab">
  1154.  
  1155. <script>${CURRENT_SITE != LICHESS_ORG ? GM_getResourceText('jquery.js') : ""}</script>
  1156. <script>${CURRENT_SITE != LICHESS_ORG ? GM_getResourceText('chessboard.js') : ""}</script>
  1157.  
  1158.  
  1159. <div class="card">
  1160. <div class="card-body" id="chessboard" style="visibility:hidden;width:0px;height:0px;padding: 0 0 0 0;">
  1161. <div class="main-title-bar" style="visibility:hidden;width:0px;height:0px;padding: 0 0 0 0;">
  1162. <h4 class="card-title">Live Chessboard:</h4>
  1163. <p class="card-title" style="visibility:hidden;width:0px;height:0px;padding: 0 0 0 0;" id="best-move-progress"></p>
  1164. </div>
  1165.  
  1166. <div id="board" style="visibility:hidden;width:0px;height:0px;padding: 0 0 0 0;"></div>
  1167. </div>
  1168. <div id="orientation" style="visibility:hidden;width:0px;height:0px;padding: 0 0 0 0;" class="hidden"></div>
  1169. <div class="card-footer sideways-card"><input class="btn" type="button" value="Get Best Move" id="bestmove-btn"></input></div>
  1170. <div class="card-footer sideways-card" style="visibility:hidden;width:0px;height:0px;padding: 0 0 0 0;">FEN :<div style="visibility:hidden;width:0px;height:0px;padding: 0 0 0 0;" id="fen"></div></div>
  1171. <div class="card-footer sideways-card" style="visibility:hidden;width:0px;height:0px;padding: 0 0 0 0;">ENEMY SCORE :<div style="visibility:hidden;width:0px;height:0px;padding: 0 0 0 0;" id="enemy-score"></div></div>
  1172. <div class="card-footer sideways-card" style="visibility:hidden;width:0px;height:0px;padding: 0 0 0 0;">MY SCORE : <div style="visibility:hidden;width:0px;height:0px;padding: 0 0 0 0;" id="my-score"></div></div>
  1173. </div>
  1174. <script>
  1175. const orientationElem = document.querySelector('#orientation');
  1176. const fenElem = document.querySelector('#fen');
  1177.  
  1178. let board = ChessBoard('board', {
  1179. pieceTheme: '${repositoryRawURL}/content/chesspieces/{piece}.svg',
  1180. position: 'start',
  1181. orientation: '${playerColor == 'b' ? 'black' : 'white'}'
  1182. });
  1183.  
  1184. const orientationObserver = new MutationObserver(() => {
  1185. board = ChessBoard('board', {
  1186. pieceTheme: '${repositoryRawURL}/content/chesspieces/{piece}.svg',
  1187. position: fenElem.textContent,
  1188. orientation: orientationElem.textContent == 'b' ? 'black' : 'white'
  1189. });
  1190. });
  1191.  
  1192. const fenObserver = new MutationObserver(() => {
  1193. board.position(fenElem.textContent);
  1194. });
  1195.  
  1196. orientationObserver.observe(orientationElem, { attributes: true, childList: true, characterData: true });
  1197. fenObserver.observe(fenElem, { attributes: true, childList: true, characterData: true });
  1198. </script>
  1199. </div>
  1200. `);
  1201.  
  1202. Gui.addPage('Engine', `
  1203. <div class="rendered-form" id="Engine-tab">
  1204. <div class="card">
  1205. <div class="card-body">
  1206. <h4 class="card-title">Engine:</h4>
  1207. <div class="form-group field-select-engine">
  1208. <select class="form-control" name="select-engine" id="select-engine">
  1209. <option value="option-lozza" id="web-engine">Lozza</option>
  1210. <option value="option-stockfish" id="web-engine">Stockfish 5</option>
  1211. <option value="option-stockfish2" id="web-engine">Stockfish 2018</option>
  1212. <option value="option-nodeserver" id="local-engine">Node Server Engines</option>
  1213. </select>
  1214. </div>
  1215.  
  1216.  
  1217. <label class="container">Use book moves
  1218. <input type="checkbox" id="use-book-moves" ${use_book_moves == true ? 'checked' : ''}>
  1219. <span class="checkmark"></span>
  1220. </label>
  1221.  
  1222.  
  1223. <div id="reload-engine-div" style="display:${node_engine_id == engineIndex ? 'none' : 'block'};">
  1224.  
  1225.  
  1226. <label class="container">Enable Engine Reload
  1227. <input type="checkbox" id="reload-engine" ${reload_engine == true ? 'checked' : ''}>
  1228. <span class="checkmark"></span>
  1229. </label>
  1230.  
  1231. <div id="reload-count-div" style="display:${reload_engine == true ? 'block' : 'none'};">
  1232. <label for="reload-count">Reload Engine every</label>
  1233. <input type="number" id="reload-count" value="${reload_every}">
  1234. <label for="reload-count"> moves</label>
  1235. </div>
  1236. </div>
  1237.  
  1238. <div id="node-engine-div" style="display:${(engineIndex == node_engine_id) ? 'block' : 'none'};">
  1239. <div>
  1240. <label for="engine-url">Engine URL:</label>
  1241. <input type="text" id="engine-url" value="${node_engine_url}">
  1242. </div>
  1243.  
  1244. <div class="space"></div>
  1245. <div>
  1246. <label for="engine-name">Engine Name:</label>
  1247. <input type="text" id="engine-name" value="${node_engine_name}">
  1248. </div>
  1249. </div>
  1250. </div>
  1251. </div>
  1252.  
  1253. <div class="card">
  1254. <div class="card-body">
  1255. <h4 class="card-title">Engine Strength:</h4>
  1256.  
  1257. <h7 class="card-title">Engine Mode:</h7>
  1258. <div class="form-group field-select-engine-mode">
  1259. <select class="form-control" name="select-engine-mode" id="select-engine-mode">
  1260. <option value="option-depth" id="select-engine-mode-0">Depth</option>
  1261. <option value="option-movetime" id="select-engine-mode-1">Move time</option>
  1262. </select>
  1263. </div>
  1264.  
  1265.  
  1266.  
  1267. <h7 class="card-title">Engine Power:</h7>
  1268. <input type="range" class="form-range" min="${MIN_DEPTH}" max="${MAX_DEPTH}" step="1" value="${current_depth}" id="depth-range">
  1269. <input type="number" class="form-range" min="${MIN_DEPTH}" max="${MAX_DEPTH}" value="${current_depth}" id="depth-range-number">
  1270. <input type="range" class="form-range" min="${MIN_MOVETIME}" max="${MAX_MOVETIME}" step="50" value="${current_movetime}" id="movetime-range">
  1271. <input type="number" class="form-range" min="${MIN_MOVETIME}" max="${MAX_MOVETIME}" value="${current_movetime}" id="movetime-range-number">
  1272. </div>
  1273.  
  1274. <div class="card-footer sideways-card" id="elo">
  1275. <ul style="margin:0px;">
  1276. <li id="value">
  1277. Elo:
  1278. </li>
  1279. <li id="rank">
  1280. Rank:
  1281. </li>
  1282. <li id="power">
  1283. Elo:
  1284. </li>
  1285. </ul>
  1286. </div>
  1287. </div>
  1288.  
  1289. </div>
  1290. `);
  1291.  
  1292. Gui.addPage('Visual', `
  1293. <div class="rendered-form" id="Visual-tab">
  1294. <div class="card">
  1295. <div class="card-body">
  1296. <h4 class="card-title">Visual:</h4>
  1297.  
  1298.  
  1299.  
  1300. <div id="max-moves-div" style="display:${node_engine_id == engineIndex ? 'none' : 'block'};">
  1301. <div>
  1302. <label for="reload-count">Max Best Moves</label>
  1303. <input type="number" min="1" max="4" id="max-moves" value="${max_best_moves}">
  1304. </div>
  1305. </div>
  1306.  
  1307. <label class="container">Display moves on chessboard
  1308. <input type="checkbox" id="display-moves-on-site" ${displayMovesOnSite == true ? 'checked' : ''}>
  1309. <span class="checkmark"></span>
  1310. </label>
  1311.  
  1312.  
  1313. <label class="container">Display Opponent best moves
  1314. <input type="checkbox" id="show-opposite-moves" ${show_opposite_moves == true ? 'checked' : ''}>
  1315. <span class="checkmark"></span>
  1316. </label>
  1317.  
  1318. </div>
  1319. </div>
  1320.  
  1321. </div>
  1322. `);
  1323.  
  1324. Gui.addPage('Settings', `
  1325. <style>
  1326. body{
  1327. display:grid;
  1328. justify-items: center;
  1329. background-color:#fff;
  1330.  
  1331. transition:0.2s;
  1332. }
  1333.  
  1334.  
  1335. body.night{
  1336. background-color:#312e2b;
  1337. transition:0.2s;
  1338. }
  1339.  
  1340. .rendered-form{
  1341. width:500px;
  1342. }
  1343.  
  1344. .card{
  1345. border: 3px solid rgba(0,0,0,.2) !important;
  1346. background-color:#fff;
  1347. transition:0.2s;
  1348. }
  1349. .card.night{
  1350. background-color:#545454;
  1351. transition:0.2s;
  1352. }
  1353.  
  1354. .card-title{
  1355. color:#000;
  1356. transition:0.2s;
  1357. }
  1358. .card-title.night{
  1359. color:#fff;
  1360. transition:0.2s;
  1361. }
  1362.  
  1363. .form-control{
  1364. color:#000;
  1365. background-color:#fff;
  1366. transition:0.2s;
  1367. }
  1368. .form-control.night{
  1369. color:#fff;
  1370. background-color:#525252;
  1371. transition:0.2s;
  1372. }
  1373.  
  1374. label,input{
  1375. color:#000;
  1376. transition:0.2s;
  1377. }
  1378. label.night,input.night{
  1379. color:#fff;
  1380. transition:0.2s;
  1381. }
  1382.  
  1383. input{
  1384. background-color:#fff;
  1385. transition:0.2s;
  1386. }
  1387. input.night{
  1388. background-color:#525252;
  1389. transition:0.2s;
  1390. }
  1391.  
  1392.  
  1393. .list-group div{
  1394. background-color:#fff;
  1395. transition:0.2s;
  1396. }
  1397. .list-group.night div{
  1398. background-color:#bbb;
  1399. transition:0.2s;
  1400. }
  1401.  
  1402. .card-footer{
  1403. color:#000;
  1404. font-weight:bold;
  1405. transition:0.2s;
  1406. }
  1407. .card-footer.night{
  1408. color:#fff;
  1409. transition:0.2s;
  1410. }
  1411.  
  1412. #fen{
  1413. color:#000;
  1414. font-size: 15px;
  1415. word-break: break-word;
  1416. transition:0.2s;
  1417. }
  1418. #fen.night{
  1419. color:#fff;
  1420. transition:0.2s;
  1421. }
  1422.  
  1423.  
  1424. .nav-tabs .nav-link:hover {
  1425. border-color: #454646 #454646 #454646;
  1426. isolation: isolate;
  1427. }
  1428. .nav-tabs .nav-link.night:hover {
  1429. border-color: #e9ecef #e9ecef #dee2e6;
  1430. isolation: isolate;
  1431. }
  1432.  
  1433. .nav-tabs .nav-link.active{
  1434. background-color:#bbb;
  1435. }
  1436. .nav-tabs .nav-link.active.night{
  1437. background-color:#fff;
  1438. }
  1439.  
  1440. .btn{
  1441. border-color:#bbb;
  1442. border-width:3px;
  1443. width:100%;
  1444. transition :0.2s;
  1445. }
  1446. .btn:hover{
  1447. background-color: #0d6efd;
  1448. transition :0.2s;
  1449. }
  1450. .btn:active{
  1451. background-color: #0c5acd;
  1452. transition :0.2s;
  1453. }
  1454.  
  1455. .space{
  1456. height:10px;
  1457. }
  1458.  
  1459. .form-control,.list-group{
  1460. border: 2px solid #0000004f !important;
  1461. }
  1462. #reload-count{
  1463. width:15%;
  1464. }
  1465. .nav-link{
  1466. font-weight:bold;
  1467. }
  1468.  
  1469. .alert {
  1470. padding: 20px;
  1471. background-color: #f44336;
  1472. color: white;
  1473. }
  1474.  
  1475.  
  1476. .container {
  1477. display: block;
  1478. position: relative;
  1479. padding-left: 35px;
  1480. margin-bottom: 12px;
  1481. cursor: pointer;
  1482. font-size: 15px;
  1483. -webkit-user-select: none;
  1484. -moz-user-select: none;
  1485. -ms-user-select: none;
  1486. user-select: none;
  1487. }
  1488.  
  1489.  
  1490. .container input {
  1491. position: absolute;
  1492. opacity: 0;
  1493. cursor: pointer;
  1494. height: 0;
  1495. width: 0;
  1496. }
  1497.  
  1498.  
  1499. .checkmark {
  1500. display: flex;
  1501. justify-content: center;
  1502. align-items: center;
  1503. position: absolute;
  1504. top: 0;
  1505. left: 0;
  1506. height: 25px;
  1507. width: 25px;
  1508. background-color: #eee;
  1509. outline:3px solid #bbb;
  1510. }
  1511.  
  1512. .checkmark.night{
  1513. outline:none;
  1514. }
  1515.  
  1516. .container:hover input ~ .checkmark {
  1517. background-color: #ccc;
  1518. }
  1519.  
  1520.  
  1521. .container input:checked ~ .checkmark {
  1522. background-color: #2196F3;
  1523. }
  1524.  
  1525.  
  1526. .checkmark:after {
  1527. content: "";
  1528. position: absolute;
  1529. display: none;
  1530. }
  1531.  
  1532.  
  1533. .container input:checked ~ .checkmark:after {
  1534. display: block;
  1535. }
  1536.  
  1537. .container .checkmark:after {
  1538. width: 40%;
  1539. height: 70%;
  1540. margin-left: 1px;
  1541. border: solid white;
  1542. border-width: 0 3px 3px 0;
  1543. -webkit-transform: rotate(45deg);
  1544. -ms-transform: rotate(45deg);
  1545. transform: rotate(45deg);
  1546. }
  1547. </style>
  1548. <div class="rendered-form" id="Settings-tab">
  1549. <div class="card">
  1550. <div class="card-body">
  1551. <h4 class="card-title">Settings:</h4>
  1552. <input class="btn" type="button" value="Reset Settings" id="reset-settings">
  1553. <div class="space"></div>
  1554. <input class="btn" type="button" value="${nightMode == true ? 'Disable Night Mode' : 'Enable Night Mode'}" id="night-mode">
  1555. </div>
  1556. </div>
  1557. </div>
  1558.  
  1559. `);
  1560.  
  1561. }
  1562.  
  1563. function fixDepthMoveTimeInput(depthRangeElem, depthRangeNumberElem, moveTimeRangeElem, moveTimeRangeNumberElem, eloElem) {
  1564. if (engineMode == DEPTH_MODE) {
  1565. if (isNotCompatibleBrowser()) {
  1566. depthRangeElem.style.display = "none";
  1567. depthRangeNumberElem.style.display = "block";
  1568. moveTimeRangeElem.style.display = "none";
  1569. moveTimeRangeNumberElem.style.display = "none";
  1570. } else {
  1571. depthRangeElem.style.display = "block";
  1572. depthRangeNumberElem.style.display = "none";
  1573. moveTimeRangeElem.style.display = "none";
  1574. moveTimeRangeNumberElem.style.display = "none";
  1575. }
  1576. } else {
  1577. if (isNotCompatibleBrowser()) {
  1578. depthRangeElem.style.display = "none";
  1579. depthRangeNumberElem.style.display = "none";
  1580. moveTimeRangeElem.style.display = "none";
  1581. moveTimeRangeNumberElem.style.display = "block";
  1582. } else {
  1583. depthRangeElem.style.display = "none";
  1584. depthRangeNumberElem.style.display = "none";
  1585. moveTimeRangeElem.style.display = "block";
  1586. moveTimeRangeNumberElem.style.display = "none";
  1587. }
  1588. }
  1589.  
  1590.  
  1591.  
  1592. setEloDescription(eloElem);
  1593. }
  1594.  
  1595.  
  1596. async function resetSettings() {
  1597. await GM_setValue(dbValues.nightMode, undefined);
  1598.  
  1599. Gui.close();
  1600.  
  1601. initialize();
  1602. }
  1603.  
  1604. function openGUI() {
  1605.  
  1606.  
  1607.  
  1608. Gui.open(() => {
  1609. const bodyElem = Gui.document.querySelector("body");
  1610. const cardElem = Gui.document.querySelectorAll(".card");
  1611. const cardTitleElem = Gui.document.querySelectorAll(".card-title");
  1612. const FormControlElem = Gui.document.querySelectorAll(".form-control");
  1613. const labelElem = Gui.document.querySelectorAll("label");
  1614. const checkMarkElem = Gui.document.querySelectorAll(".checkmark");
  1615. const inputElem = Gui.document.querySelectorAll("input");
  1616. const listGroupElem = Gui.document.querySelectorAll(".list-group");
  1617. const cardFooterElem = Gui.document.querySelectorAll(".card-footer");
  1618. const textMutedElem = Gui.document.querySelectorAll("#fen");
  1619. const navLinkElem = Gui.document.querySelectorAll(".nav-tabs .nav-link");
  1620.  
  1621. const depthRangeElem = Gui.document.querySelector('#depth-range');
  1622. const depthRangeNumberElem = Gui.document.querySelector('#depth-range-number');
  1623. const maxMovesElem = Gui.document.querySelector('#max-moves');
  1624. const maxMovesDivElem = Gui.document.querySelector('#max-moves-div');
  1625. const moveTimeRangeElem = Gui.document.querySelector('#movetime-range');
  1626. const moveTimeRangeNumberElem = Gui.document.querySelector('#movetime-range-number');
  1627. const engineModeElem = Gui.document.querySelector('#select-engine-mode');
  1628. const engineElem = Gui.document.querySelector('#select-engine');
  1629. const engineNameDivElem = Gui.document.querySelector('#node-engine-div');
  1630. const reloadEngineDivElem = Gui.document.querySelector('#reload-engine-div');
  1631. const reloadEngineElem = Gui.document.querySelector('#reload-engine');
  1632. const reloadEveryDivElem = Gui.document.querySelector('#reload-count-div');
  1633. const reloadEveryElem = Gui.document.querySelector('#reload-count');
  1634. const nodeEngineNameElem = Gui.document.querySelector('#engine-name');
  1635. const nodeEngineUrlElem = Gui.document.querySelector('#engine-url');
  1636. const useLocalEngineElem = Gui.document.querySelector('#use-book-moves');
  1637. const showOppositeMovesElem = Gui.document.querySelector('#show-opposite-moves');
  1638. const displayMovesOnSiteElem = Gui.document.querySelector('#display-moves-on-site');
  1639. const eloElem = Gui.document.querySelector('#elo');
  1640. const getBestMoveElem = Gui.document.querySelector('#bestmove-btn');
  1641. const nightModeElem = Gui.document.querySelector('#night-mode');
  1642. const tutoElem = Gui.document.querySelector('#tuto');
  1643. const resetElem = Gui.document.querySelector('#reset-settings');
  1644. const btnCloseUIElem = Gui.document.getElementById("button-close-gui");
  1645.  
  1646. const setNightClassName = (elem) => {
  1647. const pos = elem.className.indexOf("night");
  1648. if (pos == -1) {
  1649. elem.className += " night";
  1650. }
  1651. }
  1652.  
  1653. const removeNightClassName = (elem) => {
  1654. const pos = elem.className.indexOf("night");
  1655. if (pos != -1) {
  1656. elem.className = elem.className.slice(0, pos - 1);
  1657. }
  1658. }
  1659.  
  1660. const setNightClassNames = (elems) => {
  1661. for (var a = 0; a < elems.length; a++) {
  1662. setNightClassName(elems[a]);
  1663. }
  1664. }
  1665. const removeNightClassNames = (elems) => {
  1666. for (var a = 0; a < elems.length; a++) {
  1667. removeNightClassName(elems[a]);
  1668. }
  1669. }
  1670.  
  1671.  
  1672. const checkNightMode = () => {
  1673. if (nightMode) {
  1674. setNightClassName(bodyElem);
  1675. setNightClassNames(cardElem);
  1676. setNightClassNames(cardTitleElem);
  1677. setNightClassNames(FormControlElem);
  1678. setNightClassNames(inputElem);
  1679. setNightClassNames(labelElem);
  1680. setNightClassNames(checkMarkElem);
  1681. setNightClassNames(listGroupElem);
  1682. setNightClassNames(cardFooterElem);
  1683. setNightClassNames(textMutedElem);
  1684. setNightClassNames(navLinkElem);
  1685. } else {
  1686. removeNightClassName(bodyElem);
  1687. removeNightClassNames(cardElem);
  1688. removeNightClassNames(cardTitleElem);
  1689. removeNightClassNames(FormControlElem);
  1690. removeNightClassNames(inputElem);
  1691. removeNightClassNames(labelElem);
  1692. removeNightClassNames(checkMarkElem);
  1693. removeNightClassNames(listGroupElem);
  1694. removeNightClassNames(cardFooterElem);
  1695. removeNightClassNames(textMutedElem);
  1696. removeNightClassNames(navLinkElem);
  1697. }
  1698. }
  1699.  
  1700. fixDepthMoveTimeInput(depthRangeElem, depthRangeNumberElem, moveTimeRangeElem, moveTimeRangeNumberElem, eloElem);
  1701. engineElem.selectedIndex = engineIndex;
  1702. engineModeElem.selectedIndex = engineMode;
  1703. checkNightMode();
  1704.  
  1705.  
  1706. // compatibility fix
  1707. if (isNotCompatibleBrowser()) {
  1708. var forms = Gui.document.querySelectorAll('.rendered-form');
  1709. for (var a = 0; a < forms.length; a++) {
  1710. forms[a].style.width = "auto";
  1711. }
  1712. Gui.document.querySelector('#gui').style.minWidth = "350px";
  1713. Gui.document.querySelector('#content').style.maxHeight = "200px";
  1714. Gui.document.querySelector('#content').style.overflow = "scroll";
  1715. Gui.document.querySelector('#chessboard').remove();
  1716. Gui.document.querySelector('#orientation').remove();
  1717. Gui.document.querySelector('#engine-log-container').style.maxHeight = "100px";
  1718. Gui.document.querySelector('#engine-log-container').style.overflow = "scroll";
  1719. Gui.document.querySelector('#userscript-log-container').style.maxHeight = "100px";
  1720. Gui.document.querySelector('#userscript-log-container').style.overflow = "scroll";
  1721. }
  1722.  
  1723.  
  1724. if (CURRENT_SITE == LICHESS_ORG) {
  1725. // disabled web engine selection due to cors issues
  1726. engineElem.childNodes.forEach(elem => {
  1727. if (elem.id == "web-engine") {
  1728. elem.disabled = true;
  1729. }
  1730. })
  1731.  
  1732. if(!isNotCompatibleBrowser()){
  1733. Gui.document.querySelector('#chessboard').remove();
  1734. Gui.document.querySelector('#orientation').remove();
  1735. }
  1736.  
  1737. engineElem.selectedIndex = node_engine_id;
  1738. maxMovesDivElem.style.display = "none";
  1739. engineNameDivElem.style.display = "block";
  1740. reloadEngineDivElem.style.display = "none";
  1741. }
  1742.  
  1743. btnCloseUIElem.onclick = () => {
  1744. toggleGUI();
  1745. }
  1746.  
  1747.  
  1748. resetElem.onclick = () => {
  1749. resetSettings();
  1750. reloadChessEngine();
  1751. }
  1752.  
  1753. nightModeElem.onclick = () => {
  1754. if (nightMode) {
  1755. nightMode = false;
  1756. nightModeElem.value = "Enable Night Mode";
  1757. } else {
  1758. nightMode = true;
  1759. nightModeElem.value = "Disable Night Mode";
  1760. }
  1761.  
  1762.  
  1763.  
  1764. checkNightMode();
  1765.  
  1766. GM_setValue(dbValues.nightMode, nightMode);
  1767.  
  1768.  
  1769. }
  1770.  
  1771. getBestMoveElem.onclick = () => {
  1772. if (forcedBestMove)
  1773. return;
  1774.  
  1775. getBestMoveElem.disabled = true;
  1776. forcedBestMove = true;
  1777.  
  1778.  
  1779.  
  1780. updateBoard();
  1781. sendBestMove();
  1782. }
  1783.  
  1784. engineModeElem.onchange = () => {
  1785. engineMode = engineModeElem.selectedIndex;
  1786. GM_setValue(dbValues.engineMode, engineMode);
  1787.  
  1788. fixDepthMoveTimeInput(depthRangeElem, depthRangeNumberElem, moveTimeRangeElem, moveTimeRangeNumberElem, eloElem);
  1789. }
  1790. nodeEngineNameElem.onchange = () => {
  1791. node_engine_name = nodeEngineNameElem.value;
  1792. GM_setValue(dbValues.node_engine_name, node_engine_name);
  1793. }
  1794. nodeEngineUrlElem.onchange = () => {
  1795. node_engine_url = nodeEngineUrlElem.value;
  1796. GM_setValue(dbValues.node_engine_url, node_engine_url);
  1797. }
  1798.  
  1799.  
  1800.  
  1801. reloadEngineElem.onchange = () => {
  1802. reload_engine = reloadEngineElem.checked;
  1803.  
  1804. if (reload_engine) {
  1805. reloadEveryDivElem.style.display = "block";
  1806. } else {
  1807. reloadEveryDivElem.style.display = "none";
  1808. }
  1809.  
  1810. GM_setValue(dbValues.reload_engine, reload_engine);
  1811. }
  1812.  
  1813. reloadEveryElem.onchange = () => {
  1814. reload_every = reloadEveryElem.value;
  1815. GM_setValue(dbValues.reload_every, reload_every);
  1816. }
  1817.  
  1818. engineElem.onchange = () => {
  1819. lastEngine = engineIndex;
  1820. engineIndex = engineElem.selectedIndex;
  1821. GM_setValue(dbValues.engineIndex, engineIndex);
  1822.  
  1823.  
  1824. if (node_engine_id == engineIndex) {
  1825. reloadEngineDivElem.style.display = "none";
  1826. engineNameDivElem.style.display = "block";
  1827. maxMovesDivElem.style.display = "none";
  1828. }
  1829. else {
  1830. reloadEngineDivElem.style.display = "block";
  1831. engineNameDivElem.style.display = "none";
  1832. maxMovesDivElem.style.display = "block";
  1833. }
  1834.  
  1835. if (engineObjectURL) {
  1836. URL.revokeObjectURL(engineObjectURL);
  1837. engineObjectURL = null;
  1838. }
  1839.  
  1840. reloadChessEngine(true, () => {
  1841. Interface.boardUtils.removeBestMarkings();
  1842.  
  1843. removeSiteMoveMarkings();
  1844.  
  1845. Interface.boardUtils.updateBoardPower(0, 0);
  1846. });
  1847. }
  1848.  
  1849. depthRangeElem.onchange = () => {
  1850. changeEnginePower(depthRangeElem.value, eloElem);
  1851. };
  1852.  
  1853. depthRangeNumberElem.onchange = () => {
  1854. changeEnginePower(depthRangeNumberElem.value, eloElem);
  1855. };
  1856.  
  1857. maxMovesElem.onchange = () => {
  1858. max_best_moves = maxMovesElem.value;
  1859. GM_setValue(dbValues.max_best_moves, max_best_moves);
  1860. };
  1861.  
  1862.  
  1863.  
  1864. moveTimeRangeElem.onchange = () => {
  1865. changeEnginePower(moveTimeRangeElem.value, eloElem);
  1866. };
  1867.  
  1868. moveTimeRangeNumberElem.onchange = () => {
  1869. changeEnginePower(moveTimeRangeNumberElem.value, eloElem);
  1870. };
  1871.  
  1872. showOppositeMovesElem.onchange = () => {
  1873. show_opposite_moves = showOppositeMovesElem.checked;
  1874.  
  1875.  
  1876. GM_setValue(dbValues.show_opposite_moves, show_opposite_moves);
  1877. }
  1878.  
  1879. useLocalEngineElem.onchange = () => {
  1880. use_book_moves = useLocalEngineElem.checked;
  1881.  
  1882.  
  1883.  
  1884. GM_setValue(dbValues.use_book_moves, use_book_moves);
  1885. }
  1886.  
  1887. displayMovesOnSiteElem.onchange = () => {
  1888. displayMovesOnSite = displayMovesOnSiteElem.checked;
  1889.  
  1890.  
  1891. GM_setValue(dbValues.displayMovesOnSite, displayMovesOnSite);
  1892. if(displayMovesOnSite)
  1893. {
  1894. getBestMoveElem.click();
  1895. }
  1896. else
  1897. {
  1898. removeSiteMoveMarkings();
  1899. }
  1900.  
  1901. };
  1902.  
  1903. window.onunload = () => {
  1904. if (Gui.window && !Gui.window.closed) {
  1905. Gui.window.close();
  1906. }
  1907. };
  1908.  
  1909. const isWindowClosed = setInterval(() => {
  1910. if (Gui.window.closed) {
  1911. clearInterval(isWindowClosed);
  1912. if (engine != null)
  1913. engine.terminate();
  1914. }
  1915. }, 1000);
  1916.  
  1917.  
  1918. if(!displayMovesOnSite)
  1919. {
  1920. removeSiteMoveMarkings();
  1921. if(displayMovesOnSiteElem.checked)
  1922. {
  1923. displayMovesOnSiteElem.click();
  1924. }
  1925. }
  1926.  
  1927. Interface.log('Initialized GUI!');
  1928.  
  1929. observeNewMoves();
  1930. });
  1931. }
  1932.  
  1933. function changeEnginePower(val, eloElem) {
  1934. if (engineMode == DEPTH_MODE) {
  1935. current_depth = val
  1936. GM_setValue(dbValues.current_depth, current_depth);
  1937. } else {
  1938. current_movetime = val
  1939. GM_setValue(dbValues.current_movetime, current_movetime);
  1940. }
  1941.  
  1942.  
  1943. setEloDescription(eloElem);
  1944. }
  1945.  
  1946. function reloadChessEngine(forced, callback) {
  1947. // reload only if using local engines
  1948. if (node_engine_id == engineIndex && forced == false) {
  1949. callback();
  1950. }
  1951. else if (reload_engine == true && reload_count >= reload_every || forced == true) {
  1952. reload_count = 1;
  1953. Interface.log(`Reloading the chess engine!`);
  1954.  
  1955. if (engine)
  1956. engine.terminate();
  1957.  
  1958. loadChessEngine(callback);
  1959. }
  1960. else {
  1961. reload_count = reload_count + 1;
  1962. callback();
  1963. }
  1964. }
  1965.  
  1966.  
  1967.  
  1968. function loadChessEngine(callback) {
  1969. // exclude lichess.org
  1970. if (CURRENT_SITE == LICHESS_ORG) {
  1971. return callback();
  1972. }
  1973.  
  1974. if (!engineObjectURL) {
  1975. if (engineIndex == 0)
  1976. engineObjectURL = URL.createObjectURL(new Blob([GM_getResourceText('lozza.js')], { type: 'application/javascript' }));
  1977. else if (engineIndex == 1)
  1978. engineObjectURL = URL.createObjectURL(new Blob([GM_getResourceText('stockfish-5.js')], { type: 'application/javascript' }));
  1979. else if (engineIndex == 2)
  1980. engineObjectURL = URL.createObjectURL(new Blob([GM_getResourceText('stockfish-2018.js')], { type: 'application/javascript' }));
  1981. }
  1982.  
  1983. if (engineObjectURL) {
  1984. engine = new Worker(engineObjectURL);
  1985.  
  1986. engine.postMessage('ucinewgame');
  1987.  
  1988. Interface.log(`Loaded the chess engine!`);
  1989. }
  1990.  
  1991. callback();
  1992. }
  1993.  
  1994.  
  1995.  
  1996. function updatePlayerColor(callback) {
  1997. const boardOrientation = Interface.getBoardOrientation();
  1998.  
  1999. playerColor = boardOrientation;
  2000. turn = boardOrientation;
  2001.  
  2002.  
  2003. Interface.boardUtils.updateBoardOrientation(playerColor);
  2004.  
  2005. callback();
  2006. }
  2007.  
  2008. function initialize() {
  2009. Interface = new InterfaceUtils();
  2010. LozzaUtils = new LozzaUtility();
  2011.  
  2012. const boardOrientation = Interface.getBoardOrientation();
  2013. turn = boardOrientation;
  2014.  
  2015. initializeDatabase(() => {
  2016. loadChessEngine(() => {
  2017. updatePlayerColor(() => {
  2018. addGuiPages();
  2019. openGUI();
  2020. });
  2021. });
  2022. });
  2023. }
  2024.  
  2025. if (typeof GM_registerMenuCommand == 'function') {
  2026. GM_registerMenuCommand("Open Smart Chess Bot", e => {
  2027. if (chessBoardElem) {
  2028. initialize();
  2029. }
  2030. }, 's');
  2031. }
  2032.  
  2033.  
  2034. const waitForChessBoard = setInterval(() => {
  2035. if (CURRENT_SITE) {
  2036. return;
  2037. }
  2038.  
  2039.  
  2040.  
  2041. if (window.location.href.includes("lichess.org")) {
  2042. CURRENT_SITE = LICHESS_ORG;
  2043. boardElem = document.querySelector('.main-board');
  2044. firstPieceElem = document.querySelector('piece');
  2045. }
  2046. else if (window.location.href.includes("chess.com")) {
  2047. CURRENT_SITE = CHESS_COM;
  2048. boardElem = document.querySelector('.board');
  2049. firstPieceElem = document.querySelector('.piece');
  2050. }
  2051.  
  2052.  
  2053. if (boardElem && firstPieceElem && chessBoardElem != boardElem) {
  2054. chessBoardElem = boardElem;
  2055.  
  2056. initialize();
  2057. clearInterval(waitForChessBoard);
  2058. }
  2059. }, 2000);
  2060.  
  2061.  
  2062. async function initializeDatabase(callback) {
  2063. nightMode = true;
  2064. engineMode = 0;
  2065. engineIndex = 0;
  2066. reload_engine = false;
  2067. reload_every = 10;
  2068. enableUserLog = false;
  2069. enableEngineLog = false;
  2070. displayMovesOnSite = false;
  2071. show_opposite_moves = false;
  2072. use_book_moves = false;
  2073. node_engine_url = "http://localhost:5000";
  2074. node_engine_name = "stockfish-15.exe"
  2075. current_depth = Math.round(MAX_DEPTH / 2);
  2076. current_movetime = Math.round(MAX_MOVETIME / 3);
  2077. max_best_moves = 1;
  2078.  
  2079. if (GM_getValue(dbValues.nightMode) == undefined) {
  2080. await GM_setValue(dbValues.nightMode, nightMode);
  2081. await GM_setValue(dbValues.engineMode, engineMode);
  2082. await GM_setValue(dbValues.engineIndex, engineIndex);
  2083. await GM_setValue(dbValues.reload_engine, reload_engine);
  2084. await GM_setValue(dbValues.reload_every, reload_every);
  2085. await GM_setValue(dbValues.enableUserLog, enableUserLog);
  2086. await GM_setValue(dbValues.enableEngineLog, enableEngineLog);
  2087. await GM_setValue(dbValues.displayMovesOnSite, displayMovesOnSite);
  2088. await GM_setValue(dbValues.show_opposite_moves, show_opposite_moves);
  2089. await GM_setValue(dbValues.use_book_moves, use_book_moves);
  2090. await GM_setValue(dbValues.node_engine_url, node_engine_url);
  2091. await GM_setValue(dbValues.node_engine_name, node_engine_name);
  2092. await GM_setValue(dbValues.current_depth, current_depth);
  2093. await GM_setValue(dbValues.current_movetime, current_movetime);
  2094. await GM_setValue(dbValues.max_best_moves, max_best_moves);
  2095. callback();
  2096. } else {
  2097. nightMode = await GM_getValue(dbValues.nightMode);
  2098. engineMode = await GM_getValue(dbValues.engineMode);
  2099. engineIndex = await GM_getValue(dbValues.engineIndex);
  2100. reload_engine = await GM_getValue(dbValues.reload_engine);
  2101. reload_every = await GM_getValue(dbValues.reload_every);
  2102. enableUserLog = await GM_getValue(dbValues.enableUserLog);
  2103. enableEngineLog = await GM_getValue(dbValues.enableEngineLog);
  2104. displayMovesOnSite = await GM_getValue(dbValues.displayMovesOnSite);
  2105. show_opposite_moves = await GM_getValue(dbValues.show_opposite_moves);
  2106. use_book_moves = await GM_getValue(dbValues.use_book_moves);
  2107. node_engine_url = await GM_getValue(dbValues.node_engine_url);
  2108. node_engine_name = await GM_getValue(dbValues.node_engine_name);
  2109. current_depth = await GM_getValue(dbValues.current_depth);
  2110. current_movetime = await GM_getValue(dbValues.current_movetime);
  2111. max_best_moves = await GM_getValue(dbValues.max_best_moves);
  2112. callback();
  2113. }
  2114. }
  2115.  
  2116.  
  2117.  
  2118. document.addEventListener('keydown', function(event) {
  2119. if (event.key === 'g' || event.key === 'G') {
  2120. toggleGUI();
  2121. }
  2122. });