C.A.S (Chess.com Assistance System)

Chess analysis bot made for educational purposes only (Chrome + Firefox + Edge ...)

当前为 2023-02-26 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name C.A.S (Chess.com Assistance System)
  3. // @namespace sayfpack
  4. // @author sayfpack
  5. // @version 4.5
  6. // @homepageURL https://github.com/sayfpack13/chess-analysis-bot
  7. // @supportURL https://mmgc.life/
  8. // @match https://www.chess.com/*
  9. // @grant GM_getValue
  10. // @grant GM_setValue
  11. // @grant GM_xmlhttpRequest
  12. // @grant GM_getResourceText
  13. // @grant GM_registerMenuCommand
  14. // @description Chess analysis bot made for educational purposes only (Chrome + Firefox + Edge ...)
  15. // @require https://greasyfork.org/scripts/460400-usergui-js/code/userguijs.js?version=1152084
  16. // @resource jquery.js https://cdn.jsdelivr.net/npm/jquery@3.6.3/dist/jquery.min.js
  17. // @resource chessboard.js https://raw.githubusercontent.com/sayfpack13/chess-analysis-bot/main/tampermonkey%20script/content/chessboard.js
  18. // @resource chessboard.css https://raw.githubusercontent.com/sayfpack13/chess-analysis-bot/main/tampermonkey%20script/content/chessboard.css
  19. // @resource lozza.js https://raw.githubusercontent.com/sayfpack13/chess-analysis-bot/main/tampermonkey%20script/content/lozza.js
  20. // @resource stockfish.js https://github.com/exoticorn/stockfish-js/releases/download/sf_5_js/stockfish.js
  21. // @resource stockfish2.js https://github.com/lichess-org/stockfish.js/releases/download/ddugovic-250718/stockfish.js
  22. // @run-at document-start
  23. // @inject-into content
  24. // ==/UserScript==
  25.  
  26. /*
  27. e88~-_ e ,d88~~\
  28. d888 \ d8b 8888
  29. 8888 /Y88b `Y88b
  30. 8888 / Y88b `Y88b,
  31. Y888 / d88b /____Y88b d88b 8888
  32. "88_-~ Y88P / Y88b Y88P \__88P'
  33. */
  34.  
  35. // VARS
  36. const repositoryRawURL = 'https://raw.githubusercontent.com/sayfpack13/chess-analysis-bot/main/tampermonkey%20script';
  37. const LICHESS_API = "https://lichess.org/api/cloud-eval";
  38.  
  39. const MAX_DEPTH = 20;
  40. const MIN_DEPTH = 1;
  41. const MAX_MOVETIME = 2000;
  42. const MIN_MOVETIME = 50;
  43. const MAX_ELO = 3500;
  44. const DEPTH_MODE = 0;
  45. const MOVETIME_MODE = 1;
  46.  
  47.  
  48.  
  49. let engineMode = 0; // engine mode (0:depth / 1:movetime)
  50. let engineIndex = 0; // engine index (lozza => 0, stockfish => 1...)
  51. let reload_every = 10; // reload engine after x moves
  52. let enableUserLog = true; // enable engine log
  53. let displayMovesOnSite = false; // display moves on chess board
  54. let show_opposite_moves = false; // show opponent best moves if available
  55. let use_book_moves = false; // use lichess api to get book moves
  56. let node_engine_url = "http://localhost:5000"; // node server api url
  57. let node_engine_name = "stockfish-15.exe" // default engine name (node server engine only)
  58.  
  59.  
  60.  
  61. let current_depth = Math.round(MAX_DEPTH / 2);
  62. let current_movetime = Math.round(MAX_MOVETIME / 2);
  63. let node_engine_id = [3];
  64.  
  65. let Gui;
  66. let closedGui = false;
  67. let reload_count = 1;
  68.  
  69. const rank = ["Beginner", "Intermediate", "Advanced", "Expert", "Master"];
  70.  
  71. const dbValues = {
  72. openGuiAutomatically: 'openGuiAutomatically',
  73. subtleMode: 'subtleMode',
  74. subtletiness: 'subtletiness'
  75. };
  76.  
  77.  
  78.  
  79.  
  80. let Interface = null;
  81. let LozzaUtils = null;
  82.  
  83. let initialized = false;
  84. let firstMoveMade = false;
  85.  
  86.  
  87. let engine = null;
  88. let engineObjectURL = null;
  89. let lastEngine = engineIndex;
  90.  
  91. let chessBoardElem = null;
  92. let turn = '-';
  93. let playerColor = null;
  94. let isPlayerTurn = null;
  95. let lastFen = null;
  96.  
  97. let uiChessBoard = null;
  98.  
  99. let activeGuiMoveHighlights = [];
  100. let activeSiteMoveHighlights = [];
  101.  
  102. let engineLogNum = 1;
  103. let userscriptLogNum = 1;
  104. let enemyScore = 0;
  105. let myScore = 0;
  106.  
  107.  
  108. function moveResult(givenFen = "", from, to, power, clear = true) {
  109. // make sure both fens are equal
  110. // because engine requests/results are async
  111.  
  112. if (givenFen != "" && givenFen != currentFen) {
  113. return;
  114. }
  115.  
  116.  
  117. if (from.length < 2 || to.length < 2) {
  118. return;
  119. }
  120.  
  121. if (clear) {
  122. removeSiteMoveMarkings();
  123. Interface.boardUtils.removeBestMarkings();
  124. }
  125.  
  126.  
  127.  
  128. if (isPlayerTurn) // my turn
  129. myScore = myScore + Number(power);
  130. else
  131. enemyScore = enemyScore + Number(power);
  132.  
  133. Interface.boardUtils.updateBoardPower(myScore, enemyScore);
  134.  
  135. if (displayMovesOnSite || (!isPlayerTurn && show_opposite_moves)) {
  136. markMoveToSite(from, to);
  137. }
  138.  
  139.  
  140.  
  141. Interface.boardUtils.markMove(from, to);
  142. Interface.stopBestMoveProcessingAnimation();
  143.  
  144.  
  145. }
  146.  
  147.  
  148. function getBookMoves(turn) {
  149. GM_xmlhttpRequest({
  150. method: "GET",
  151. url: LICHESS_API + "?fen=" + currentFen + "&multiPv=3&variant=fromPosition",
  152. headers: {
  153. "Content-Type": "application/json"
  154. },
  155. onload: function (response) {
  156. if (response.response.includes("error")) {
  157.  
  158. getBestMoves(turn);
  159.  
  160. } else {
  161. let data = JSON.parse(response.response);
  162. let nextMove = data.pvs[0].moves.split(' ')[0];
  163. let score = current_depth;
  164.  
  165.  
  166. moveResult(currentFen, nextMove.slice(0, 2), nextMove.slice(2, 4), score, true);
  167. }
  168.  
  169.  
  170. }, onerror: function (error) {
  171. getBestMoves(turn);
  172.  
  173. }
  174. });
  175.  
  176. }
  177.  
  178. function getNodeBestMoves(turn) {
  179.  
  180.  
  181. GM_xmlhttpRequest({
  182. method: "GET",
  183. url: node_engine_url + "/getBestMove?fen=" + currentFen + "&engine_mode=" + engineMode + "&depth=" + current_depth + "&movetime=" + current_movetime + "&turn=" + turn + "&engine_name=" + node_engine_name,
  184. headers: {
  185. "Content-Type": "application/json"
  186. },
  187. onload: function (response) {
  188. if (response.response == "false") {
  189. return;
  190. }
  191. let data = JSON.parse(response.response);
  192. let fen = data.fen;
  193. let depth = data.depth;
  194. let movetime = data.movetime;
  195. let power = data.score;
  196. let nextMove = data.move;
  197.  
  198.  
  199. if (engineMode == DEPTH_MODE) {
  200. Interface.updateBestMoveProgress(`Depth: ${depth}`);
  201. } else {
  202. Interface.updateBestMoveProgress(`Move time: ${movetime} ms`);
  203. }
  204.  
  205.  
  206.  
  207. moveResult(fen, nextMove.slice(0, 2), nextMove.slice(2, 4), power, true);
  208.  
  209.  
  210.  
  211. }, onerror: function (error) {
  212. console.log("check node server !!");
  213.  
  214. }
  215. });
  216.  
  217. }
  218.  
  219. function getElo() {
  220. let elo;
  221. if (engineMode == DEPTH_MODE) {
  222. elo = MAX_ELO / MAX_DEPTH;
  223. elo *= current_depth;
  224. } else {
  225. elo = MAX_ELO / MAX_MOVETIME;
  226. elo *= current_movetime;
  227. }
  228. return elo;
  229. }
  230.  
  231. function getRank() {
  232. let part;
  233. if (engineMode == DEPTH_MODE) {
  234. part = current_depth / (MAX_DEPTH / rank.length);
  235. } else {
  236. part = current_movetime / (MAX_MOVETIME / rank.length);
  237. }
  238. part = Math.round(part);
  239.  
  240. if (part >= rank.length) {
  241. part = rank.length - 1;
  242. }
  243.  
  244. return rank[part];
  245. }
  246.  
  247.  
  248.  
  249.  
  250.  
  251.  
  252. function getEloDescription() {
  253. let desc = `Elo: ${getElo()}, Rank: ${getRank()}, `;
  254. if (engineMode == DEPTH_MODE) {
  255. desc += `Depth: ${current_depth}`;
  256. } else {
  257. desc += `Move Time: ${current_movetime} ms`;
  258. }
  259. return desc;
  260. }
  261.  
  262. function isNotCompatibleBrowser() {
  263. return navigator.userAgent.toLowerCase().includes("firefox")
  264. }
  265.  
  266. onload = function () {
  267. if (isNotCompatibleBrowser()) {
  268. Gui = new UserGui;
  269. }
  270.  
  271. }
  272.  
  273. if (!isNotCompatibleBrowser()) {
  274. Gui = new UserGui;
  275. } else {
  276. onload();
  277. }
  278.  
  279.  
  280. Gui.settings.window.title = 'C.A.S';
  281. Gui.settings.window.external = true;
  282. Gui.settings.window.size.width = 500;
  283. Gui.settings.gui.external.popup = false;
  284. Gui.settings.gui.external.style += GM_getResourceText('chessboard.css');
  285. Gui.settings.gui.external.style += `
  286. div[class^='board'] {
  287. background-color: black;
  288. }
  289. .best-move-from {
  290. background-color: #31ff7f;
  291. transform: scale(0.85);
  292. }
  293. .best-move-to {
  294. background-color: #31ff7f;
  295. }
  296. .negative-best-move-from {
  297. background-color: #fd0000;
  298. transform: scale(0.85);
  299. }
  300. .negative-best-move-to {
  301. background-color: #fd0000;
  302. }
  303. body {
  304. display: block;
  305. margin-left: auto;
  306. margin-right: auto;
  307. width: 360px;
  308. }
  309. #fen {
  310. margin-left: 10px;
  311. }
  312. #engine-log-container {
  313. max-height: 35vh;
  314. overflow: auto!important;
  315. }
  316. #userscript-log-container {
  317. max-height: 35vh;
  318. overflow: auto!important;
  319. }
  320. .sideways-card {
  321. display: flex;
  322. align-items: center;
  323. justify-content: space-between;
  324. }
  325. .rendered-form .card {
  326. margin-bottom: 10px;
  327. }
  328. .hidden {
  329. display: none;
  330. }
  331. .main-title-bar {
  332. display: flex;
  333. justify-content: space-between;
  334. }
  335. @keyframes wiggle {
  336. 0% { transform: scale(1); }
  337. 80% { transform: scale(1); }
  338. 85% { transform: scale(1.1); }
  339. 95% { transform: scale(1); }
  340. 100% { transform: scale(1); }
  341. }
  342.  
  343. .wiggle {
  344. display: inline-block;
  345. animation: wiggle 1s infinite;
  346. }
  347. `;
  348.  
  349. function FenUtils() {
  350. this.board = [
  351. [1, 1, 1, 1, 1, 1, 1, 1],
  352. [1, 1, 1, 1, 1, 1, 1, 1],
  353. [1, 1, 1, 1, 1, 1, 1, 1],
  354. [1, 1, 1, 1, 1, 1, 1, 1],
  355. [1, 1, 1, 1, 1, 1, 1, 1],
  356. [1, 1, 1, 1, 1, 1, 1, 1],
  357. [1, 1, 1, 1, 1, 1, 1, 1],
  358. [1, 1, 1, 1, 1, 1, 1, 1],
  359. ];
  360.  
  361. this.pieceCodeToFen = pieceStr => {
  362. const [pieceColor, pieceName] = pieceStr.split('');
  363.  
  364. return pieceColor == 'w' ? pieceName.toUpperCase() : pieceName.toLowerCase();
  365. }
  366.  
  367. this.getFenCodeFromPieceElem = pieceElem => {
  368. return this.pieceCodeToFen([...pieceElem.classList].find(x => x.match(/^(b|w)[prnbqk]{1}$/)));
  369. }
  370.  
  371. this.getPieceColor = pieceFenStr => {
  372. return pieceFenStr == pieceFenStr.toUpperCase() ? 'w' : 'b';
  373. }
  374.  
  375. this.getPieceOppositeColor = pieceFenStr => {
  376. return this.getPieceColor(pieceFenStr) == 'w' ? 'b' : 'w';
  377. }
  378.  
  379. this.squeezeEmptySquares = fenStr => {
  380. return fenStr.replace(/11111111/g, '8')
  381. .replace(/1111111/g, '7')
  382. .replace(/111111/g, '6')
  383. .replace(/11111/g, '5')
  384. .replace(/1111/g, '4')
  385. .replace(/111/g, '3')
  386. .replace(/11/g, '2');
  387. }
  388.  
  389. this.posToIndex = pos => {
  390. const [x, y] = pos.split('');
  391.  
  392. return { 'y': 8 - y, 'x': 'abcdefgh'.indexOf(x) };
  393. }
  394.  
  395. this.getBoardPiece = pos => {
  396. const indexObj = this.posToIndex(pos);
  397.  
  398. return this.board[indexObj.y][indexObj.x];
  399. }
  400.  
  401. this.getRights = () => {
  402. let rights = '';
  403.  
  404. // check for white
  405. const e1 = this.getBoardPiece('e1'),
  406. h1 = this.getBoardPiece('h1'),
  407. a1 = this.getBoardPiece('a1');
  408.  
  409. if (e1 == 'K' && h1 == 'R') rights += 'K';
  410. if (e1 == 'K' && a1 == 'R') rights += 'Q';
  411.  
  412. //check for black
  413. const e8 = this.getBoardPiece('e8'),
  414. h8 = this.getBoardPiece('h8'),
  415. a8 = this.getBoardPiece('a8');
  416.  
  417. if (e8 == 'k' && h8 == 'r') rights += 'k';
  418. if (e8 == 'k' && a8 == 'r') rights += 'q';
  419.  
  420. return rights ? rights : '-';
  421. }
  422.  
  423.  
  424.  
  425. this.getBasicFen = () => {
  426. const pieceElems = [...chessBoardElem.querySelectorAll('.piece')];
  427.  
  428. pieceElems.forEach(pieceElem => {
  429. const pieceFenCode = this.getFenCodeFromPieceElem(pieceElem);
  430. const [xPos, yPos] = pieceElem.classList.toString().match(/square-(\d)(\d)/).slice(1);
  431.  
  432. this.board[8 - yPos][xPos - 1] = pieceFenCode;
  433. });
  434.  
  435. const basicFen = this.squeezeEmptySquares(this.board.map(x => x.join('')).join('/'));
  436.  
  437. return basicFen;
  438. }
  439.  
  440. this.getFen = () => {
  441. const basicFen = this.getBasicFen();
  442. const rights = this.getRights();
  443.  
  444.  
  445. return `${basicFen} ${turn} ${rights} - 0 1`;
  446. }
  447. }
  448.  
  449. function InterfaceUtils() {
  450. this.boardUtils = {
  451. findSquareElem: (squareCode) => {
  452. if (!Gui?.document) return;
  453.  
  454. return Gui.document.querySelector(`.square-${squareCode}`);
  455. },
  456. markMove: (fromSquare, toSquare) => {
  457. if (!Gui?.document) return;
  458.  
  459. const [fromElem, toElem] = [this.boardUtils.findSquareElem(fromSquare), this.boardUtils.findSquareElem(toSquare)];
  460.  
  461.  
  462. if (isPlayerTurn) {
  463. fromElem.classList.add('best-move-from');
  464. toElem.classList.add('best-move-to');
  465. } else {
  466. fromElem.classList.add('negative-best-move-from');
  467. toElem.classList.add('negative-best-move-to');
  468. }
  469.  
  470. activeGuiMoveHighlights.push(fromElem);
  471. activeGuiMoveHighlights.push(toElem);
  472. },
  473. removeBestMarkings: () => {
  474. if (!Gui?.document) return;
  475.  
  476. activeGuiMoveHighlights.forEach(elem => {
  477. elem.classList.remove('best-move-from', 'best-move-to', 'negative-best-move-from', 'negative-best-move-to');
  478. });
  479.  
  480. activeGuiMoveHighlights = [];
  481. },
  482. updateBoardFen: fen => {
  483. if (!Gui?.document) return;
  484.  
  485. Gui.document.querySelector('#fen').textContent = fen;
  486. },
  487. updateBoardPower: (myScore, enemyScore) => {
  488. if (!Gui?.document) return;
  489.  
  490. Gui.document.querySelector('#enemy-score').textContent = enemyScore;
  491. Gui.document.querySelector('#my-score').textContent = myScore;
  492. },
  493. updateBoardOrientation: orientation => {
  494. if (!Gui?.document) return;
  495.  
  496. const orientationElem = Gui?.document?.querySelector('#orientation');
  497.  
  498. if (orientationElem) {
  499. orientationElem.textContent = orientation;
  500. }
  501. }
  502. }
  503.  
  504. this.engineLog = str => {
  505. if (!Gui?.document || enableUserLog == 0) return;
  506.  
  507. const logElem = document.createElement('div');
  508. logElem.classList.add('list-group-item');
  509.  
  510. if (str.includes('info')) logElem.classList.add('list-group-item-info');
  511. if (str.includes('bestmove')) logElem.classList.add('list-group-item-success');
  512.  
  513. logElem.innerText = `#${engineLogNum++} ${str}`;
  514.  
  515. Gui.document.querySelector('#engine-log-container').prepend(logElem);
  516. }
  517.  
  518. this.log = str => {
  519. if (!Gui?.document || enableUserLog == 0) return;
  520.  
  521. const logElem = document.createElement('div');
  522. logElem.classList.add('list-group-item');
  523.  
  524. if (str.includes('info')) logElem.classList.add('list-group-item-info');
  525. if (str.includes('bestmove')) logElem.classList.add('list-group-item-success');
  526.  
  527. const container = Gui?.document?.querySelector('#userscript-log-container');
  528.  
  529. if (container) {
  530. logElem.innerText = `#${userscriptLogNum++} ${str}`;
  531.  
  532. container.prepend(logElem);
  533. }
  534. }
  535.  
  536. this.getBoardOrientation = () => {
  537. return document.querySelector('.board.flipped') ? 'b' : 'w';
  538. }
  539.  
  540. this.updateBestMoveProgress = text => {
  541. if (!Gui?.document) return;
  542.  
  543. const progressBarElem = Gui.document.querySelector('#best-move-progress');
  544.  
  545. progressBarElem.innerText = text;
  546.  
  547. progressBarElem.classList.remove('hidden');
  548. progressBarElem.classList.add('wiggle');
  549. }
  550.  
  551. this.stopBestMoveProcessingAnimation = () => {
  552. if (!Gui?.document) return;
  553.  
  554. const progressBarElem = Gui.document.querySelector('#best-move-progress');
  555.  
  556. progressBarElem.classList.remove('wiggle');
  557. }
  558.  
  559. this.hideBestMoveProgress = () => {
  560. if (!Gui?.document) return;
  561.  
  562. const progressBarElem = Gui.document.querySelector('#best-move-progress');
  563.  
  564. if (!progressBarElem.classList.contains('hidden')) {
  565. progressBarElem.classList.add('hidden');
  566. this.stopBestMoveProcessingAnimation();
  567. }
  568. }
  569. }
  570.  
  571. function LozzaUtility() {
  572. this.separateMoveCodes = moveCode => {
  573. moveCode = moveCode.trim();
  574.  
  575. let move = moveCode.split(' ')[1];
  576.  
  577. return [move.slice(0, 2), move.slice(2, 4)];
  578. }
  579.  
  580. this.extractInfo = str => {
  581. const keys = ['time', 'nps', 'depth'];
  582.  
  583. return keys.reduce((acc, key) => {
  584. const match = str.match(`${key} (\\d+)`);
  585.  
  586. if (match) {
  587. acc[key] = Number(match[1]);
  588. }
  589.  
  590. return acc;
  591. }, {});
  592. }
  593. }
  594.  
  595. function fenSquareToChessComSquare(fenSquareCode) {
  596. const [x, y] = fenSquareCode.split('');
  597.  
  598. return `square-${['abcdefgh'.indexOf(x) + 1]}${y}`;
  599. }
  600.  
  601. function markMoveToSite(fromSquare, toSquare) {
  602. const highlight = (fenSquareCode, style) => {
  603. const squareClass = fenSquareToChessComSquare(fenSquareCode);
  604.  
  605. const highlightElem = document.createElement('div');
  606. highlightElem.classList.add('highlight');
  607. highlightElem.classList.add(squareClass);
  608. highlightElem.dataset.testElement = 'highlight';
  609. highlightElem.style = style;
  610.  
  611. activeSiteMoveHighlights.push(highlightElem);
  612.  
  613. const existingHighLight = document.querySelector(`.highlight.${squareClass}`);
  614.  
  615. if (existingHighLight) {
  616. existingHighLight.remove();
  617. }
  618.  
  619. chessBoardElem.prepend(highlightElem);
  620. }
  621.  
  622. const defaultFromSquareStyle = 'background-color: rgb(120 130 255 / 90%); border: 4px solid rgb(0 0 0 / 50%);';
  623. const defaultToSquareStyle = 'background-color: rgb(60 140 255 / 90%); border: 4px dashed rgb(0 0 0 / 50%);';
  624. const negativeFromSquareStyle = 'background-color: rgb(255 0 0 / 20%); border: 4px solid rgb(0 0 0 / 50%);';
  625. const negativeToSquareStyle = 'background-color: rgb(255 0 0 / 20%); border: 4px dashed rgb(0 0 0 / 50%);';
  626.  
  627. const subtleMode = GM_getValue(dbValues.subtleMode);
  628. const subtletiness = GM_getValue(dbValues.subtletiness);
  629.  
  630. highlight(fromSquare, subtleMode ? `background-color: rgb(0 0 0 / ${subtletiness}%);` : (isPlayerTurn ? defaultFromSquareStyle : negativeFromSquareStyle));
  631. highlight(toSquare, subtleMode ? `background-color: rgb(0 0 0 / ${subtletiness}%);` : (isPlayerTurn ? defaultToSquareStyle : negativeToSquareStyle));
  632. }
  633.  
  634. function removeSiteMoveMarkings() {
  635. activeSiteMoveHighlights.forEach(elem => {
  636. elem?.remove();
  637. });
  638.  
  639. activeSiteMoveHighlights = [];
  640. }
  641.  
  642. function updateBestMove(forced = false, mutationArr) {
  643.  
  644.  
  645. const FenUtil = new FenUtils();
  646.  
  647. let currentFen = FenUtil.getFen();
  648.  
  649.  
  650. if (currentFen != lastFen || forced == true) {
  651. lastFen = currentFen;
  652.  
  653.  
  654.  
  655. if (forced == true) {
  656.  
  657. sendBestMove();
  658. }
  659. else if (mutationArr) {
  660. const attributeMutationArr = mutationArr.filter(m => m.target.classList.contains('piece') && m.attributeName == 'class');
  661.  
  662. if (attributeMutationArr?.length) {
  663. turn = FenUtil.getPieceOppositeColor(FenUtil.getFenCodeFromPieceElem(attributeMutationArr[0].target));
  664. Interface.log(`Turn updated to ${turn}!`);
  665.  
  666. sendBestMove();
  667. }
  668. }
  669.  
  670. updateBoard();
  671.  
  672.  
  673. }
  674. }
  675.  
  676.  
  677.  
  678. function sendBestMove() {
  679. updateBoard();
  680. isPlayerTurn = playerColor == null || turn == playerColor;
  681.  
  682. if (!isPlayerTurn && !show_opposite_moves) {
  683. return;
  684. }
  685.  
  686.  
  687.  
  688. reloadChessEngine(false, () => {
  689. Interface.log('Sending best move request to the engine!');
  690. if (use_book_moves) {
  691. getBookMoves(turn);
  692. } else {
  693. getBestMoves(turn);
  694. }
  695. });
  696.  
  697.  
  698.  
  699. }
  700.  
  701.  
  702. function updateBoard() {
  703. Interface.stopBestMoveProcessingAnimation();
  704.  
  705. const FenUtil = new FenUtils();
  706.  
  707. currentFen = FenUtil.getFen();
  708.  
  709. Interface.boardUtils.removeBestMarkings();
  710.  
  711. removeSiteMoveMarkings();
  712.  
  713. Interface.boardUtils.updateBoardFen(currentFen);
  714. }
  715.  
  716. function sleep(ms) {
  717. return new Promise(resolve => setTimeout(resolve, ms));
  718. }
  719.  
  720. function getBestMoves(turn) {
  721. if (!node_engine_id.includes(engineIndex)) {
  722. // local engines
  723. while (!engine) {
  724. sleep(100);
  725. }
  726. engine.postMessage(`position fen ${currentFen}`);
  727.  
  728. if (engineMode == DEPTH_MODE) {
  729. engine.postMessage('go depth ' + current_depth);
  730. } else {
  731. engine.postMessage('go movetime ' + current_movetime);
  732. }
  733.  
  734.  
  735. } else {
  736. // node server
  737. console.log("using node server");
  738.  
  739.  
  740. getNodeBestMoves(turn);
  741. }
  742. }
  743.  
  744. function observeNewMoves() {
  745. updateBestMove();
  746.  
  747. const boardObserver = new MutationObserver(mutationArr => {
  748. const lastPlayerColor = playerColor;
  749.  
  750. updatePlayerColor();
  751.  
  752.  
  753. if (playerColor != lastPlayerColor) {
  754. Interface.log(`Player color changed from ${lastPlayerColor} to ${playerColor}!`);
  755.  
  756. updateBestMove();
  757. } else {
  758. updateBestMove(false, mutationArr);
  759. }
  760. });
  761.  
  762. boardObserver.observe(chessBoardElem, { childList: true, subtree: true, attributes: true });
  763. }
  764.  
  765. function addGuiPages() {
  766. if (Gui?.document) return;
  767.  
  768. Gui.addPage("Main", `
  769. <div class="rendered-form" id="main-tab">
  770. <script>${GM_getResourceText('jquery.js')}</script>
  771. <script>${GM_getResourceText('chessboard.js')}</script>
  772. <div class="card">
  773. <div class="card-body" id="chessboard">
  774. <div class="main-title-bar">
  775. <h4 class="card-title">Live Chessboard:</h4>
  776. <p id="best-move-progress"></p>
  777. </div>
  778.  
  779. <div id="board" style="width: 447px"></div>
  780. </div>
  781. <div id="orientation" class="hidden"></div>
  782. <div class="card-footer card"><input type="button" value="Get Best Move" id="bestmove-btn"></input></div>
  783. <div class="card-footer sideways-card">FEN :<small class="text-muted"><div id="fen"></div></small></div>
  784. <div class="card-footer sideways-card">ENEMY SCORE :<div id="enemy-score"></div></div>
  785. <div class="card-footer sideways-card">MY SCORE : <div id="my-score"></div></div>
  786. </div>
  787. <script>
  788. const orientationElem = document.querySelector('#orientation');
  789. const fenElem = document.querySelector('#fen');
  790.  
  791. let board = ChessBoard('board', {
  792. pieceTheme: '${repositoryRawURL}/content/chesspieces/{piece}.svg',
  793. position: 'start',
  794. orientation: '${playerColor == 'b' ? 'black' : 'white'}'
  795. });
  796.  
  797. const orientationObserver = new MutationObserver(() => {
  798. board = ChessBoard('board', {
  799. pieceTheme: '${repositoryRawURL}/content/chesspieces/{piece}.svg',
  800. position: fenElem.textContent,
  801. orientation: orientationElem.textContent == 'b' ? 'black' : 'white'
  802. });
  803. });
  804.  
  805. const fenObserver = new MutationObserver(() => {
  806. board.position(fenElem.textContent);
  807. });
  808.  
  809. orientationObserver.observe(orientationElem, { attributes: true, childList: true, characterData: true });
  810. fenObserver.observe(fenElem, { attributes: true, childList: true, characterData: true });
  811. </script>
  812. </div>
  813. `);
  814.  
  815. Gui.addPage('Log', `
  816. <div class="rendered-form" id="log-tab">
  817. <div class="card">
  818. <div class="card-body">
  819. <h4 class="card-title">Userscript Log:</h4>
  820. <ul class="list-group" id="userscript-log-container"></ul>
  821. </div>
  822. </div>
  823. <div class="card">
  824. <div class="card-body">
  825. <h4 class="card-title">Engine Log</h4>
  826. <ul class="list-group" id="engine-log-container"></ul>
  827. </div>
  828. </div>
  829. </div>
  830. `);
  831.  
  832.  
  833. const subtletiness = GM_getValue(dbValues.subtletiness);
  834.  
  835.  
  836. const openGuiAutomatically = GM_getValue(dbValues.openGuiAutomatically) == true;
  837. const subtleMode = GM_getValue(dbValues.subtleMode) == true;
  838.  
  839.  
  840.  
  841.  
  842.  
  843. Gui.addPage('Settings', `
  844. <div class="rendered-form" id="settings-tab">
  845. <div class="card">
  846. <div class="card-body">
  847. <h4 class="card-title">Engine:</h4>
  848. <div class="form-group field-select-engine">
  849. <select class="form-control" name="select-engine" id="select-engine">
  850. <option value="option-lozza" id="select-engine-0">Lozza</option>
  851. <option value="option-stockfish" id="select-engine-2">Stockfish 5</option>
  852. <option value="option-stockfish2" id="select-engine-3">Stockfish 2018</option>
  853. <option value="option-nodeserver" id="select-engine-4">Node Server Engines</option>
  854. </select>
  855. </div>
  856.  
  857.  
  858. <div id="node-engine-div" style="display:${(node_engine_id.includes(engineIndex)) ? 'block' : 'none'};">
  859. <label for="engine-url">Engine URL:</label>
  860. <input type="text" id="engine-url" value="${node_engine_url}">
  861. <br>
  862. <label for="engine-name">Engine Name:</label>
  863. <input type="text" id="engine-name" value="${node_engine_name}">
  864. </div>
  865. </div>
  866. </div>
  867.  
  868.  
  869. <div class="card">
  870. <div class="card-body">
  871. <h4 class="card-title">Engine Strength:</h4>
  872.  
  873. <h7 class="card-title">Engine Mode:</h7>
  874. <div class="form-group field-select-engine-mode">
  875. <select class="form-control" name="select-engine-mode" id="select-engine-mode">
  876. <option value="option-depth" id="select-engine-mode-0">Depth</option>
  877. <option value="option-movetime" id="select-engine-mode-1">Move time</option>
  878. </select>
  879. </div>
  880. <h7 class="card-title">Engine Power:</h7>
  881. <input type="range" class="form-range" min="${MIN_DEPTH}" max="${MAX_DEPTH}" step="1" value="${current_depth}" id="depth-range">
  882. <input type="number" class="form-range" min="${MIN_DEPTH}" max="${MAX_DEPTH}" value="${current_depth}" id="depth-range-number">
  883. <input type="range" class="form-range" min="${MIN_MOVETIME}" max="${MAX_MOVETIME}" step="50" value="${current_movetime}" id="movetime-range">
  884. <input type="number" class="form-range" min="${MIN_MOVETIME}" max="${MAX_MOVETIME}" value="${current_movetime}" id="movetime-range-number">
  885. </div>
  886. <div class="card-footer sideways-card" id="elo">${getEloDescription()}</div>
  887. </div>
  888.  
  889. <div class="card">
  890. <div class="card-body">
  891. <h4 class="card-title">Other:</h4>
  892.  
  893. <div>
  894. <input type="checkbox" id="enable-user-log" ${enableUserLog ? 'checked' : ''}>
  895. <label for="enable-user-log">Enable User Scripts Log</label>
  896. </div>
  897.  
  898.  
  899. <div>
  900. <input type="checkbox" id="use-book-moves" ${use_book_moves ? 'checked' : ''}>
  901. <label for="use-book-moves">Use book moves</label>
  902. </div>
  903.  
  904.  
  905.  
  906. <div id="reload-count-div" style="display:${node_engine_id.includes(engineIndex) ? 'none' : 'block'};">
  907. <label for="reload-count">Reload Engine every</label>
  908. <input type="number" id="reload-count" value="${reload_every}">
  909. <label for="reload-count"> moves</label>
  910. </div>
  911. </div>
  912.  
  913.  
  914. <div class="card">
  915. <div class="card-body">
  916. <h4 class="card-title">Visual:</h4>
  917. <div id="display-moves-on-site-warning" class="alert alert-danger">
  918. <strong>Warning !!</strong> You can get Permanently banned in 24 hours. Use with caution.
  919. </div>
  920.  
  921. <div>
  922. <input type="checkbox" id="show-opposite-moves" ${show_opposite_moves ? 'checked' : ''}>
  923. <label for="show-opposite-moves">Show Opponent best moves</label>
  924. </div>
  925.  
  926.  
  927. <input type="checkbox" id="display-moves-on-site" ${displayMovesOnSite ? 'checked' : ''}>
  928. <label for="display-moves-on-site">Display moves on site</label>
  929. <!-- Display moves on site additional settings -->
  930. <div class="card ${displayMovesOnSite ? '' : 'hidden'}" id="display-moves-on-site-additional">
  931. <div class="card-body">
  932. <!-- Open GUI automatically checkbox -->
  933. <div>
  934. <input type="checkbox" id="open-gui-automatically" ${openGuiAutomatically ? 'checked' : ''}>
  935. <label for="open-gui-automatically">Open GUI automatically</label>
  936. </div>
  937. <!-- Subtle mode settinngs -->
  938. <div>
  939. <!-- Subtle mode checkbox -->
  940. <div>
  941. <input type="checkbox" id="subtle-mode" ${subtleMode ? 'checked' : ''}>
  942. <label for="subtle-mode">Subtle mode</label>
  943. </div>
  944. <!-- Subtle mode additional settings -->
  945. <div>
  946. <div class="card ${subtleMode ? '' : 'hidden'}" id="subtletiness-range-container">
  947. <div class="card-body">
  948. <!-- Subtletiness range -->
  949. <h6 class="card-title">Visibility</h6>
  950. <input type="range" class="form-range" min="1" max="50" value="${subtletiness}" id="subtletiness-range">
  951. </div>
  952. <div class="card-footer sideways-card">Percentage <small id="subtletiness-info">${subtletiness}%</small></div>
  953. </div>
  954. </div>
  955. </div>
  956. </div>
  957. </div>
  958. </div>
  959. </div>
  960. </div>
  961. `);
  962.  
  963.  
  964. }
  965.  
  966. function fixDepthMoveTimeInput(depthRangeElem, depthRangeNumberElem, moveTimeRangeElem, moveTimeRangeNumberElem, eloElem) {
  967. if (engineMode == DEPTH_MODE) {
  968. if (isNotCompatibleBrowser()) {
  969. depthRangeElem.style.display = "none";
  970. depthRangeNumberElem.style.display = "block";
  971. moveTimeRangeElem.style.display = "none";
  972. moveTimeRangeNumberElem.style.display = "none";
  973. } else {
  974. depthRangeElem.style.display = "block";
  975. depthRangeNumberElem.style.display = "none";
  976. moveTimeRangeElem.style.display = "none";
  977. moveTimeRangeNumberElem.style.display = "none";
  978. }
  979. } else {
  980. if (isNotCompatibleBrowser()) {
  981. depthRangeElem.style.display = "none";
  982. depthRangeNumberElem.style.display = "none";
  983. moveTimeRangeElem.style.display = "none";
  984. moveTimeRangeNumberElem.style.display = "block";
  985. } else {
  986. depthRangeElem.style.display = "none";
  987. depthRangeNumberElem.style.display = "none";
  988. moveTimeRangeElem.style.display = "block";
  989. moveTimeRangeNumberElem.style.display = "none";
  990. }
  991. }
  992.  
  993.  
  994. eloElem.innerText = getEloDescription();
  995.  
  996. }
  997.  
  998. function openGUI() {
  999. Interface.log(`Opening GUI!`);
  1000.  
  1001. const hide = elem => elem.classList.add('hidden');
  1002. const show = elem => elem.classList.remove('hidden');
  1003.  
  1004. Gui.open(() => {
  1005. const depthRangeElem = Gui.document.querySelector('#depth-range');
  1006. const depthRangeNumberElem = Gui.document.querySelector('#depth-range-number');
  1007. const moveTimeRangeElem = Gui.document.querySelector('#movetime-range');
  1008. const moveTimeRangeNumberElem = Gui.document.querySelector('#movetime-range-number');
  1009. const engineModeElem = Gui.document.querySelector('#select-engine-mode');
  1010. const engineElem = Gui.document.querySelector('#select-engine');
  1011. const engineNameDivElem = Gui.document.querySelector('#node-engine-div');
  1012. const reloadEveryDivElem = Gui.document.querySelector('#reload-count-div');
  1013. const nodeEngineNameElem = Gui.document.querySelector('#engine-name');
  1014. const nodeEngineUrlElem = Gui.document.querySelector('#engine-url');
  1015. const useLocalEngineElem = Gui.document.querySelector('#use-book-moves');
  1016. const showOppositeMovesElem = Gui.document.querySelector('#show-opposite-moves');
  1017. const displayMovesOnSiteElem = Gui.document.querySelector('#display-moves-on-site');
  1018. const openGuiAutomaticallyElem = Gui.document.querySelector('#open-gui-automatically');
  1019. const openGuiAutomaticallyAdditionalElem = Gui.document.querySelector('#display-moves-on-site-additional');
  1020. const subtleModeElem = Gui.document.querySelector('#subtle-mode');
  1021. const subtletinessRangeContainerElem = Gui.document.querySelector('#subtletiness-range-container');
  1022. const subtletinessRange = Gui.document.querySelector('#subtletiness-range');
  1023. const subtletinessInfo = Gui.document.querySelector('#subtletiness-info');
  1024. const reloadEveryElem = Gui.document.querySelector('#reload-count');
  1025. const enableUserLogElem = Gui.document.querySelector('#enable-user-log');
  1026. const eloElem = Gui.document.querySelector('#elo');
  1027. const getBestMoveElem = Gui.document.querySelector('#bestmove-btn');
  1028.  
  1029.  
  1030. fixDepthMoveTimeInput(depthRangeElem, depthRangeNumberElem, moveTimeRangeElem, moveTimeRangeNumberElem, eloElem);
  1031.  
  1032. engineElem.selectedIndex = engineIndex;
  1033. engineModeElem.selectedIndex = engineMode;
  1034.  
  1035.  
  1036. // compatibility fixed
  1037. if (isNotCompatibleBrowser()) {
  1038. Gui.document.querySelector('#content').style.maxHeight = "500px";
  1039. Gui.document.querySelector('#content').style.overflow = "scroll";
  1040. Gui.document.querySelector('#chessboard').style.display = "none";
  1041. Gui.document.querySelector('#orientation').style.display = "none";
  1042. Gui.document.querySelector('#engine-log-container').style.maxHeight = "100px";
  1043. Gui.document.querySelector('#engine-log-container').style.overflow = "scroll";
  1044. Gui.document.querySelector('#userscript-log-container').style.maxHeight = "100px";
  1045. Gui.document.querySelector('#userscript-log-container').style.overflow = "scroll";
  1046.  
  1047. Gui.document.querySelector('#button-close-gui').addEventListener('click', e => {
  1048. e.preventDefault();
  1049. if (closedGui == true) {
  1050. closedGui = false;
  1051. Gui.document.querySelector("#content").style.display = "block";
  1052. }
  1053. else {
  1054. closedGui = true;
  1055. Gui.document.querySelector("#content").style.display = "none";
  1056.  
  1057. }
  1058.  
  1059.  
  1060. });
  1061. }
  1062.  
  1063. getBestMoveElem.onclick = () => {
  1064. updateBestMove(true);
  1065. }
  1066.  
  1067. engineModeElem.onchange = () => {
  1068. engineMode = engineModeElem.selectedIndex;
  1069.  
  1070. fixDepthMoveTimeInput(depthRangeElem, depthRangeNumberElem, moveTimeRangeElem, moveTimeRangeNumberElem, eloElem);
  1071. }
  1072. nodeEngineNameElem.onchange = () => {
  1073. node_engine_name = nodeEngineNameElem.value;
  1074. }
  1075. nodeEngineUrlElem.onchange = () => {
  1076. node_engine_url = nodeEngineUrlElem.value;
  1077. }
  1078.  
  1079. enableUserLogElem.onchange = () => {
  1080. const isChecked = enableUserLogElem.checked;
  1081.  
  1082. if (isChecked)
  1083. enableUserLog = true;
  1084. else
  1085. enableUserLog = false;
  1086. }
  1087.  
  1088. reloadEveryElem.onchange = () => {
  1089. reload_every = reloadEveryElem.value;
  1090. }
  1091.  
  1092. engineElem.onchange = () => {
  1093. lastEngine = engineIndex;
  1094. engineIndex = engineElem.selectedIndex;
  1095.  
  1096.  
  1097.  
  1098. if (node_engine_id.includes(engineIndex)) {
  1099. reloadEveryDivElem.style.display = "none";
  1100. engineNameDivElem.style.display = "block";
  1101.  
  1102.  
  1103. }
  1104. else {
  1105. reloadEveryDivElem.style.display = "block";
  1106. engineNameDivElem.style.display = "none";
  1107. }
  1108.  
  1109.  
  1110.  
  1111.  
  1112.  
  1113. if (engineObjectURL) {
  1114. URL.revokeObjectURL(engineObjectURL);
  1115. engineObjectURL = null;
  1116. }
  1117.  
  1118.  
  1119.  
  1120.  
  1121. reloadChessEngine(true, () => {
  1122. Interface.boardUtils.removeBestMarkings();
  1123.  
  1124. removeSiteMoveMarkings();
  1125.  
  1126. Interface.boardUtils.updateBoardPower(0, 0);
  1127. });
  1128.  
  1129. }
  1130.  
  1131.  
  1132.  
  1133. depthRangeElem.onchange = () => {
  1134. changeEnginePower(depthRangeElem.value, eloElem);
  1135. };
  1136.  
  1137. depthRangeNumberElem.onchange = () => {
  1138. changeEnginePower(depthRangeNumberElem.value, eloElem);
  1139. };
  1140.  
  1141. moveTimeRangeElem.onchange = () => {
  1142. changeEnginePower(moveTimeRangeElem.value, eloElem);
  1143. };
  1144.  
  1145. moveTimeRangeNumberElem.onchange = () => {
  1146. changeEnginePower(moveTimeRangeNumberElem.value, eloElem);
  1147. };
  1148.  
  1149. showOppositeMovesElem.onchange = () => {
  1150. const isChecked = showOppositeMovesElem.checked;
  1151.  
  1152. if (isChecked) {
  1153. show_opposite_moves = true;
  1154. } else {
  1155. show_opposite_moves = false;
  1156. }
  1157. }
  1158.  
  1159. useLocalEngineElem.onchange = () => {
  1160. const isChecked = useLocalEngineElem.checked;
  1161.  
  1162. if (isChecked) {
  1163. use_book_moves = true;
  1164. } else {
  1165. use_book_moves = false;
  1166. }
  1167. }
  1168.  
  1169. displayMovesOnSiteElem.onchange = () => {
  1170. const isChecked = displayMovesOnSiteElem.checked;
  1171.  
  1172. if (isChecked) {
  1173. displayMovesOnSite = true;
  1174.  
  1175. show(openGuiAutomaticallyAdditionalElem);
  1176.  
  1177. openGuiAutomaticallyElem.checked = GM_getValue(dbValues.openGuiAutomatically);
  1178. } else {
  1179. displayMovesOnSite = false;
  1180. GM_setValue(dbValues.openGuiAutomatically, true);
  1181.  
  1182. hide(openGuiAutomaticallyAdditionalElem);
  1183. }
  1184. };
  1185.  
  1186. openGuiAutomaticallyElem.onchange = () => {
  1187. GM_setValue(dbValues.openGuiAutomatically, openGuiAutomaticallyElem.checked);
  1188. };
  1189.  
  1190. subtleModeElem.onchange = () => {
  1191. const isChecked = subtleModeElem.checked;
  1192.  
  1193. if (isChecked) {
  1194. GM_setValue(dbValues.subtleMode, true);
  1195. show(subtletinessRangeContainerElem);
  1196. } else {
  1197. GM_setValue(dbValues.subtleMode, false);
  1198. hide(subtletinessRangeContainerElem);
  1199. }
  1200. };
  1201.  
  1202. subtletinessRange.onchange = () => {
  1203. GM_setValue(dbValues.subtletiness, subtletinessRange.value);
  1204. subtletinessInfo.innerText = `${subtletinessRange.value}%`;
  1205. };
  1206.  
  1207. window.onunload = () => {
  1208. if (Gui.window && !Gui.window.closed) {
  1209. Gui.window.close();
  1210. }
  1211. };
  1212.  
  1213. const isWindowClosed = setInterval(() => {
  1214. if (Gui.window.closed) {
  1215. clearInterval(isWindowClosed);
  1216. if (engine != null)
  1217. engine.terminate();
  1218. }
  1219. }, 1000);
  1220.  
  1221. observeNewMoves();
  1222.  
  1223. Interface.log('Initialized!');
  1224. });
  1225. }
  1226.  
  1227. function changeEnginePower(val, eloElem) {
  1228. if (engineMode == DEPTH_MODE) {
  1229. current_depth = val
  1230. } else {
  1231. current_movetime = val
  1232. }
  1233.  
  1234.  
  1235. eloElem.innerText = getEloDescription();
  1236. }
  1237.  
  1238. function reloadChessEngine(forced, callback) {
  1239. // reload only if using local engines
  1240. if (node_engine_id.includes(engineIndex) && forced == false)
  1241. callback();
  1242. else if (reload_count >= reload_every || forced == true) {
  1243. reload_count = 1;
  1244. Interface.log(`Reloading the chess engine!`);
  1245.  
  1246. if (engine)
  1247. engine.terminate();
  1248.  
  1249. loadChessEngine(callback);
  1250. }
  1251. else {
  1252. reload_count = reload_count + 1;
  1253. callback();
  1254. }
  1255. }
  1256.  
  1257.  
  1258.  
  1259. function loadChessEngine(callback) {
  1260. if (!engineObjectURL) {
  1261. if (engineIndex == 0)
  1262. engineObjectURL = URL.createObjectURL(new Blob([GM_getResourceText('lozza.js')], { type: 'application/javascript' }));
  1263. else if (engineIndex == 1)
  1264. engineObjectURL = URL.createObjectURL(new Blob([GM_getResourceText('stockfish.js')], { type: 'application/javascript' }));
  1265. else if (engineIndex == 2)
  1266. engineObjectURL = URL.createObjectURL(new Blob([GM_getResourceText('stockfish2.js')], { type: 'application/javascript' }));
  1267. }
  1268.  
  1269. if (engineObjectURL) {
  1270. engine = new Worker(engineObjectURL);
  1271.  
  1272. engine.onmessage = e => {
  1273. if (e.data.includes('bestmove')) {
  1274.  
  1275.  
  1276. let move = e.data.split(' ')[1];
  1277. let move2 = e.data.split(' ')[3];
  1278.  
  1279. moveResult("", move.slice(0, 2), move.slice(2, 4), 0, true);
  1280.  
  1281. }
  1282.  
  1283. else if (e.data.includes('info')) {
  1284.  
  1285. const infoObj = LozzaUtils.extractInfo(e.data);
  1286.  
  1287. if (engineMode == DEPTH_MODE) {
  1288.  
  1289. Interface.updateBestMoveProgress(`Depth: ${infoObj.depth}`);
  1290.  
  1291. } else {
  1292.  
  1293. Interface.updateBestMoveProgress(`Move time: ${infoObj.time} ms`);
  1294.  
  1295. }
  1296.  
  1297. }
  1298. Interface.engineLog(e.data);
  1299. };
  1300.  
  1301. engine.postMessage('ucinewgame');
  1302.  
  1303. Interface.log(`Loaded the chess engine!`);
  1304. }
  1305.  
  1306. callback();
  1307. }
  1308.  
  1309. function initializeDatabase() {
  1310. const initValue = (name, value) => {
  1311. if (GM_getValue(name) == undefined) {
  1312. GM_setValue(name, value);
  1313. }
  1314. };
  1315.  
  1316. initValue(dbValues.subtleMode, false);
  1317. initValue(dbValues.openGuiAutomatically, true);
  1318. initValue(dbValues.subtletiness, 25);
  1319.  
  1320. Interface.log(`Initialized the database!`);
  1321. }
  1322.  
  1323. async function updatePlayerColor() {
  1324. const boardOrientation = Interface.getBoardOrientation();
  1325.  
  1326. playerColor = boardOrientation;
  1327. turn = boardOrientation;
  1328.  
  1329. Interface.boardUtils.updateBoardOrientation(playerColor);
  1330. }
  1331.  
  1332. async function initialize(openInterface) {
  1333. Interface = new InterfaceUtils();
  1334. LozzaUtils = new LozzaUtility();
  1335.  
  1336. const boardOrientation = Interface.getBoardOrientation();
  1337. turn = boardOrientation;
  1338.  
  1339. initializeDatabase();
  1340.  
  1341. loadChessEngine(() => {
  1342.  
  1343. });
  1344.  
  1345. updatePlayerColor();
  1346.  
  1347. if (openInterface) {
  1348. addGuiPages();
  1349. openGUI();
  1350. } else {
  1351. observeNewMoves();
  1352. }
  1353. }
  1354.  
  1355. if (typeof GM_registerMenuCommand == 'function') {
  1356. GM_registerMenuCommand("Open C.A.S", e => {
  1357. if (chessBoardElem) {
  1358. initialize(true);
  1359. }
  1360. }, 's');
  1361. }
  1362.  
  1363. const waitForChessBoard = setInterval(() => {
  1364. const boardElem = document.querySelector('chess-board');
  1365. const firstPieceElem = document.querySelector('.piece');
  1366.  
  1367. if (boardElem && firstPieceElem && chessBoardElem != boardElem) {
  1368. chessBoardElem = boardElem;
  1369.  
  1370. if (window.location.href != 'https://www.chess.com/play') {
  1371. const openGuiAutomatically = GM_getValue(dbValues.openGuiAutomatically);
  1372.  
  1373. if (openGuiAutomatically == undefined) {
  1374. initialize(true);
  1375. } else {
  1376. initialize(openGuiAutomatically);
  1377. }
  1378. }
  1379. }
  1380. }, 1000);