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

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

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

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