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

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

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

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