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

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

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

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