- // ==UserScript==
- // @name 🏆 [#1 Chess Assistant] A.C.A.S (Advanced Chess Assistance System)
- // @name:en 🏆 [#1 Chess Assistant] A.C.A.S (Advanced Chess Assistance System)
- // @name:fi 🏆 [#1 Chess Assistant] A.C.A.S (Edistynyt shakkiavustusjärjestelmä)
- // @name:sw 🏆 [#1 Chess Assistant] A.C.A.S (Advanserad Schack Assitant System)
- // @name:zh-CN 🏆 [#1 Chess Assistant] A.C.A.S(高级国际象棋辅助系统)
- // @name:es 🏆 [#1 Chess Assistant] A.C.A.S (Sistema Avanzado de Asistencia al Ajedrez)
- // @name:hi 🏆 [#1 Chess Assistant] A.C.A.S (उन्नत शतरंज सहायता प्रणाली)
- // @name:ar 🏆 [#1 Chess Assistant] A.C.A.S (نظام المساعدة المتقدم في الشطرنج)
- // @name:pt 🏆 [#1 Chess Assistant] A.C.A.S (Sistema Avançado de Assistência ao Xadrez)
- // @name:ja 🏆 [#1 Chess Assistant] A.C.A.S(先進的なチェス支援システム)
- // @name:de 🏆 [#1 Chess Assistant] A.C.A.S (Fortgeschrittenes Schach-Hilfesystem)
- // @name:fr 🏆 [#1 Chess Assistant] A.C.A.S (Système Avancé d'Assistance aux Échecs)
- // @name:it 🏆 [#1 Chess Assistant] A.C.A.S (Sistema Avanzato di Assistenza agli Scacchi)
- // @name:ko 🏆 [#1 Chess Assistant] A.C.A.S (고급 체스 보조 시스템)
- // @name:nl 🏆 [#1 Chess Assistant] A.C.A.S (Geavanceerd Schaakondersteuningssysteem)
- // @name:pl 🏆 [#1 Chess Assistant] A.C.A.S (Zaawansowany System Pomocy Szachowej)
- // @name:tr 🏆 [#1 Chess Assistant] A.C.A.S (Gelişmiş Satranç Yardım Sistemi)
- // @name:vi 🏆 [#1 Chess Assistant] A.C.A.S (Hệ Thống Hỗ Trợ Cờ Vua Nâng Cao)
- // @name:uk 🏆 [#1 Chess Assistant] A.C.A.S (Система передової допомоги в шахах)
- // @name:ru 🏆 [#1 Chess Assistant] A.C.A.S (Система расширенной помощи в шахматах)
- // @description Enhance your chess performance with a cutting-edge real-time move analysis and strategy assistance system
- // @description:en Enhance your chess performance with a cutting-edge real-time move analysis and strategy assistance system
- // @description:fi Paranna shakkipelisi suorituskykyä huippuluokan reaaliaikaisen siirtoanalyysin ja strategisen avustusjärjestelmän avulla
- // @description:sw Förbättra dina schackprestationer med ett banbrytande rörelseanalys i realtid och strategiassistans
- // @description:zh-CN 利用尖端实时走法分析和策略辅助系统,提升您的国际象棋水平
- // @description:es Mejora tu rendimiento en ajedrez con un sistema de análisis de movimientos en tiempo real y asistencia estratégica de vanguardia
- // @description:hi अपने शतरंज प्रदर्शन को उन्नत करें, एक कटिंग-एज रियल-टाइम मूव विश्लेषण और रणनीति सहायता प्रणाली के साथ
- // @description:ar قم بتحسين أداءك في الشطرنج مع تحليل حركات اللعب في الوقت الحقيقي ونظام مساعدة استراتيجية حديث
- // @description:pt Melhore seu desempenho no xadrez com uma análise de movimentos em tempo real e um sistema avançado de assistência estratégica
- // @description:ja 最新のリアルタイムのムーブ分析と戦略支援システムでチェスのパフォーマンスを向上させましょう
- // @description:de Verbessern Sie Ihre Schachleistung mit einer hochmodernen Echtzeitzug-Analyse- und Strategiehilfe-System
- // @description:fr Améliorez vos performances aux échecs avec une analyse de mouvement en temps réel de pointe et un système d'assistance stratégique
- // @description:it Migliora le tue prestazioni agli scacchi con un sistema all'avanguardia di analisi dei movimenti in tempo reale e assistenza strategica
- // @description:ko 최첨단 실시간 움직임 분석 및 전략 지원 시스템으로 체스 성과 향상
- // @description:nl Verbeter je schaakprestaties met een geavanceerd systeem voor realtime zetanalyse en strategische ondersteuning
- // @description:pl Popraw swoje osiągnięcia w szachach dzięki zaawansowanemu systemowi analizy ruchów w czasie rzeczywistym i wsparciu strategicznemu
- // @description:tr Keskinleşmiş gerçek zamanlı hareket analizi ve strateji yardım sistemiyle satranç performansınızı artırın
- // @description:vi Nâng cao hiệu suất cờ vua của bạn với hệ thống phân tích nước đi và hỗ trợ chiến thuật hiện đại
- // @description:uk Покращуйте свою шахову гру з використанням передової системи аналізу ходів в режимі реального часу та стратегічної підтримки
- // @description:ru Слава Украине
- // @homepageURL https://psyyke.github.io/A.C.A.S
- // @supportURL https://github.com/Psyyke/A.C.A.S/tree/main#why-doesnt-it-work
- // @match https://psyyke.github.io/A.C.A.S/*
- // @match http://localhost/*
- // @match https://www.chess.com/*
- // @match https://lichess.org/*
- // @match https://playstrategy.org/*
- // @match https://www.pychess.org/*
- // @match https://chess.org/*
- // @match https://papergames.io/*
- // @match https://vole.wtf/kilobytes-gambit/
- // @match https://chess.coolmathgames.com/*
- // @match https://www.coolmathgames.com/0-chess/*
- // @match https://immortal.game/*
- // @match https://chessarena.com/*
- // @match http://chess.net/*
- // @match https://chess.net/*
- // @match https://www.freechess.club/*
- // @match https://*.chessclub.com/*
- // @match https://gameknot.com/*
- // @match https://chesstempo.com/*
- // @match https://www.redhotpawn.com/*
- // @match https://www.chessanytime.com/*
- // @match https://www.simplechess.com/*
- // @match https://chessworld.net/*
- // @match https://app.edchess.io/*
- // @grant GM_getValue
- // @grant GM_setValue
- // @grant GM_deleteValue
- // @grant GM_listValues
- // @grant GM_openInTab
- // @grant GM.getValue
- // @grant GM.setValue
- // @grant GM.deleteValue
- // @grant GM.listValues
- // @grant GM.openInTab
- // @grant GM_registerMenuCommand
- // @grant GM_setClipboard
- // @grant GM_notification
- // @grant unsafeWindow
- // @run-at document-start
- // @require https://update.greasyfork.org/scripts/534637/LegacyGMjs.js?acasv=2
- // @require https://update.greasyfork.org/scripts/470418/CommLinkjs.js?acasv=2
- // @require https://update.greasyfork.org/scripts/470417/UniversalBoardDrawerjs.js?acasv=1
- // @icon https://raw.githubusercontent.com/Psyyke/A.C.A.S/main/assets/images/grey-logo.png
- // @version 2.3.1
- // @namespace HKR
- // @author HKR
- // @license GPL-3.0
- // ==/UserScript==
-
- /*
- e e88~-_ e ,d88~~\
- d8b d888 \ d8b 8888
- /Y88b 8888 /Y88b `Y88b
- / Y88b 8888 / Y88b `Y88b,
- /____Y88b d88b Y888 / d88b /____Y88b d88b 8888
- / Y88b Y88P "88_-~ Y88P / Y88b Y88P \__88P'
-
- Advanced Chess Assistance System (A.C.A.S) v2 | Q3 2023
-
- [WARNING]
- - Please be advised that the use of A.C.A.S may violate the rules and lead to disqualification or banning from tournaments and online platforms.
- - The developers of A.C.A.S and related systems will NOT be held accountable for any consequences resulting from its use.
- - We strongly advise to use A.C.A.S only in a controlled environment ethically.
-
- [ADDITIONAL]
- - Big fonts created with: https://www.patorjk.com/software/taag/ (Cyberlarge)
-
- JOIN THE DISCUSSION ABOUT USERSCRIPTS IN GENERAL @ https://hakorr.github.io/Userscripts/community/invite ("Userscript Hub")
-
- DANGER ZONE - DO NOT PROCEED IF YOU DON'T KNOW WHAT YOU'RE DOING*\
- \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
- //////////////////////////////////////////////////////////////////
- DANGER ZONE - DO NOT PROCEED IF YOU DON'T KNOW WHAT YOU'RE DOING*/
-
- (async () => { await LOAD_LEGACY_GM_SUPPORT();
- /*
- ______ _____ ______ _______
- | ____ | | | |_____] |_____| |
- |_____| |_____ |_____| |_____] | | |_____
-
-
- Code below this point runs on any site, including the GUI.
- */
-
- const backendConfig = {
- 'hosts': { 'prod': 'psyyke.github.io', 'dev': 'localhost' },
- 'path': '/A.C.A.S/'
- };
-
- const currentBackendUrlKey = 'currentBackendURL';
- const currentBackendUrl = typeof GM_getValue === 'function'
- ? GM_getValue(currentBackendUrlKey)
- : await GM.getValue(currentBackendUrlKey);
- const isBackendUrlUpToDate = Object.values(backendConfig.hosts).some(x => currentBackendUrl?.includes(x));
-
- function constructBackendURL(host) {
- const protocol = window.location.protocol + '//';
- const hosts = backendConfig.hosts;
-
- return protocol + (host || (hosts?.prod || hosts?.path)) + backendConfig.path;
- }
-
- function isRunningOnBackend(skipGM) {
- const hostsArr = Object.values(backendConfig.hosts);
-
- const foundHost = hostsArr.find(host => host === window?.location?.host);
- const isCorrectPath = window?.location?.pathname?.includes(backendConfig.path);
-
- const isBackend = typeof foundHost === 'string' && isCorrectPath;
-
- if(isBackend && !skipGM)
- GM_setValue(currentBackendUrlKey, constructBackendURL(foundHost));
-
- return isBackend;
- }
-
- // KEEP THESE AS FALSE ON PRODUCTION
- const debugModeActivated = false;
- const onlyUseDevelopmentBackend = false;
-
- const domain = window.location.hostname.replace('www.', '');
- const greasyforkURL = 'https://greasyfork.org/en/scripts/459137';
-
- function prependProtocolWhenNeeded(url) {
- if(!url.startsWith('http://') && !url.startsWith('https://')) {
- return 'http://' + url;
- }
-
- return url;
- }
-
- function getCurrentBackendURL(skipGmStorage) {
- if(onlyUseDevelopmentBackend) {
- return constructBackendURL(backendConfig.hosts?.dev);
- }
-
- const gmStorageUrl = GM_getValue(currentBackendUrlKey);
-
- if(skipGmStorage || !gmStorageUrl) {
- return constructBackendURL();
- }
-
- return prependProtocolWhenNeeded(gmStorageUrl);
- }
-
- if(!isBackendUrlUpToDate) {
- GM_setValue(currentBackendUrlKey, getCurrentBackendURL(true));
- }
-
- function createInstanceVariable(dbValue) {
- return {
- set: (instanceID, value) => GM_setValue(dbValues[dbValue](instanceID), { value, 'date': Date.now() }),
- get: instanceID => {
- const data = GM_getValue(dbValues[dbValue](instanceID));
-
- if(data?.date) {
- data.date = Date.now();
-
- GM_setValue(dbValues[dbValue](instanceID), data);
- }
-
- return data?.value;
- }
- }
- }
-
- // If you modify tempValueIndicator or AcasConfig key,
- // then modify them on the GUI (acas-globals.js) as well
- const tempValueIndicator = '-temp-value-';
- const dbValues = {
- AcasConfig: 'AcasConfig',
- playerColor: instanceID => 'playerColor' + tempValueIndicator + instanceID,
- turn: instanceID => 'turn' + tempValueIndicator + instanceID,
- fen: instanceID => 'fen' + tempValueIndicator + instanceID
- };
- // Add them to acas-userscript-bridge.js as well if you,
- // decide to add more variables here
- const instanceVars = {
- playerColor: createInstanceVariable('playerColor'),
- fen: createInstanceVariable('fen')
- };
-
- function exposeViaMessages() {
- const handlers = {
- USERSCRIPT_getValue: (args, messageId) => {
- const [key] = args;
- const value = GM_getValue(key);
- window.postMessage({ messageId, value }, '*');
- },
- USERSCRIPT_setValue: (args, messageId) => {
- const [key, value] = args;
- GM_setValue(key, value);
- window.postMessage({ messageId, value: true }, '*');
- },
- USERSCRIPT_deleteValue: (args, messageId) => {
- const [key] = args;
- GM_deleteValue(key);
- window.postMessage({ messageId, value: true }, '*');
- },
- USERSCRIPT_listValues: (args, messageId) => {
- const value = GM_listValues();
- window.postMessage({ messageId, value }, '*');
- },
- USERSCRIPT_getInfo: (args, messageId) => {
- const value = typeof GM_info !== 'undefined' ? JSON.parse(JSON.stringify(GM_info)) : {};
- window.postMessage({ messageId, value }, '*');
- },
- USERSCRIPT_instanceVars: (args, messageId) => {
- const [instanceId, key, value] = args;
-
- if (!instanceVars.hasOwnProperty(key)) {
- window.postMessage({ messageId, value: false }, '*');
- return;
- }
-
- const result = (value !== undefined)
- ? instanceVars[key].set(instanceId, value)
- : instanceVars[key].get(instanceId);
-
- window.postMessage({ messageId, value: result }, '*');
- }
- };
-
- window.addEventListener('message', (event) => {
- const handler = handlers[event.data?.type];
- if(handler) handler(event.data.args, event.data.messageId);
- });
-
- const script = document.createElement('script');
- script.innerHTML = 'window.isUserscriptActive = true;';
-
- document.head.appendChild(script);
- }
-
- function exposeViaUnsafe() {
- if(typeof unsafeWindow !== 'object') return;
-
- unsafeWindow.USERSCRIPT = {
- 'getValue': val => GM_getValue(val),
- 'setValue': (val, data) => GM_setValue(val, data),
- 'deleteValue': val => GM_deleteValue(val),
- 'listValues': val => GM_listValues(val),
- 'instanceVars': instanceVars,
- 'getInfo': () => GM_info
- };
-
- unsafeWindow.isUserscriptActive = true;
- }
-
- if(isRunningOnBackend()) {
- if(typeof unsafeWindow === 'object')
- exposeViaUnsafe();
- else
- exposeViaMessages();
-
- return;
- }
-
- /*
- _______ _ _ _______ _______ _______ _______ _____ _______ _______ _______
- | |_____| |______ |______ |______ |______ | | |______ |______
- |_____ | | |______ ______| ______| ______| __|__ | |______ ______|
-
-
- Code below this point only runs on chess sites, not on the GUI itself.
- */
-
- function getUniqueID() {
- return ([1e7]+-1e3+4e3+-8e3+-1e11).replace(/[018]/g, c =>
- (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
- )
- }
-
- const commLinkInstanceID = getUniqueID();
-
- const blacklistedURLs = [
- constructBackendURL(backendConfig?.hosts?.prod),
- constructBackendURL(backendConfig?.hosts?.dev),
- 'https://www.chess.com/play',
- 'https://lichess.org/',
- 'https://chess.org/',
- 'https://papergames.io/en/chess',
- 'https://playstrategy.org/',
- 'https://www.pychess.org/',
- 'https://www.coolmathgames.com/0-chess',
- 'https://chess.net/'
- ];
-
- const configKeys = {
- 'engineElo': 'engineElo',
- 'moveSuggestionAmount': 'moveSuggestionAmount',
- 'arrowOpacity': 'arrowOpacity',
- 'displayMovesOnExternalSite': 'displayMovesOnExternalSite',
- 'showMoveGhost': 'showMoveGhost',
- 'showOpponentMoveGuess': 'showOpponentMoveGuess',
- 'showOpponentMoveGuessConstantly': 'showOpponentMoveGuessConstantly',
- 'onlyShowTopMoves': 'onlyShowTopMoves',
- 'maxMovetime': 'maxMovetime',
- 'chessVariant': 'chessVariant',
- 'chessEngine': 'chessEngine',
- 'lc0Weight': 'lc0Weight',
- 'engineNodes': 'engineNodes',
- 'chessFont': 'chessFont',
- 'useChess960': 'useChess960',
- 'onlyCalculateOwnTurn': 'onlyCalculateOwnTurn',
- 'ttsVoiceEnabled': 'ttsVoiceEnabled',
- 'ttsVoiceName': 'ttsVoiceName',
- 'ttsVoiceSpeed': 'ttsVoiceSpeed',
- 'chessEngineProfile': 'chessEngineProfile',
- 'primaryArrowColorHex': 'primaryArrowColorHex',
- 'secondaryArrowColorHex': 'secondaryArrowColorHex',
- 'opponentArrowColorHex': 'opponentArrowColorHex',
- 'reverseSide': 'reverseSide',
- 'autoMove': 'autoMove',
- 'autoMoveLegit': 'autoMoveLegit',
- 'autoMoveRandom': 'autoMoveRandom',
- 'autoMoveAfterUser': 'autoMoveAfterUser',
- 'legitModeType': 'legitModeType',
- 'moveDisplayDelay': 'moveDisplayDelay',
- 'renderOnExternalSite': 'renderOnExternalSite',
- 'feedbackOnExternalSite': 'feedbackOnExternalSite'
- };
-
- const config = {};
-
- Object.values(configKeys).forEach(key => {
- config[key] = {
- get: profile => getGmConfigValue(key, commLinkInstanceID, profile),
- set: null
- };
- });
-
- let BoardDrawer = null;
- let chessBoardElem = null;
- let chesscomVariantPlayerColorsTable = null;
- let activeGuiMoveMarkings = [];
- let activeMetricRenders = [];
- let activeFeedback = [];
-
- let lastBoardRanks = null;
- let lastBoardFiles = null;
-
- let lastBoardSize = null;
- let lastPieceSize = null;
-
- let lastBoardMatrix = null;
- let lastBoardOrientation = null;
-
- let matchFirstSuggestionGiven = false;
-
- let lastMoveRequestTime = 0;
- let lastPieceAmount = 0;
-
- let isUserMouseDown = false;
- let activeAutomoves = [];
-
- const supportedSites = {};
-
- const pieceNameToFen = {
- 'pawn': 'p',
- 'knight': 'n',
- 'bishop': 'b',
- 'rook': 'r',
- 'queen': 'q',
- 'king': 'k'
- };
-
- function getArrowStyle(type, fill, opacity) {
- const baseStyleArr = [
- 'stroke: rgb(0 0 0 / 50%);',
- 'stroke-width: 2px;',
- 'stroke-linejoin: round;'
- ];
-
- switch(type) {
- case 'best':
- return [
- `fill: ${fill || 'limegreen'};`,
- `opacity: ${opacity || 0.9};`,
- ...baseStyleArr
- ].join('\n');
- case 'secondary':
- return [
- ...baseStyleArr,
- `fill: ${fill ? fill : 'dodgerblue'};`,
- `opacity: ${opacity || 0.7};`,
- ].join('\n');
- case 'opponent':
- return [
- ...baseStyleArr,
- `fill: ${fill ? fill : 'crimson'};`,
- `opacity: ${opacity || 0.3};`,
- ].join('\n');
- }
- };
-
- const CommLink = new CommLinkHandler(`frontend_${commLinkInstanceID}`, {
- 'singlePacketResponseWaitTime': 1500,
- 'maxSendAttempts': 3,
- 'statusCheckInterval': 1,
- 'silentMode': true
- });
-
- // manually register a command so that the variables are dynamic
- CommLink.commands['createInstance'] = async () => {
- return await CommLink.send('mum', 'createInstance', {
- 'domain': domain,
- 'instanceID': commLinkInstanceID,
- 'chessVariant': getChessVariant(),
- 'playerColor': getPlayerColorVariable()
- });
- }
-
- CommLink.registerSendCommand('ping', { commlinkID: 'mum', data: 'ping' });
- CommLink.registerSendCommand('pingInstance', { data: 'ping' });
- CommLink.registerSendCommand('log');
- CommLink.registerSendCommand('updateBoardOrientation');
- CommLink.registerSendCommand('updateBoardFen');
- CommLink.registerSendCommand('calculateBestMoves');
-
- CommLink.registerListener(`backend_${commLinkInstanceID}`, packet => {
- try {
- switch(packet.command) {
- case 'ping':
- return `pong (took ${Date.now() - packet.date}ms)`;
- case 'getFen':
- return getFen();
- case 'removeSiteMoveMarkings':
- boardUtils.removeMarkings();
- return true;
- case 'markMoveToSite':
- boardUtils.markMoves(packet.data);
-
- const profile = packet.data?.[0]?.profile;
- const isAutoMove = getConfigValue(configKeys.autoMove, profile);
- const isAutoMoveAfterUser = getConfigValue(configKeys.autoMoveAfterUser, profile);
-
- if (isAutoMove && (!isAutoMoveAfterUser || matchFirstSuggestionGiven)) {
- const existingAutomoves = activeAutomoves.filter(x => x.move.active);
-
- // Stop all existing automoves
- for(const x of existingAutomoves) {
- x.move.stop();
- }
-
- const isLegit = getConfigValue(configKeys.autoMoveLegit, profile);
- const isRandom = getConfigValue(configKeys.autoMoveRandom, profile);
-
- const move = isRandom
- ? packet.data[Math.floor(Math.random() * Math.random() * packet.data.length)]?.player
- : packet.data[0]?.player;
-
- makeMove(profile, move, isLegit);
- }
-
- matchFirstSuggestionGiven = true;
-
- return true;
- case 'renderMetricsToSite':
- renderMetrics(packet.data);
- return true;
- case 'feedbackToSite':
- displayFeedback(packet.data);
- return true;
- }
- } catch(e) {
- return null;
- }
- });
-
- function clearMetricRenders() {
- activeMetricRenders.forEach(elem => {
- if(elem) elem?.remove();
- });
- }
-
- function renderMetrics(addedMetrics) {
- clearMetricRenders();
-
- function processMetric(metric) {
- const data = metric?.data;
-
- if(!data) return;
-
- const shapeType = data?.shapeType;
- const shapeSquare = data?.shapeSquare;
- const shapeConfig = data?.shapeConfig;
-
- if(shapeType && shapeSquare && shapeConfig) {
- const shape = BoardDrawer.createShape(shapeType, shapeSquare, shapeConfig);
-
- activeMetricRenders.push(shape);
- }
- }
-
- function findMetricByType(type) {
- return addedMetrics.filter(metric => metric?.data?.shapeType === type) || [];
- }
-
- findMetricByType('text')
- .forEach(processMetric);
-
- findMetricByType('rectangle')
- .forEach(processMetric);
- }
-
- function clearFeedback() {
- activeFeedback.forEach(elem => {
- if(elem) elem?.remove();
- });
- }
-
-
- function displayFeedback(addedFeedback) {
- clearFeedback();
-
- function processFeedback(feedback) {
- const data = feedback?.data;
-
- if(!data) return;
-
- const shapeType = data?.shapeType;
- const shapeSquare = data?.shapeSquare;
- const shapeConfig = data?.shapeConfig;
-
- if(shapeType && shapeSquare && shapeConfig) {
- const shape = BoardDrawer.createShape(shapeType, shapeSquare, shapeConfig);
-
- activeFeedback.push(shape);
- }
- }
-
- addedFeedback.forEach(processFeedback);
- }
-
- const boardUtils = {
- markMoves: moveObjArr => {
- const maxScale = 1;
- const minScale = 0.5;
- const totalRanks = moveObjArr.length;
-
- moveObjArr.forEach((markingObj, idx) => {
- const profile = markingObj.profile;
-
- if(idx === 0)
- boardUtils.removeMarkings(profile);
-
- const [from, to] = markingObj.player;
- const [oppFrom, oppTo] = markingObj.opponent;
- const oppMovesExist = oppFrom && oppTo;
- const rank = idx + 1;
-
- const showOpponentMoveGuess = getConfigValue(configKeys.showOpponentMoveGuess, profile);
- const showOpponentMoveGuessConstantly = getConfigValue(configKeys.showOpponentMoveGuessConstantly, profile);
-
- const arrowOpacity = getConfigValue(configKeys.arrowOpacity, profile) / 100;
- const primaryArrowColorHex = getConfigValue(configKeys.primaryArrowColorHex, profile);
- const secondaryArrowColorHex = getConfigValue(configKeys.secondaryArrowColorHex, profile);
- const opponentArrowColorHex = getConfigValue(configKeys.opponentArrowColorHex, profile);
-
- let playerArrowElem = null;
- let oppArrowElem = null;
- let arrowStyle = getArrowStyle('best', primaryArrowColorHex, arrowOpacity);
- let lineWidth = 30;
- let arrowheadWidth = 80;
- let arrowheadHeight = 60;
- let startOffset = 30;
-
- if(idx !== 0) {
- arrowStyle = getArrowStyle('secondary', secondaryArrowColorHex, arrowOpacity);
-
- const arrowScale = totalRanks === 2
- ? 0.75
- : maxScale - (maxScale - minScale) * ((rank - 1) / (totalRanks - 1));
-
- lineWidth = lineWidth * arrowScale;
- arrowheadWidth = arrowheadWidth * arrowScale;
- arrowheadHeight = arrowheadHeight * arrowScale;
- startOffset = startOffset;
- }
-
- playerArrowElem = BoardDrawer.createShape('arrow', [from, to],
- {
- style: arrowStyle,
- lineWidth, arrowheadWidth, arrowheadHeight, startOffset
- }
- );
-
- if(oppMovesExist && showOpponentMoveGuess) {
- oppArrowElem = BoardDrawer.createShape('arrow', [oppFrom, oppTo],
- {
- style: getArrowStyle('opponent', opponentArrowColorHex, arrowOpacity),
- lineWidth, arrowheadWidth, arrowheadHeight, startOffset
- }
- );
-
- if(showOpponentMoveGuessConstantly) {
- oppArrowElem.style.display = 'block';
- } else {
- const squareListener = BoardDrawer.addSquareListener(from, type => {
- if(!oppArrowElem) {
- squareListener.remove();
- }
-
- switch(type) {
- case 'enter':
- oppArrowElem.style.display = 'inherit';
- break;
- case 'leave':
- oppArrowElem.style.display = 'none';
- break;
- }
- });
- }
- }
-
- if(idx === 0 && playerArrowElem) {
- const parentElem = playerArrowElem.parentElement;
-
- // move best arrow element on top (multiple same moves can hide the best move)
- parentElem.appendChild(playerArrowElem);
-
- if(oppArrowElem) {
- parentElem.appendChild(oppArrowElem);
- }
- }
-
- activeGuiMoveMarkings.push({ ...markingObj, playerArrowElem, oppArrowElem, profile });
- });
- },
- removeMarkings: profile => {
- let removalArr = activeGuiMoveMarkings;
-
- if(profile) {
- removalArr = removalArr.filter(obj => obj.profile === profile);
-
- activeGuiMoveMarkings = activeGuiMoveMarkings.filter(obj => obj.profile !== profile);
- } else {
- activeGuiMoveMarkings = [];
- }
-
- removalArr.forEach(markingObj => {
- markingObj.oppArrowElem?.remove();
- markingObj.playerArrowElem?.remove();
- });
- },
- setBoardOrientation: orientation => {
- if(BoardDrawer) {
- if(debugModeActivated) console.warn('setBoardOrientation', orientation);
-
- BoardDrawer.setOrientation(orientation);
- }
- },
- setBoardDimensions: dimensionArr => {
- if(BoardDrawer) {
- if(debugModeActivated) console.warn('setBoardDimensions', dimensionArr);
-
- BoardDrawer.setBoardDimensions(dimensionArr);
- }
- }
- };
-
- function displayImportantNotification(title, text) {
- if(typeof GM_notification === 'function') {
- GM_notification({ title: title, text: text });
- } else {
- alert(`[${title}]` + '\n\n' + text);
- }
- }
-
- function filterInvisibleElems(elementArr, inverse) {
- return [...elementArr].filter(elem => {
- const style = getComputedStyle(elem);
- const bounds = elem.getBoundingClientRect();
-
- const isHidden =
- style.visibility === 'hidden' ||
- style.display === 'none' ||
- style.opacity === '0' ||
- bounds.width == 0 ||
- bounds.height == 0;
-
- return inverse ? isHidden : !isHidden;
- });
- }
-
- function getElementSize(elem) {
- const rect = elem.getBoundingClientRect();
-
- if(rect.width !== 0 && rect.height !== 0) {
- return { width: rect.width, height: rect.height };
- }
-
- const computedStyle = window.getComputedStyle(elem);
- const width = parseFloat(computedStyle.width);
- const height = parseFloat(computedStyle.height);
-
- return { width, height };
- }
-
- function extractElemTransformData(elem) {
- const computedStyle = window.getComputedStyle(elem);
- const transformMatrix = new DOMMatrix(computedStyle.transform);
-
- const x = transformMatrix.e;
- const y = transformMatrix.f;
-
- return [x, y];
- }
-
- function getElemCoordinatesFromTransform(elem, config) {
- const onlyFlipX = config?.onlyFlipX;
- const onlyFlipY = config?.onlyFlipY;
-
- lastBoardSize = getElementSize(chessBoardElem);
-
- const [files, ranks] = getBoardDimensions();
-
- lastBoardRanks = ranks;
- lastBoardFiles = files;
-
- const boardOrientation = getPlayerColorVariable();
-
- let [x, y] = extractElemTransformData(elem);
-
- const boardDimensions = lastBoardSize;
- let squareDimensions = boardDimensions.width / lastBoardRanks;
-
- const normalizedX = Math.round(x / squareDimensions);
- const normalizedY = Math.round(y / squareDimensions);
-
- if(onlyFlipY || boardOrientation === 'w') {
- const flippedY = lastBoardFiles - normalizedY - 1;
-
- return [normalizedX, flippedY];
- } else {
- const flippedX = lastBoardRanks - normalizedX - 1;
-
- return [flippedX, normalizedY];
- }
- }
-
- function getElemCoordinatesFromLeftBottomPercentages(elem) {
- if(!lastBoardRanks || !lastBoardFiles) {
- const [files, ranks] = getBoardDimensions();
-
- lastBoardRanks = ranks;
- lastBoardFiles = files;
- }
-
- const boardOrientation = getPlayerColorVariable();
-
- const leftPercentage = parseFloat(elem.style.left?.replace('%', ''));
- const bottomPercentage = parseFloat(elem.style.bottom?.replace('%', ''));
-
- const x = Math.max(Math.round(leftPercentage / (100 / lastBoardRanks)), 0);
- const y = Math.max(Math.round(bottomPercentage / (100 / lastBoardFiles)), 0);
-
- if (boardOrientation === 'w') {
- return [x, y];
- } else {
- const flippedX = lastBoardRanks - (x + 1);
- const flippedY = lastBoardFiles - (y + 1);
-
- return [flippedX, flippedY];
- }
- }
-
- function getElemCoordinatesFromLeftTopPixels(elem) {
- const pieceSize = getElementSize(elem);
-
- lastPieceSize = pieceSize;
-
- const leftPixels = parseFloat(elem.style.left?.replace('px', ''));
- const topPixels = parseFloat(elem.style.top?.replace('px', ''));
-
- const x = Math.max(Math.round(leftPixels / pieceSize.width), 0);
- const y = Math.max(Math.round(topPixels / pieceSize.width), 0);
-
- const boardOrientation = getPlayerColorVariable();
-
- if (boardOrientation === 'w') {
- const flippedY = lastBoardFiles - (y + 1);
-
- return [x, flippedY];
- } else {
- const flippedX = lastBoardRanks - (x + 1);
-
- return [flippedX, y];
- }
- }
-
- function updateChesscomVariantPlayerColorsTable() {
- let colors = [];
-
- document.querySelectorAll('*[data-color]').forEach(pieceElem => {
- const colorCode = Number(pieceElem?.dataset?.color);
-
- if(!colors?.includes(colorCode)) {
- colors.push(colorCode);
- }
- });
-
- if(colors?.length > 1) {
- colors = colors.sort((a, b) => a - b);
-
- chesscomVariantPlayerColorsTable = { [colors[0]]: 'w', [colors[1]]: 'b' };
- }
- }
-
- function getBoardDimensionsFromSize() {
- const boardDimensions = getElementSize(chessBoardElem);
-
- lastBoardSize = boardDimensions;
-
- const boardWidth = boardDimensions?.width;
- const boardHeight = boardDimensions.height;
-
- const boardPiece = getPieceElem();
-
- if(boardPiece) {
- const pieceDimensions = getElementSize(boardPiece);
-
- lastPieceSize = getElementSize(boardPiece);
-
- const boardPieceWidth = pieceDimensions?.width;
- const boardPieceHeight = pieceDimensions?.height;
-
- const boardRanks = Math.floor(boardWidth / boardPieceWidth);
- const boardFiles = Math.floor(boardHeight / boardPieceHeight);
-
- const ranksInAllowedRange = 0 < boardRanks && boardRanks <= 69;
- const filesInAllowedRange = 0 < boardFiles && boardFiles <= 69;
-
- if(ranksInAllowedRange && filesInAllowedRange) {
- return [boardRanks, boardFiles];
- }
- }
- }
-
- function chessCoordinatesToIndex(coord) {
- const x = coord.charCodeAt(0) - 97;
- let y = null;
-
- const lastHalf = coord.slice(1);
-
- if(lastHalf === ':') {
- y = 9;
- } else {
- y = Number(coord.slice(1)) - 1;
- }
-
- return [x, y];
- }
-
- /* Need to make the board matricies more cohesive, right now it's really confusing flipping them
- * differently for each function. I just can't be bothered right now so please don't make fun of it.
- * Thanks, Haka
- * */
-
- function chessCoordinatesToMatrixIndex(coord) {
- const [boardRanks, boardFiles] = getBoardDimensions();
- const indexArr = chessCoordinatesToIndex(coord);
-
- let x, y;
-
- y = boardFiles - (indexArr[1] + 1);
- x = indexArr[0];
-
- return [x, y];
- }
-
- function chessCoordinatesToDomIndex(coord) {
- const [boardRanks, boardFiles] = getBoardDimensions();
- const indexArr = chessCoordinatesToIndex(coord);
- const boardOrientation = getBoardOrientation();
-
- let x, y;
-
- if(boardOrientation === 'w') {
- x = indexArr[0];
- y = boardFiles - (indexArr[1] + 1);
- } else {
- x = boardRanks - (indexArr[0] + 1);
- y = indexArr[1];
- }
-
- return [x, y];
- }
-
- function indexToChessCoordinates(coord) {
- const [boardRanks, boardFiles] = getBoardDimensions();
- const boardOrientation = getBoardOrientation();
-
- const [x, y] = coord;
- const file = String.fromCharCode('a'.charCodeAt(0) + x);
-
- let rank;
-
- if (boardOrientation === 'w') {
- rank = boardRanks - y;
- } else {
- rank = boardRanks - y;
- }
-
- return `${file}${rank}`;
- }
-
- function isPawnPromotion(bestMove) {
- const [fenCoordFrom, fenCoordTo] = bestMove;
- const piece = getBoardPiece(fenCoordFrom);
-
- if(typeof piece !== 'string' || piece.toLowerCase() !== 'p')
- return false;
-
- // Determine the row from the ending coordinate (assumes standard algebraic notation, e.g., 'e8')
- const endingRow = parseInt(fenCoordTo[1], 10);
-
- // Check if the pawn reaches the promotion row
- if ((piece === 'P' && endingRow === (lastBoardFiles ?? 8)) || (piece === 'p' && endingRow === 1)) {
- return true;
- }
-
- return false;
- }
-
-
- function fenCoordArrToDomCoord(fenCoordArr) {
- // fenCoordArr e.g. ["e6", "e5"]
-
- const boardClientRect = chessBoardElem.getBoundingClientRect();
-
- const pieceElem = getPieceElem();
- const pieceDimensions = getElementSize(pieceElem);
- const pieceWidth = pieceDimensions?.width;
- const pieceHeight = pieceDimensions?.height;
-
- lastPieceSize = pieceDimensions;
-
- const [boardRanks, boardFiles] = getBoardDimensions();
-
- // Array to hold the center coordinates of each square
- const centerCoordinates = fenCoordArr.map(coord => {
- const [x, y] = chessCoordinatesToDomIndex(coord);
-
- const centerX = boardClientRect.x + (x * pieceWidth) + (pieceWidth / 2);
- const centerY = boardClientRect.y + (y * pieceHeight) + (pieceHeight / 2);
-
- return [centerX, centerY];
- });
-
- return centerCoordinates;
- }
-
- function getRandomOwnPieceDomCoord(fenCoord, boardMatrix) {
- const boardOrientation = getBoardOrientation();
- let [x, y] = chessCoordinatesToMatrixIndex(fenCoord);
-
- const pieceAtFenCoord = boardMatrix[y][x];
-
- if(pieceAtFenCoord === 1) {
- return null;
- }
-
- const isWhitePiece = pieceAtFenCoord === pieceAtFenCoord.toUpperCase();
-
- const getDistance = (row1, col1, row2, col2) => {
- return Math.abs(row1 - row2) + Math.abs(col1 - col2);
- };
-
- let candidatePieces = [];
-
- // Loop through the board matrix to find all close own pieces
- for(let row = 0; row < boardMatrix.length; row++) {
- for(let col = 0; col < boardMatrix[row].length; col++) {
- const currentPiece = boardMatrix[row][col];
-
- // Skip if no piece is found or if the piece is of the wrong color
- if(currentPiece === 1 || (isWhitePiece && currentPiece === currentPiece.toLowerCase()) || (!isWhitePiece && currentPiece === currentPiece.toUpperCase())) {
- continue;
- }
-
- const distance = getDistance(y, x, row, col);
-
- if(distance < 6) {
- candidatePieces.push({ distance, coord: [col, row], piece: currentPiece });
- }
- }
- }
-
- if (candidatePieces.length > 0) {
- // Choose a random piece from the candidates
- const randomIndex = Math.floor(Math.random() * candidatePieces.length);
- const chosenPiece = candidatePieces[randomIndex];
-
- return fenCoordArrToDomCoord([indexToChessCoordinates(chosenPiece.coord)])[0];
- }
-
- return null;
- }
-
- function getPieceAmount() {
- return getPieceElem(true)?.length ?? 0;
- }
-
- class AutomaticMove {
- constructor(profile, fenMoveArr, isLegit, callback) {
- this.id = getUniqueID();
-
- // activeAutomoves is an external variable, not a child of AutomaticMove
- activeAutomoves.push({ 'id': this.id, 'move': this });
-
- this.profile = profile;
- this.fenMoveArr = fenMoveArr;
- this.isLegit = isLegit;
-
- this.active = true;
- this.isPromotingPawn = false;
-
- this.onFinished = function(...args) {
- activeAutomoves.filter(x => x.id !== this.id); // remove the move from the active automove list
-
- this.active = false;
-
- callback(...args);
- };
-
- this.moveDomCoords = fenCoordArrToDomCoord(fenMoveArr);
- this.isPromotion = isPawnPromotion(fenMoveArr);
-
- if(this.isLegit) {
- const legitModeType = getConfigValue(configKeys.legitModeType, this.profile) ?? 'casual';
-
- const pieceRanges = [
- { minPieces: 30, maxPieces: Infinity }, // Opening (60+ pieces)
- { minPieces: 23, maxPieces: 29 }, // Early Middlegame (48 to 64 pieces)
- { minPieces: 16, maxPieces: 22 }, // Mid Middlegame (32 to 48 pieces)
- { minPieces: 10, maxPieces: 15 }, // Late Middlegame (16 to 32 pieces)
- { minPieces: 6, maxPieces: 9 }, // Endgame (8 to 16 pieces)
- { minPieces: 3, maxPieces: 5 }, // Very Endgame (2 to 8 pieces)
- { minPieces: 1, maxPieces: 2 }, // Extremely Few Pieces (1 piece)
- ];
-
- const timeRanges = {
- beginner: [
- [2000, 4000],
- [3000, 15000],
- [5000, 25000],
- [4000, 30000],
- [3000, 15000],
- [2000, 10000],
- [1000, 4000],
- ],
- casual: [
- [900, 3000], // Opening
- [1000, 15000], // Early Middlegame
- [3000, 20000], // Mid Middlegame
- [2000, 13000], // Late Middlegame
- [1500, 10000], // Endgame
- [1000, 9000], // Very Endgame
- [500, 3000], // Extremely Few Pieces
- ],
- intermediate: [
- [750, 2000],
- [1000, 10000],
- [2000, 15000],
- [1500, 12000],
- [1000, 8000],
- [750, 7000],
- [500, 2000],
- ],
- advanced: [
- [500, 1500],
- [1000, 8000],
- [750, 8000],
- [750, 12000],
- [750, 5000],
- [750, 3000],
- [500, 1200],
- ],
- master: [
- [333, 999],
- [400, 2000],
- [400, 3000],
- [400, 2500],
- [400, 2000],
- [400, 1500],
- [333, 750],
- ],
- professional: [
- [333, 666],
- [333, 666],
- [333, 1000],
- [333, 1500],
- [333, 1000],
- [333, 666],
- [333, 666],
- ],
- god: [
- [50, 333],
- [50, 233],
- [50, 300],
- [50, 250],
- [50, 200],
- [50, 150],
- [50, 100],
- ]
- };
-
- this.timeRanges = pieceRanges.map((range, index) => ({
- ...range,
- timeRange: timeRanges[legitModeType][index],
- }));
-
- this.shouldHesitate = this.isLegit && Math.random() < 0.15;
- this.shouldHesitateTwice = this.isLegit && Math.random() < 0.25;
- this.hesitationTypeOne = this.isLegit && Math.random() < 0.35;
-
- const legitTotalMoveTime = this.calculateMoveTime(getPieceAmount());
- const elapsedMoveTime = (Date.now() - lastMoveRequestTime); // How long did it take for the engine to calculate the move
- const remainingTime = Math.max(legitTotalMoveTime - elapsedMoveTime, 500);
-
- const delays = this.generateDelaysForDesiredTime(remainingTime);
-
- for(const key of Object.keys(delays)) {
- this[key] = delays[key];
- }
- }
-
- this.start();
- }
-
- generateDelaysForDesiredTime(desiredTotalTime) {
- // Fixed minimum values
- const PROMOTION_DELAY = this.getRandomIntegerBetween(1000, 1111); // just can't be done very fast for some reason at least on Chess.com
-
- if(desiredTotalTime > 6000) {
- const timelines = [
- { move: .4, to: .2, hesitation: .15, hesitationResolve: .15, secondHesitationResolve: .15 },
- { move: .1, to: .3, hesitation: .25, hesitationResolve: .15, secondHesitationResolve: .2 },
- { move: .2, to: .25, hesitation: .2, hesitationResolve: .2, secondHesitationResolve: .15 }
- ];
-
- const timeline = timelines[Math.floor(Math.random() * timelines.length)];
-
- return {
- promotionDelay: PROMOTION_DELAY,
- moveDelay: desiredTotalTime * timeline.move,
- toSquareSelectDelay: desiredTotalTime * timeline.to,
- hesitationDelay: desiredTotalTime * timeline.hesitation,
- hesitationResolveDelay: desiredTotalTime * timeline.hesitationResolve,
- secondHesitationResolveDelay: desiredTotalTime * timeline.secondHesitationResolve
- };
- }
- // There is time for one hesitation
- if(desiredTotalTime > 3000) {
- const timelines = [
- { move: .3, to: .2, hesitation: .25, hesitationResolve: .25 },
- { move: .1, to: .3, hesitation: .45, hesitationResolve: .15 },
- { move: .2, to: .25, hesitation: .2, hesitationResolve: .35 }
- ];
-
- const timeline = timelines[Math.floor(Math.random() * timelines.length)];
-
- return {
- promotionDelay: PROMOTION_DELAY,
- moveDelay: desiredTotalTime * timeline.move,
- toSquareSelectDelay: desiredTotalTime * timeline.to,
- hesitationDelay: desiredTotalTime * timeline.hesitation,
- hesitationResolveDelay: desiredTotalTime * timeline.hesitationResolve,
- secondHesitationResolveDelay: -1
- };
- }
- // There is not enough time to hesitate
- else {
- const timelines = [
- { move: .9, to: .1 },
- { move: .45, to: .55 },
- { move: .6, to: .4 },
- { move: .4, to: .6 },
- { move: .1, to: .9 }
- ];
-
- const timeline = timelines[Math.floor(Math.random() * timelines.length)];
-
- return {
- promotionDelay: PROMOTION_DELAY,
- moveDelay: desiredTotalTime * timeline.move,
- toSquareSelectDelay: desiredTotalTime * timeline.to,
- hesitationDelay: -1, hesitationResolveDelay: -1, secondHesitationResolveDelay: -1
- };
- }
- }
-
- calculateMoveTime(pieceCount) {
- for(let range of this.timeRanges) {
- if(pieceCount >= range.minPieces && pieceCount <= range.maxPieces) {
- return this.getRandomIntegerBetween(range.timeRange[0], range.timeRange[1]);
- }
- }
-
- return 500;
- }
-
- getRandomIntegerBetween(min, max) {
- return Math.floor(Math.random() * (max - min + 1)) + min;
- }
-
- getRandomIntegerNearAverage(min, max) {
- const mid = (min + max) / 2;
- const range = (max - min) / 2;
-
- let value = Math.floor(mid + (Math.random() - 0.5) * range * 1.5);
-
- return Math.max(min, Math.min(max, value));
- }
-
- delay(ms) {
- return this.active ? new Promise(resolve => setTimeout(resolve, ms)) : true;
- }
-
- async triggerPieceClick(input) {
- const parentExists = activeAutomoves.find(x => x.move === this) ? true : false;
-
- if(!parentExists) {
- return;
- }
-
- let clientX, clientY;
-
- if(input instanceof Element) {
- const rect = input.getBoundingClientRect();
- clientX = rect.left + rect.width / 2;
- clientY = rect.top + rect.height / 2;
- } else if (typeof input === 'object') {
- clientX = input[0];
- clientY = input[1];
- } else {
- return;
- }
-
- const xDivider = Math.random() < 0.85 ? 4 : Math.random() < 0.15 ? 3 : 2;
- const yDivider = Math.random() < 0.65 ? 3 : Math.random() < 0.35 ? 2 : 4;
-
- const randomVariationX = (lastPieceSize?.width - 4) / xDivider;
- const randomVariationY = (lastPieceSize?.height - 4) / yDivider;
-
- const randomOffsetX = (Math.random() - 0.5) * 2 * randomVariationX;
- const randomOffsetY = (Math.pow(Math.random(), 0.5) - 0.5) * 2 * randomVariationY;
-
- const randomizedX = clientX + randomOffsetX;
- const randomizedY = clientY + randomOffsetY;
-
- const pointerEventOptions = {
- bubbles: true,
- cancelable: true,
- clientX: randomizedX,
- clientY: randomizedY,
- };
-
- const elementToTrigger = (input instanceof Element) ? input : document.elementFromPoint(clientX, clientY);
-
- if(elementToTrigger) {
- switch(domain) {
- case 'chess.com':
- elementToTrigger.dispatchEvent(new PointerEvent('pointerdown', pointerEventOptions));
-
- if(this.isLegit) await this.delay(this.getRandomIntegerNearAverage(35, 125));
-
- elementToTrigger.dispatchEvent(new PointerEvent('pointerup', pointerEventOptions));
-
- break;
- case 'lichess.org':
- elementToTrigger.dispatchEvent(new MouseEvent('mousedown', pointerEventOptions));
-
- if(this.isLegit) await this.delay(this.getRandomIntegerNearAverage(35, 125));
-
- elementToTrigger.dispatchEvent(new MouseEvent('mouseup', pointerEventOptions));
-
- break;
- case 'chessarena.com':
- elementToTrigger.dispatchEvent(new MouseEvent('mousedown', pointerEventOptions));
-
- if(this.isLegit) await this.delay(this.getRandomIntegerNearAverage(35, 125));
-
- elementToTrigger.dispatchEvent(new MouseEvent('mouseup', pointerEventOptions));
-
- break;
- }
- }
-
- if(debugModeActivated) {
- const dot = document.createElement('div');
- dot.style.position = 'absolute';
- dot.style.width = '7px';
- dot.style.height = '7px';
- dot.style.borderRadius = '50%';
- dot.style.backgroundColor = 'lime';
- dot.style.left = `${randomizedX - 2.5}px`;
- dot.style.top = `${randomizedY - 2.5}px`;
-
- const container = document.createElement('div');
- container.style.position = 'absolute';
- container.style.width = `${Math.round(randomVariationX * 2)}px`;
- container.style.height = `${Math.round(randomVariationY * 2)}px`;
- container.style.border = '2px dashed green';
- container.style.backgroundColor = 'rgba(0, 0, 0, 0.3)';
- container.style.left = `${clientX - randomVariationX}px`;
- container.style.top = `${clientY - randomVariationY}px`;
-
- document.body.appendChild(container);
- document.body.appendChild(dot);
-
- setTimeout(() => {
- dot.remove();
- container.remove();
- }, 1000);
- }
- }
-
- click(domCoord) {
- if(this.active)
- this.triggerPieceClick(domCoord);
- }
-
- async hesitate() {
- const hesitationPieceDomCoord = getRandomOwnPieceDomCoord(this.fenMoveArr[0], getBoardMatrix());
-
- if(hesitationPieceDomCoord) {
- if(this.hesitationTypeOne) {
- this.click(this.moveDomCoords[0]);
- await this.delay(this.hesitationDelay);
- }
-
- this.click(hesitationPieceDomCoord);
-
- await this.delay(this.hesitationResolveDelay);
-
- if(this.shouldHesitateTwice && this.secondHesitationResolveDelay !== -1) {
- const secondHesitationPieceDomCoord = getRandomOwnPieceDomCoord(this.fenMoveArr[0], getBoardMatrix());
- this.click(secondHesitationPieceDomCoord);
- await this.delay(this.secondHesitationResolveDelay);
- }
- }
-
- this.finishMove(this.toSquareSelectDelay, this.promotionDelay);
- }
-
-
- async finishMove(delay01, delay02) {
- this.click(this.moveDomCoords[0]);
-
- await this.delay(delay01);
-
- this.click(this.moveDomCoords[1]);
-
- // Handle promotion click if necessary
- if(this.isPromotion) {
- this.isPromotingPawn = true;
-
- await this.delay(delay02);
-
- this.click(this.moveDomCoords[1]);
-
- this.isPromotingPawn = false;
- }
-
- this.onFinished(true);
- }
-
- async playLegit() {
- await this.delay(this.moveDelay);
-
- if(this.shouldHesitate && this.hesitationDelay !== -1)
- this.hesitate();
- else
- this.finishMove(this.toSquareSelectDelay, this.promotionDelay);
- }
-
- async start() {
- if(this.isLegit) {
- this.playLegit();
- } else {
- this.finishMove(5, 1111);
- }
- }
-
- async stop() {
- if(this.isPromotingPawn) {
- // Attempt to promote the pawn before closing
- this.click(this.moveDomCoords[1]);
- }
-
- this.onFinished(false);
- }
- }
-
- async function makeMove(profile, fenMoveArr, isLegit) {
- const move = new AutomaticMove(profile, fenMoveArr, isLegit, e => {
- // This is ran when the move finished
-
- if(debugModeActivated) console.warn('Move', fenMoveArr, move.id, 'finished', 'for profile:', profile);
- });
- }
-
- function getGmConfigValue(key, instanceID, profileID) {
- if(typeof profileID === 'object') {
- profileID = profileID.name;
- }
-
- const config = GM_getValue(dbValues.AcasConfig);
-
- const instanceValue = config?.instance?.[instanceID]?.[key];
- const globalValue = config?.global?.[key];
-
- if(instanceValue !== undefined) {
- return instanceValue;
- }
-
- if(globalValue !== undefined) {
- return globalValue;
- }
-
- if(profileID) {
- const globalProfileValue = config?.global?.['profiles']?.[profileID]?.[key];
- const instanceProfileValue = config?.instance?.[instanceID]?.['profiles']?.[profileID]?.[key];
-
- if(instanceProfileValue !== undefined) {
- return instanceProfileValue;
- }
-
- if(globalProfileValue !== undefined) {
- return globalProfileValue;
- }
- }
-
- return null;
- }
-
- function isBoardDrawerNeeded() {
- const config = GM_getValue(dbValues.AcasConfig);
-
- const gP = config?.global?.['profiles'];
- const iP = config?.instance?.[commLinkInstanceID]?.['profiles'];
-
- if(gP) {
- const globalProfiles = Object.keys(gP);
-
- for(profileName of globalProfiles) {
- const externalMoves = gP[profileName][configKeys.displayMovesOnExternalSite];
- const externalRenders = gP[profileName][configKeys.renderOnExternalSite];
- const externalFeedback = gP[profileName][configKeys.feedbackOnExternalSite];
-
-
- if(externalMoves || externalRenders || externalFeedback) {
- return true;
- }
- }
- }
-
- if(iP) {
- const instanceProfiles = Object.keys(iP);
-
- for(profileName of instanceProfiles) {
- const externalMoves = iP[profileName][configKeys.displayMovesOnExternalSite];
- const externalRenders = gP[profileName][configKeys.renderOnExternalSite];
- const externalFeedback = gP[profileName][configKeys.feedbackOnExternalSite];
-
- if(externalMoves || externalRenders || externalFeedback) {
- return true;
- }
- }
- }
-
- return false;
- }
-
- function getConfigValue(key, profile) {
- return config[key]?.get(profile);
- }
-
- function setConfigValue(key, val) {
- return config[key]?.set(val);
- }
-
- function squeezeEmptySquares(fenStr) {
- return fenStr.replace(/1+/g, match => match.length);
- }
-
- function getPlayerColorVariable() {
- return instanceVars.playerColor.get(commLinkInstanceID);
- }
-
- function getFenPieceColor(pieceFenStr) {
- return pieceFenStr == pieceFenStr.toUpperCase() ? 'w' : 'b';
- }
-
- function getFenPieceOppositeColor(pieceFenStr) {
- return getFenPieceColor(pieceFenStr) == 'w' ? 'b' : 'w';
- }
-
- function convertPieceStrToFen(str) {
- if(!str || str.length !== 2) {
- return null;
- }
-
- const firstChar = str[0].toLowerCase();
- const secondChar = str[1];
-
- if(firstChar === 'w') {
- return secondChar.toUpperCase();
- } else if (firstChar === 'b') {
- return secondChar.toLowerCase();
- }
-
- return null;
- }
-
- function getCanvasPixelColor(canvas, [xPercentage, yPercentage], debug) {
- const ctx = canvas.getContext('2d');
-
- const x = xPercentage * canvas.width;
- const y = yPercentage * canvas.height;
-
- const imageData = ctx.getImageData(x, y, 1, 1);
- const pixel = imageData.data;
- const brightness = (pixel[0] + pixel[1] + pixel[2]) / 3;
-
- if(debug) {
- const clonedCanvas = document.createElement('canvas');
- clonedCanvas.width = canvas.width;
- clonedCanvas.height = canvas.height;
-
- const clonedCtx = clonedCanvas.getContext('2d');
- clonedCtx.drawImage(canvas, 0, 0);
-
- clonedCtx.fillStyle = 'red';
- clonedCtx.beginPath();
- clonedCtx.arc(x, y, 1, 0, Math.PI * 2);
- clonedCtx.fill();
-
- const dataURL = clonedCanvas.toDataURL();
-
- console.log(canvas, pixel, dataURL);
- }
-
- return brightness < 128 ? 'b' : 'w';
- }
-
- function canvasHasPixelAt(canvas, [xPercentage, yPercentage], debug) {
- xPercentage = Math.min(Math.max(xPercentage, 0), 100);
- yPercentage = Math.min(Math.max(yPercentage, 0), 100);
-
- const ctx = canvas.getContext('2d');
- const x = xPercentage * canvas.width;
- const y = yPercentage * canvas.height;
-
- const imageData = ctx.getImageData(x, y, 1, 1);
- const pixel = imageData.data;
-
- if(debug) {
- const clonedCanvas = document.createElement('canvas');
- clonedCanvas.width = canvas.width;
- clonedCanvas.height = canvas.height;
-
- const clonedCtx = clonedCanvas.getContext('2d');
- clonedCtx.drawImage(canvas, 0, 0);
-
- clonedCtx.fillStyle = 'red';
- clonedCtx.beginPath();
- clonedCtx.arc(x, y, 1, 0, Math.PI * 2);
- clonedCtx.fill();
-
- const dataURL = clonedCanvas.toDataURL();
-
- console.log(canvas, pixel, dataURL);
- }
-
- return pixel[3] !== 0;
- }
-
- function getSiteData(dataType, obj) {
- const pathname = window.location.pathname;
-
- let dataObj = { pathname };
-
- if(obj && typeof obj === 'object') {
- dataObj = { ...dataObj, ...obj };
- }
-
- const dataHandlerFunction = supportedSites[domain]?.[dataType];
-
- if(typeof dataHandlerFunction !== 'function') {
- return null;
- }
-
- const result = dataHandlerFunction(dataObj);
-
- return result;
- }
-
- function addSupportedChessSite(domain, typeHandlerObj) {
- supportedSites[domain] = typeHandlerObj;
- }
-
- function getBoardElem() {
- const boardElem = getSiteData('boardElem');
-
- return boardElem || null;
- }
-
- function getPieceElem(getAll) {
- const boardElem = getBoardElem();
-
- const boardQuerySelector = (getAll ? query => [...boardElem?.querySelectorAll(query)] : boardElem?.querySelector?.bind(boardElem));
-
- if(typeof boardQuerySelector !== 'function')
- return null;
-
- const pieceElem = getSiteData('pieceElem', { boardQuerySelector, getAll });
-
- return pieceElem || null;
- }
-
- function getSquareElems(element) {
- const squareElems = getSiteData('squareElems', { element });
-
- return squareElems || null;
- }
-
- function getChessVariant() {
- const chessVariant = getSiteData('chessVariant');
-
- return chessVariant || null;
- }
-
- function getBoardOrientation() {
- const boardOrientation = getSiteData('boardOrientation');
-
- return boardOrientation || null;
- }
-
- function getPieceElemFen(pieceElem) {
- const pieceFen = getSiteData('pieceElemFen', { pieceElem });
-
- return pieceFen || null;
- }
-
- // this function gets called a lot, needs to be optimized
- function getPieceElemCoords(pieceElem) {
- const pieceCoords = getSiteData('pieceElemCoords', { pieceElem });
-
- return pieceCoords || null;
- }
-
- function getBoardDimensions() {
- const boardDimensionArr = getSiteData('boardDimensions');
-
- if(boardDimensionArr) {
- lastBoardRanks = boardDimensionArr[0];
- lastBoardFiles = boardDimensionArr[1];
-
- return boardDimensionArr;
- } else {
- lastBoardRanks = 8;
- lastBoardFiles = 8;
-
- return [8, 8];
- }
- }
-
- function isMutationNewMove(mutationArr) {
- const isNewMove = getSiteData('isMutationNewMove', { mutationArr });
-
- return isNewMove || false;
- }
-
- function getBoardMatrix() {
- const [boardRanks, boardFiles] = getBoardDimensions();
-
- const board = Array.from({ length: boardFiles }, () => Array(boardRanks).fill(1));
- const pieceElems = getPieceElem(true);
- const isValidPieceElemsArray = Array.isArray(pieceElems) || pieceElems instanceof NodeList;
-
- if(isValidPieceElemsArray) {
- pieceElems.forEach(pieceElem => {
- const pieceFenCode = getPieceElemFen(pieceElem);
- const pieceCoordsArr = getPieceElemCoords(pieceElem);
-
- //if(debugModeActivated) console.warn('pieceElem', pieceElem, 'pieceFenCode', pieceFenCode, 'pieceCoordsArr', pieceCoordsArr);
-
- try {
- const [xIdx, yIdx] = pieceCoordsArr;
-
- board[boardFiles - (yIdx + 1)][xIdx] = pieceFenCode;
- } catch(e) {
- if(debugModeActivated) console.error(e);
- }
- });
- }
-
- lastBoardMatrix = board;
-
- return board;
- }
-
- function getBoardPiece(fenCoord) {
- const [boardRanks, boardFiles] = getBoardDimensions();
- const indexArr = chessCoordinatesToIndex(fenCoord);
-
- return getBoardMatrix()?.[boardFiles - (indexArr[1] + 1)]?.[indexArr[0]];
- }
-
- // Works on 8x8 boards only
- function getRights() {
- let rights = '';
-
- // check for white
- const e1 = getBoardPiece('e1'),
- h1 = getBoardPiece('h1'),
- a1 = getBoardPiece('a1');
-
- if(e1 == 'K' && h1 == 'R') rights += 'K';
- if(e1 == 'K' && a1 == 'R') rights += 'Q';
-
- //check for black
- const e8 = getBoardPiece('e8'),
- h8 = getBoardPiece('h8'),
- a8 = getBoardPiece('a8');
-
- if(e8 == 'k' && h8 == 'r') rights += 'k';
- if(e8 == 'k' && a8 == 'r') rights += 'q';
-
- return rights ? rights : '-';
- }
-
- function getBasicFen() {
- const boardMatrix = getBoardMatrix();
-
- return squeezeEmptySquares(boardMatrix.map(x => x.join('')).join('/'));
- }
-
- function getFen(onlyBasic) {
- const basicFen = getBasicFen();
-
- if(debugModeActivated) console.warn('basicFen', basicFen);
-
- if(onlyBasic) {
- return basicFen;
- }
-
- // FEN structure: [fen] [player color] [castling rights] [en passant targets] [halfmove clock] [fullmove clock]
- const fullFen = `${basicFen} ${getPlayerColorVariable()} ${getRights()} - 0 1`;
-
- return fullFen;
- }
-
- function resetCachedValues() {
- chesscomVariantPlayerColorsTable = null;
- }
-
- function fenToArray(fen) {
- const rows = fen.split('/');
- const board = [];
-
- for (let row of rows) {
- const boardRow = [];
- for (let char of row) {
- if (isNaN(char)) {
- boardRow.push(char);
- } else {
- boardRow.push(...Array(parseInt(char)).fill(''));
- }
- }
- board.push(boardRow);
- }
-
- return board;
- }
-
- function onNewMove(mutationArr, bypassFenChangeDetection) {
- const currentFullFen = getFen();
- const lastFullFen = instanceVars.fen.get(commLinkInstanceID);
-
- const fenChanged = currentFullFen !== lastFullFen;
-
- if((fenChanged || bypassFenChangeDetection)) {
- if(debugModeActivated) console.warn('NEW MOVE DETECTED!');
-
- const pieceAmount = getPieceAmount();
- const pieceAmountChange = Math.abs(pieceAmount - lastPieceAmount);
-
- // Possibly new match due to large piece amount change
- if(pieceAmountChange > 7) {
- matchFirstSuggestionGiven = false;
- }
-
- resetCachedValues();
-
- boardUtils.setBoardDimensions(getBoardDimensions());
-
- const lastPlayerColor = getPlayerColorVariable();
-
- updatePlayerColor();
-
- const playerColor = getPlayerColorVariable();
- const orientationChanged = playerColor != lastPlayerColor;
-
- if(orientationChanged) {
- CommLink.commands.log(`Player color (e.g. board orientation) changed from ${lastPlayerColor} to ${playerColor}!`);
-
- resetCachedValues();
-
- matchFirstSuggestionGiven = false;
-
- CommLink.commands.log(`Turn updated to ${playerColor}!`);
- }
-
- boardUtils.removeMarkings();
-
- CommLink.commands.updateBoardFen(currentFullFen);
-
- lastMoveRequestTime = Date.now();
- lastPieceAmount = pieceAmount;
-
- if(orientationChanged) {
- CommLink.commands.calculateBestMoves(currentFullFen);
- }
- }
- }
-
- function observeNewMoves() {
- const boardObserver = new MutationObserver(mutationArr => {
- if(debugModeActivated) console.log(mutationArr);
-
- if(isMutationNewMove(mutationArr))
- {
- if(debugModeActivated) console.warn('Mutation is a new move:', mutationArr);
-
- try {
- if(domain === 'chess.org' || domain === 'chessarena.com')
- {
- setTimeout(() => onNewMove(mutationArr), 250);
- }
- else
- {
- onNewMove(mutationArr);
- }
- } catch(e) {
- if(debugModeActivated) console.error('Error while running onNewMove:', e);
- }
- }
- });
-
- boardObserver.observe(chessBoardElem, { childList: true, subtree: true, attributes: true });
- }
-
- async function updatePlayerColor() {
- const boardOrientation = getBoardOrientation();
-
- const boardOrientationChanged = lastBoardOrientation !== boardOrientation;
- const boardOrientationDiffers = BoardDrawer && BoardDrawer?.orientation !== boardOrientation;
-
- if(boardOrientationChanged || boardOrientationDiffers) {
- lastBoardOrientation = boardOrientation;
-
- instanceVars.playerColor.set(commLinkInstanceID, boardOrientation);
-
- boardUtils.setBoardOrientation(boardOrientation);
-
- await CommLink.commands.updateBoardOrientation(boardOrientation);
- }
- }
-
-
-
-
-
-
- /*
- _______ _____ _______ _______ _______ _____ _______ _______ _____ _______ _____ _______
- |______ | | |______ |______ |_____] |______ | | |______ | |
- ______| __|__ | |______ ______| | |______ |_____ __|__ | __|__ |_____
-
- Code below this point handles chess site specific things. (e.g. which element is the board or the pieces)
- */
-
- addSupportedChessSite('chess.com', {
- 'boardElem': obj => {
- const pathname = obj.pathname;
-
- if(pathname?.includes('/variants')) {
- return document.querySelector('.TheBoard-layers');
- }
-
- return document.querySelector('#board-layout-chessboard > .board');
- },
-
- 'pieceElem': obj => {
- const pathname = obj.pathname;
- const getAll = obj.getAll;
-
- if(pathname?.includes('/variants')) {
- const filteredPieceElems = filterInvisibleElems(document.querySelectorAll('.TheBoard-layers *[data-piece]'))
- .filter(elem => elem?.dataset?.piece?.toLowerCase() !== 'x');
-
- return getAll ? filteredPieceElems : filteredPieceElems[0];
- }
-
- return obj.boardQuerySelector('.piece');
- },
-
- 'squareElems': obj => {
- const pathname = obj.pathname;
- const element = obj.element;
-
- if(pathname?.includes('/variants')) {
- return [...element.querySelectorAll('.square')];
- }
- },
-
- 'chessVariant': obj => {
- const pathname = obj.pathname;
-
- if(pathname?.includes('/variants')) {
- const variant = pathname.match(/variants\/([^\/]*)/)?.[1]
- .replaceAll('-chess', '')
- .replaceAll('-', '');
-
- const replacementTable = {
- 'doubles-bughouse': 'bughouse',
- 'paradigm-chess30': 'paradigm'
- };
-
- return replacementTable[variant] || variant;
- }
- },
-
- 'boardOrientation': obj => {
- const pathname = obj.pathname;
-
- if(pathname?.includes('/variants')) {
- const playerNumberStr = document.querySelector('.playerbox-bottom [data-player]')?.dataset?.player;
-
- if(!playerNumberStr)
- return 'w';
-
- return playerNumberStr === '0' ? 'w' : 'b';
- }
-
- const boardElem = getBoardElem();
-
- return boardElem?.classList.contains('flipped') ? 'b' : 'w';
- },
-
- 'pieceElemFen': obj => {
- const pathname = obj.pathname;
- const pieceElem = obj.pieceElem;
-
- let pieceColor = null;
- let pieceName = null;
-
- if(pathname?.includes('/variants')) {
- if(!chesscomVariantPlayerColorsTable) {
- updateChesscomVariantPlayerColorsTable();
- }
-
- const pieceFenStr = pieceElem?.dataset?.piece;
-
- pieceColor = chesscomVariantPlayerColorsTable?.[pieceElem?.dataset?.color];
- pieceName = pieceElem?.dataset?.piece;
-
- if(pieceName?.length > 1) {
- pieceName = pieceName[0];
- }
- } else {
- const pieceStr = [...pieceElem.classList].find(x => x.match(/^(b|w)[prnbqk]{1}$/));
-
- [pieceColor, pieceName] = pieceStr.split('');
- }
-
- return pieceColor == 'w' ? pieceName.toUpperCase() : pieceName.toLowerCase();
- },
-
- 'pieceElemCoords': obj => {
- const pathname = obj.pathname;
- const pieceElem = obj.pieceElem;
-
- if(pathname?.includes('/variants')) {
- const coords = getElemCoordinatesFromTransform(pieceElem);
-
- return coords;
- }
-
- return pieceElem.classList.toString()
- ?.match(/square-(\d)(\d)/)
- ?.slice(1)
- ?.map(x => Number(x) - 1);
- },
-
- 'boardDimensions': obj => {
- const pathname = obj.pathname;
-
- if(pathname?.includes('/variants')) {
- const squaresContainerElem = document.querySelector('.TheBoard-squares');
-
- let ranks = 0;
- let files = 0;
-
- [...squaresContainerElem.childNodes].forEach((x, i) => {
- const visibleChildElems = filterInvisibleElems([...x.childNodes]);
-
- if(visibleChildElems?.length > 0) {
- ranks = ranks + 1;
-
- if(visibleChildElems.length > files) {
- files = visibleChildElems.length;
- }
- }
- });
-
- return [ranks, files];
- } else {
- return [8, 8];
- }
- },
-
- 'isMutationNewMove': obj => {
- const pathname = obj.pathname;
- const mutationArr = obj.mutationArr;
-
- if(pathname?.includes('/variants')) {
- return mutationArr.find(m => m.type === 'childList') ? true : false;
- }
-
- if(mutationArr.length == 1)
- return false;
-
- const modifiedHoverSquare = mutationArr.find(m => m?.target?.classList?.contains('hover-square')) ? true : false;
- const modifiedHighlight = mutationArr.find(m => m?.target?.classList?.contains('highlight')) ? true : false;
- const modifiedElemPool = mutationArr.find(m => m?.target?.classList?.contains('element-pool')) ? true : false;
-
- const isPremove = mutationArr.filter(m => m?.target?.classList?.contains('highlight'))
- .map(x => x?.target?.style?.['background-color'])
- .find(x => x === 'rgb(244, 42, 50)') ? true : false;
-
- return (
- (mutationArr.length >= 4 && !modifiedHoverSquare)
- || mutationArr.length >= 7
- || modifiedHighlight
- || modifiedElemPool
- ) && !isPremove;
- }
- });
-
- addSupportedChessSite('lichess.org', {
- 'boardElem': obj => {
- return document.querySelector('cg-board');
- },
-
- 'pieceElem': obj => {
- return obj.boardQuerySelector('piece:not(.ghost)');
- },
-
- 'chessVariant': obj => {
- const variantLinkElem = document.querySelector('.variant-link');
-
- if(variantLinkElem) {
- let variant = variantLinkElem?.innerText?.toLowerCase()?.replaceAll(' ', '-');
-
- const replacementTable = {
- 'correspondence': 'chess',
- 'koth': 'kingofthehill',
- 'three-check': '3check'
- };
-
- return replacementTable[variant] || variant;
- }
- },
-
- 'boardOrientation': obj => {
- const filesElem = document.querySelector('coords.files');
-
- return filesElem?.classList?.contains('black') ? 'b' : 'w';
- },
-
- 'pieceElemFen': obj => {
- const pieceElem = obj.pieceElem;
-
- const pieceColor = pieceElem?.classList?.contains('white') ? 'w' : 'b';
- const elemPieceName = [...pieceElem?.classList]?.find(className => Object.keys(pieceNameToFen).includes(className));
-
- if(pieceColor && elemPieceName) {
- const pieceName = pieceNameToFen[elemPieceName];
-
- return pieceColor == 'w' ? pieceName.toUpperCase() : pieceName.toLowerCase();
- }
- },
-
- 'pieceElemCoords': obj => {
- const pieceElem = obj.pieceElem;
-
- const key = pieceElem?.cgKey;
-
- if(key) {
- return chessCoordinatesToIndex(key);
- }
- },
-
- 'boardDimensions': obj => {
- return [8, 8];
- },
-
- 'isMutationNewMove': obj => {
- const mutationArr = obj.mutationArr;
-
- return mutationArr.length >= 4
- || mutationArr.find(m => m.type === 'childList') ? true : false
- || mutationArr.find(m => m?.target?.classList?.contains('last-move')) ? true : false;
- }
- });
-
- addSupportedChessSite('playstrategy.org', {
- 'boardElem': obj => {
- return document.querySelector('cg-board');
- },
-
- 'pieceElem': obj => {
- return obj.boardQuerySelector('piece[class*="-piece"]:not(.ghost)');
- },
-
- 'chessVariant': obj => {
- const variantLinkElem = document.querySelector('.variant-link');
-
- if(variantLinkElem) {
- let variant = variantLinkElem?.innerText
- ?.toLowerCase()
- ?.replaceAll(' ', '-');
-
- const replacementTable = {
- 'correspondence': 'chess',
- 'koth': 'kingofthehill',
- 'three-check': '3check',
- 'five-check': '5check',
- 'no-castling': 'nocastle'
- };
-
- return replacementTable[variant] || variant;
- }
- },
-
- 'boardOrientation': obj => {
- const cgWrapElem = document.querySelector('.cg-wrap');
-
- return cgWrapElem.classList?.contains('orientation-p1') ? 'w' : 'b';
- },
-
- 'pieceElemFen': obj => {
- const pieceElem = obj.pieceElem;
-
- const playerColor = getPlayerColorVariable();
- const pieceColor = pieceElem?.classList?.contains('ally') ? playerColor : (playerColor == 'w' ? 'b' : 'w');
-
- let pieceName = null;
-
- [...pieceElem?.classList]?.forEach(className => {
- if(className?.includes('-piece')) {
- const elemPieceName = className?.split('-piece')?.[0];
-
- if(elemPieceName && elemPieceName?.length === 1) {
- pieceName = elemPieceName;
- }
- }
- });
-
- if(pieceColor && pieceName) {
- return pieceColor == 'w' ? pieceName.toUpperCase() : pieceName.toLowerCase();
- }
- },
-
- 'pieceElemCoords': obj => {
- const pieceElem = obj.pieceElem;
-
- const key = pieceElem?.cgKey;
-
- if(key) {
- return chessCoordinatesToIndex(key);
- }
- },
-
- 'boardDimensions': obj => {
- return getBoardDimensionsFromSize();
- },
-
- 'isMutationNewMove': obj => {
- const mutationArr = obj.mutationArr;
-
- return mutationArr.length >= 4
- || mutationArr.find(m => m.type === 'childList') ? true : false
- || mutationArr.find(m => m?.target?.classList?.contains('last-move')) ? true : false;
- }
- });
-
- addSupportedChessSite('pychess.org', {
- 'boardElem': obj => {
- return document.querySelector('cg-board');
- },
-
- 'pieceElem': obj => {
- return obj.boardQuerySelector('piece[class*="-piece"]:not(.ghost)');
- },
-
- 'chessVariant': obj => {
- const variantLinkElem = document.querySelector('#main-wrap .tc .user-link');
-
- if(variantLinkElem) {
- let variant = variantLinkElem?.innerText
- ?.toLowerCase()
- ?.replaceAll(' ', '')
- ?.replaceAll('-', '');
-
- const replacementTable = {
- 'correspondence': 'chess',
- 'koth': 'kingofthehill',
- 'nocastling': 'nocastle',
- 'gorogoro+': 'gorogoro',
- 'oukchaktrang': 'cambodian'
- };
-
- return replacementTable[variant] || variant;
- }
- },
-
- 'boardOrientation': obj => {
- const cgWrapElem = document.querySelector('.cg-wrap');
-
- return cgWrapElem.classList?.contains('orientation-black') ? 'b' : 'w';
- },
-
- 'pieceElemFen': obj => {
- const pieceElem = obj.pieceElem;
-
- const playerColor = getPlayerColorVariable();
- const pieceColor = pieceElem?.classList?.contains('ally') ? playerColor : (playerColor == 'w' ? 'b' : 'w');
-
- let pieceName = null;
-
- [...pieceElem?.classList]?.forEach(className => {
- if(className?.includes('-piece')) {
- const elemPieceName = className?.split('-piece')?.[0];
-
- if(elemPieceName && elemPieceName?.length === 1) {
- pieceName = elemPieceName;
- }
- }
- });
-
- if(pieceColor && pieceName) {
- return pieceColor == 'w' ? pieceName.toUpperCase() : pieceName.toLowerCase();
- }
- },
-
- 'pieceElemCoords': obj => {
- const pieceElem = obj.pieceElem;
-
- const key = pieceElem?.cgKey;
-
- if(key) {
- return chessCoordinatesToIndex(key);
- }
- },
-
- 'boardDimensions': obj => {
- return getBoardDimensionsFromSize();
- },
-
- 'isMutationNewMove': obj => {
- const mutationArr = obj.mutationArr;
-
- return mutationArr.length >= 4
- || mutationArr.find(m => m.type === 'childList') ? true : false
- || mutationArr.find(m => m?.target?.classList?.contains('last-move')) ? true : false;
- }
- });
-
- addSupportedChessSite('chess.org', {
- 'boardElem': obj => {
- return document.querySelector('.cg-board');
- },
-
- 'pieceElem': obj => {
- return obj.boardQuerySelector('piece:not(.ghost)');
- },
-
- 'chessVariant': obj => {
- return 'chess';
- },
-
- 'boardOrientation': obj => {
- const filesElem = document.querySelector('coords.files');
-
- return filesElem?.classList?.contains('black') ? 'b' : 'w';
- },
-
- 'pieceElemFen': obj => {
- const pieceElem = obj.pieceElem;
-
- const pieceColor = pieceElem?.classList?.contains('white') ? 'w' : 'b';
- const elemPieceName = [...pieceElem?.classList]?.find(className => Object.keys(pieceNameToFen).includes(className));
-
- if(pieceColor && elemPieceName) {
- const pieceName = pieceNameToFen[elemPieceName];
-
- return pieceColor == 'w' ? pieceName.toUpperCase() : pieceName.toLowerCase();
- }
- },
-
- 'pieceElemCoords': obj => {
- const pieceElem = obj.pieceElem;
-
- return getElemCoordinatesFromTransform(pieceElem);
- },
-
- 'boardDimensions': obj => {
- return [8, 8];
- },
-
- 'isMutationNewMove': obj => {
- const mutationArr = obj.mutationArr;
-
- if(isUserMouseDown) {
- return false;
- }
-
- return mutationArr.length >= 4
- || mutationArr.find(m => m.type === 'childList') ? true : false
- || mutationArr.find(m => m?.target?.classList?.contains('last-move')) ? true : false;
- }
- });
-
- addSupportedChessSite('chess.coolmathgames.com', {
- 'boardElem': obj => {
- return document.querySelector('cg-board');
- },
-
- 'pieceElem': obj => {
- return obj.boardQuerySelector('piece:not(.ghost)');
- },
-
- 'chessVariant': obj => {
- return 'chess';
- },
-
- 'boardOrientation': obj => {
- const boardElem = getBoardElem();
-
- return document.querySelector('.ranks.black') ? 'b' : 'w';
- },
-
- 'pieceElemFen': obj => {
- const pieceElem = obj.pieceElem;
-
- const pieceColor = pieceElem?.classList?.contains('white') ? 'w' : 'b';
- const elemPieceName = [...pieceElem?.classList]?.find(className => Object.keys(pieceNameToFen).includes(className));
-
- if(pieceColor && elemPieceName) {
- const pieceName = pieceNameToFen[elemPieceName];
-
- return pieceColor == 'w' ? pieceName.toUpperCase() : pieceName.toLowerCase();
- }
- },
-
- 'pieceElemCoords': obj => {
- const pieceElem = obj.pieceElem;
-
- const key = pieceElem?.cgKey;
-
- if(key) {
- return chessCoordinatesToIndex(key);
- }
- },
-
- 'boardDimensions': obj => {
- return [8, 8];
- },
-
- 'isMutationNewMove': obj => {
- const mutationArr = obj.mutationArr;
-
- if(isUserMouseDown) {
- return false;
- }
-
- return mutationArr.length >= 4
- || mutationArr.find(m => m.type === 'childList') ? true : false
- || mutationArr.find(m => m?.target?.classList?.contains('last-move')) ? true : false;
- }
- });
-
- addSupportedChessSite('papergames.io', {
- 'boardElem': obj => {
- return document.querySelector('.cm-chessboard');
- },
-
- 'pieceElem': obj => {
- return obj.boardQuerySelector('*[data-piece][data-square]');
- },
-
- 'chessVariant': obj => {
- return 'chess';
- },
-
- 'boardOrientation': obj => {
- const boardElem = getBoardElem();
-
- if(boardElem) {
- const firstRankText = [...boardElem.querySelector('.coordinates').childNodes]?.[0].textContent;
-
- return firstRankText == 'h' ? 'b' : 'w';
- }
- },
-
- 'pieceElemFen': obj => {
- const pieceElem = obj.pieceElem;
-
- return convertPieceStrToFen(pieceElem?.dataset?.piece);
- },
-
- 'pieceElemCoords': obj => {
- const pieceElem = obj.pieceElem;
-
- const key = pieceElem?.dataset?.square;
-
- if(key) {
- return chessCoordinatesToIndex(key);
- }
- },
-
- 'boardDimensions': obj => {
- return [8, 8];
- },
-
- 'isMutationNewMove': obj => {
- const mutationArr = obj.mutationArr;
-
- return mutationArr.length >= 12;
- }
- });
-
- addSupportedChessSite('vole.wtf', {
- 'boardElem': obj => {
- return document.querySelector('#board');
- },
-
- 'pieceElem': obj => {
- return obj.boardQuerySelector('*[data-t][data-l][data-p]:not([data-p="0"]');
- },
-
- 'chessVariant': obj => {
- return 'chess';
- },
-
- 'boardOrientation': obj => {
- return 'w';
- },
-
- 'pieceElemFen': obj => {
- const pieceElem = obj.pieceElem;
-
- const pieceNum = Number(pieceElem?.dataset?.p);
- const pieceFenStr = 'pknbrq';
-
- if(pieceNum > 8) {
- return pieceFenStr[pieceNum - 9].toUpperCase();
- } else {
- return pieceFenStr[pieceNum - 1];
- }
- },
-
- 'pieceElemCoords': obj => {
- const pieceElem = obj.pieceElem;
-
- return [Number(pieceElem?.dataset?.l), 7 - Number(pieceElem?.dataset?.t)];
- },
-
- 'boardDimensions': obj => {
- return [8, 8];
- },
-
- 'isMutationNewMove': obj => {
- const mutationArr = obj.mutationArr;
-
- return mutationArr.length >= 12;
- }
- });
-
- addSupportedChessSite('immortal.game', {
- 'boardElem': obj => {
- return document.querySelector('div.pawn.relative, div.knight.relative, div.bishop.relative, div.rook.relative, div.queen.relative, div.king.relative')?.parentElement?.parentElement;
- },
-
- 'pieceElem': obj => {
- return obj.boardQuerySelector('div.pawn.relative, div.knight.relative, div.bishop.relative, div.rook.relative, div.queen.relative, div.king.relative');
- },
-
- 'chessVariant': obj => {
- return 'chess';
- },
-
- 'boardOrientation': obj => {
- const coordA = [...document.querySelectorAll('svg text[x]')]
- .find(elem => elem?.textContent == 'a');
-
- const coordAX = Number(coordA?.getAttribute('x')) || 10;
-
- return coordAX < 15 ? 'w' : 'b';
- },
-
- 'pieceElemFen': obj => {
- const pieceElem = obj.pieceElem;
-
- const pieceColor = pieceElem?.classList?.contains('white') ? 'w' : 'b';
- const elemPieceName = [...pieceElem?.classList]?.find(className => Object.keys(pieceNameToFen).includes(className));
-
- if(pieceColor && elemPieceName) {
- const pieceName = pieceNameToFen[elemPieceName];
-
- return pieceColor === 'w' ? pieceName.toUpperCase() : pieceName.toLowerCase();
- }
- },
-
- 'pieceElemCoords': obj => {
- const pieceElem = obj.pieceElem;
-
- return getElemCoordinatesFromTransform(pieceElem?.parentElement);
- },
-
- 'boardDimensions': obj => {
- return [8, 8];
- },
-
- 'isMutationNewMove': obj => {
- const mutationArr = obj.mutationArr;
-
- if(isUserMouseDown) {
- return false;
- }
-
- return mutationArr.length >= 5;
- }
- });
-
- addSupportedChessSite('chessarena.com', {
- 'boardElem': obj => {
- return document.querySelector('*[data-component="GameLayoutBoard"] cg-board');
- },
-
- 'pieceElem': obj => {
- return obj.boardQuerySelector('cg-piece:not(*[style*="visibility: hidden;"])');
- },
-
- 'chessVariant': obj => {
- return 'chess';
- },
-
- 'boardOrientation': obj => {
- const titlesElem = document.querySelector('cg-titles');
-
- return titlesElem?.classList?.contains('rotated') ? 'b' : 'w';
- },
-
- 'pieceElemFen': obj => {
- const pieceElem = obj.pieceElem;
-
- const pieceColor = pieceElem?.className?.[0];
- const elemPieceName = pieceElem?.className?.[1];
-
- if(pieceColor && elemPieceName) {
- const pieceName = elemPieceName; // pieceNameToFen[elemPieceName]
-
- return pieceColor === 'w' ? pieceName.toUpperCase() : pieceName.toLowerCase();
- }
- },
-
- 'pieceElemCoords': obj => {
- const pieceElem = obj.pieceElem;
-
- return getElemCoordinatesFromTransform(pieceElem, { 'onlyFlipY': true });
- },
-
- 'boardDimensions': obj => {
- return [8, 8];
- },
-
- 'isMutationNewMove': obj => {
- const mutationArr = obj.mutationArr;
-
- if(isUserMouseDown) {
- return false;
- }
-
- return mutationArr.find(m => m?.attributeName === 'style') ? true : false;
- }
- });
-
-
- addSupportedChessSite('chess.net', {
- 'boardElem': obj => {
- return document.querySelector('cg-board');
- },
-
- 'pieceElem': obj => {
- return obj.boardQuerySelector('piece:not(.ghost)');
- },
-
- 'chessVariant': obj => {
- const variantLinkElem = document.querySelector('.variant-link');
-
- if(variantLinkElem) {
- let variant = variantLinkElem?.innerText?.toLowerCase()?.replaceAll(' ', '-');
-
- const replacementTable = {
- 'correspondence': 'chess',
- 'koth': 'kingofthehill',
- 'three-check': '3check'
- };
-
- return replacementTable[variant] || variant;
- }
- },
-
- 'boardOrientation': obj => {
- const filesElem = document.querySelector('coords.files');
-
- return filesElem?.classList?.contains('black') ? 'b' : 'w';
- },
-
- 'pieceElemFen': obj => {
- const pieceElem = obj.pieceElem;
-
- const pieceColor = pieceElem?.classList?.contains('white') ? 'w' : 'b';
- const elemPieceName = [...pieceElem?.classList]?.find(className => Object.keys(pieceNameToFen).includes(className));
-
- if(pieceColor && elemPieceName) {
- const pieceName = pieceNameToFen[elemPieceName];
-
- return pieceColor == 'w' ? pieceName.toUpperCase() : pieceName.toLowerCase();
- }
- },
-
- 'pieceElemCoords': obj => {
- const pieceElem = obj.pieceElem;
-
- const key = pieceElem?.cgKey;
-
- if(key) {
- return chessCoordinatesToIndex(key);
- }
- },
-
- 'boardDimensions': obj => {
- return [8, 8];
- },
-
- 'isMutationNewMove': obj => {
- const mutationArr = obj.mutationArr;
-
- return mutationArr.length >= 4
- || mutationArr.find(m => m.type === 'childList') ? true : false
- || mutationArr.find(m => m?.target?.classList?.contains('last-move')) ? true : false;
- }
- });
-
- addSupportedChessSite('freechess.club', {
- 'boardElem': obj => {
- return document.querySelector('cg-board');
- },
-
- 'pieceElem': obj => {
- return obj.boardQuerySelector('piece:not(.ghost)');
- },
-
- 'chessVariant': obj => {
- return 'chess';
- },
-
- 'boardOrientation': obj => {
- const filesElem = document.querySelector('coords.files');
-
- return filesElem?.classList?.contains('black') ? 'b' : 'w';
- },
-
- 'pieceElemFen': obj => {
- const pieceElem = obj.pieceElem;
-
- const pieceColor = pieceElem?.classList?.contains('white') ? 'w' : 'b';
- const elemPieceName = [...pieceElem?.classList]?.find(className => Object.keys(pieceNameToFen).includes(className));
-
- if(pieceColor && elemPieceName) {
- const pieceName = pieceNameToFen[elemPieceName];
-
- return pieceColor == 'w' ? pieceName.toUpperCase() : pieceName.toLowerCase();
- }
- },
-
- 'pieceElemCoords': obj => {
- const pieceElem = obj.pieceElem;
-
- const key = pieceElem?.cgKey;
-
- if(key) {
- return chessCoordinatesToIndex(key);
- }
- },
-
- 'boardDimensions': obj => {
- return [8, 8];
- },
-
- 'isMutationNewMove': obj => {
- const mutationArr = obj.mutationArr;
-
- return mutationArr.length >= 4
- || mutationArr.find(m => m.type === 'childList') ? true : false
- || mutationArr.find(m => m?.target?.classList?.contains('last-move')) ? true : false;
- }
- });
-
- addSupportedChessSite('play.chessclub.com', {
- 'boardElem': obj => {
- return document.querySelector('cg-board');
- },
-
- 'pieceElem': obj => {
- return obj.boardQuerySelector('piece:not(.ghost)');
- },
-
- 'chessVariant': obj => {
- return 'chess';
- },
-
- 'boardOrientation': obj => {
- const filesElem = document.querySelector('coords.files');
-
- return filesElem?.classList?.contains('black') ? 'b' : 'w';
- },
-
- 'pieceElemFen': obj => {
- const pieceElem = obj.pieceElem;
-
- const pieceColor = pieceElem?.classList?.contains('white') ? 'w' : 'b';
- const elemPieceName = [...pieceElem?.classList]?.find(className => Object.keys(pieceNameToFen).includes(className));
-
- if(pieceColor && elemPieceName) {
- const pieceName = pieceNameToFen[elemPieceName];
-
- return pieceColor == 'w' ? pieceName.toUpperCase() : pieceName.toLowerCase();
- }
- },
-
- 'pieceElemCoords': obj => {
- const pieceElem = obj.pieceElem;
-
- const key = pieceElem?.cgKey;
-
- if(key) {
- return chessCoordinatesToIndex(key);
- }
- },
-
- 'boardDimensions': obj => {
- return [8, 8];
- },
-
- 'isMutationNewMove': obj => {
- const mutationArr = obj.mutationArr;
-
- return mutationArr.length >= 4
- || mutationArr.find(m => m.type === 'childList') ? true : false
- || mutationArr.find(m => m?.target?.classList?.contains('last-move')) ? true : false;
- }
- });
-
- addSupportedChessSite('gameknot.com', {
- 'boardElem': obj => {
- return document.querySelector('#chess-board-acboard');
- },
-
- 'pieceElem': obj => {
- return obj.boardQuerySelector('*[class*="chess-board-piece"] > img[src*="chess56."][style*="visible"]');
- },
-
- 'chessVariant': obj => {
- return 'chess';
- },
-
- 'boardOrientation': obj => {
- return document.querySelector('#chess-board-my-side-color .player_white') ? 'w' : 'b';
- },
-
- 'pieceElemFen': obj => {
- const pieceElem = obj.pieceElem;
-
- const left = Number(pieceElem.style.left.replace('px', ''));
- const top = Number(pieceElem.style.top.replace('px', ''));
-
- const pieceColor = left >= 0 ? 'w' : 'b';
- const pieceName = 'kqrnbp'[(top * -1) / 60];
-
- return pieceColor === 'w' ? pieceName.toUpperCase() : pieceName.toLowerCase();
- },
-
- 'pieceElemCoords': obj => {
- const pieceElem = obj.pieceElem;
-
- return getElemCoordinatesFromLeftTopPixels(pieceElem.parentElement);
- },
-
- 'boardDimensions': obj => {
- return [8, 8];
- },
-
- 'isMutationNewMove': obj => {
- const mutationArr = obj.mutationArr;
-
- return mutationArr.find(m => m.type === 'childList') ? true : false
- || mutationArr.find(m => m?.target?.classList?.contains('last-move')) ? true : false;
- }
- });
-
- addSupportedChessSite('chesstempo.com', {
- 'boardElem': obj => {
- return document.querySelector('.ct-board-squares');
- },
-
- 'pieceElem': obj => {
- return obj.boardQuerySelector('*[class*="ct-pieceClass"][class*="ct-piece-"]');
- },
-
- 'chessVariant': obj => {
- return 'chess';
- },
-
- 'boardOrientation': obj => {
- return document.querySelector('.ct-coord-column').innerText === 'a' ? 'w' : 'b';
- },
-
- 'pieceElemFen': obj => {
- const pieceElem = obj.pieceElem;
-
- const pieceNameClass = [...pieceElem.classList].find(x => x?.includes('ct-piece-'));
- const colorNameCombo = pieceNameClass?.split('ct-piece-')?.pop();
-
- const elemPieceColor = colorNameCombo.startsWith('white') ? 'w' : 'b';
- const elemPieceName = colorNameCombo.substring(5);
-
- const pieceName = pieceNameToFen[elemPieceName];
-
- return elemPieceColor === 'w' ? pieceName.toUpperCase() : pieceName.toLowerCase();
- },
-
- 'pieceElemCoords': obj => {
- const pieceElem = obj.pieceElem;
-
- return [pieceElem?.ct?.piece?.piece?.column, pieceElem?.ct?.piece?.piece?.row];
- },
-
- 'boardDimensions': obj => {
- return [8, 8];
- },
-
- 'isMutationNewMove': obj => {
- const mutationArr = obj.mutationArr;
-
- return mutationArr.length >= 4;
- }
- });
-
- addSupportedChessSite('redhotpawn.com', {
- 'boardElem': obj => {
- return document.querySelector('#board-0_1');
- },
-
- 'pieceElem': obj => {
- return obj.boardQuerySelector('li.piece[id*="-pc-"]');
- },
-
- 'chessVariant': obj => {
- return 'chess';
- },
-
- 'boardOrientation': obj => {
- const aCoordLeftStyleNum = Number([...document.querySelectorAll('.boardCoordinate')]
- .find(elem => elem?.innerText === 'a')
- ?.style?.left?.replace('px', ''));
-
- return aCoordLeftStyleNum < 200 ? 'w' : 'b';
- },
-
- 'pieceElemFen': obj => {
- const pieceElem = obj.pieceElem;
-
- return (pieceElem?.id?.match(/-pc-(.*?)-/) || [])[1];
- },
-
- 'pieceElemCoords': obj => {
- const pieceElem = obj.pieceElem;
-
- return getElemCoordinatesFromLeftTopPixels(pieceElem);
- },
-
- 'boardDimensions': obj => {
- return [8, 8];
- },
-
- 'isMutationNewMove': obj => {
- const mutationArr = obj.mutationArr;
-
- if(isUserMouseDown) {
- return false;
- }
-
- return mutationArr.length >= 4;
- }
- });
-
- addSupportedChessSite('simplechess.com', {
- 'boardElem': obj => {
- return document.querySelector('#chessboard');
- },
-
- 'pieceElem': obj => {
- const getAll = obj.getAll;
-
- const pieceElems = [...document.querySelectorAll('canvas.canvas_piece')].filter(elem => canvasHasPixelAt(elem, [0.5, 0.5]));
-
- return getAll ? pieceElems : pieceElems[0];
- },
-
- 'chessVariant': obj => {
- return 'chess';
- },
-
- 'boardOrientation': obj => {
- return document.querySelector('#chessboard_coordy0')?.innerText === '8' ? 'w' : 'b';
- },
-
- 'pieceElemFen': obj => {
- const pieceElem = obj.pieceElem;
-
- const pieceTypeCoordPercentages = [
- { 'name' : 'k', 'coords': [52/60, 26/60] },
- { 'name' : 'q', 'coords': [8/60, 16/60] },
- { 'name' : 'n', 'coords': [51/60, 42/60] },
- { 'name' : 'b', 'coords': [9/60, 50/60] },
- { 'name' : 'r', 'coords': [45/60, 15/60] },
- { 'name' : 'p', 'coords': [0.5, 0.5] }
- ];
-
- const pieceColorCoordPercentages = {
- 'k': [42/60, 27/60],
- 'q': [30/60, 50/60],
- 'n': [38/60, 41/60],
- 'b': [30/60, 20/60]
- };
-
- let pieceName = null;
-
- for(obj of pieceTypeCoordPercentages) {
- const isThisPiece = canvasHasPixelAt(pieceElem, obj.coords);
-
- if(isThisPiece) {
- pieceName = obj.name;
-
- break;
- }
- }
-
- if(pieceName) {
- const colorCoords = pieceColorCoordPercentages[pieceName] || [0.5, 0.5];
-
- const pieceColor = getCanvasPixelColor(pieceElem, colorCoords);
-
- return pieceColor === 'w' ? pieceName.toUpperCase() : pieceName.toLowerCase();
- }
- },
-
- 'pieceElemCoords': obj => {
- const pieceElem = obj.pieceElem;
-
- return getElemCoordinatesFromLeftTopPixels(pieceElem);
- },
-
- 'boardDimensions': obj => {
- return [8, 8];
- },
-
- 'isMutationNewMove': obj => {
- const mutationArr = obj.mutationArr;
-
- if(isUserMouseDown) {
- return false;
- }
-
- return mutationArr.length >= 7;
- }
- });
-
- addSupportedChessSite('chessworld.net', {
- 'boardElem': obj => {
- return document.querySelector('#ChessWorldChessBoard');
- },
-
- 'pieceElem': obj => {
- return obj.boardQuerySelector('img[src*="merida"');
- },
-
- 'chessVariant': obj => {
- return 'chess';
- },
-
- 'boardOrientation': obj => {
- return document.querySelector('div[style*="boardb.jpg"]') ? 'w' : 'b';
- },
-
- 'pieceElemFen': obj => {
- const pieceElem = obj.pieceElem;
-
- const [elemPieceColor, elemPieceName] = pieceElem
- ?.src
- ?.split('/')
- ?.pop()
- ?.replace('.png', '')
- ?.split('_');
-
- const pieceColor = elemPieceColor === 'white' ? 'w' : 'b';
- const pieceName = pieceNameToFen[elemPieceName];
-
- return pieceColor === 'w' ? pieceName.toUpperCase() : pieceName.toLowerCase();
- },
-
- 'pieceElemCoords': obj => {
- const pieceElem = obj.pieceElem;
-
- return chessCoordinatesToIndex(pieceElem?.id);
- },
-
- 'boardDimensions': obj => {
- return [8, 8];
- },
-
- 'isMutationNewMove': obj => {
- const mutationArr = obj.mutationArr;
-
- return mutationArr.length >= 2;
- }
- });
-
- addSupportedChessSite('app.edchess.io', {
- 'boardElem': obj => {
- return document.querySelector('*[data-boardid="chessboard"]');
- },
-
- 'pieceElem': obj => {
- return obj.boardQuerySelector('*[data-piece]');
- },
-
- 'chessVariant': obj => {
- return 'chess';
- },
-
- 'boardOrientation': obj => {
- return document.querySelector('*[data-square]')?.dataset?.square == 'h1' ? 'b' : 'w';
- },
-
- 'pieceElemFen': obj => {
- const pieceElem = obj.pieceElem;
- const [pieceColor, pieceName] = pieceElem?.dataset?.piece?.split('');
-
- return pieceColor === 'w' ? pieceName.toUpperCase() : pieceName.toLowerCase();
- },
-
- 'pieceElemCoords': obj => {
- const pieceElem = obj.pieceElem;
-
- return chessCoordinatesToIndex(pieceElem?.parentElement?.parentElement?.dataset?.square);
- },
-
- 'boardDimensions': obj => {
- return [8, 8];
- },
-
- 'isMutationNewMove': obj => {
- const mutationArr = obj.mutationArr;
-
- return mutationArr.length >= 2;
- }
- });
-
- /*
- _____ __ _ _____ _______ _____ _______ _____ ______ _______ _______ _____ _____ __ _
- | | \ | | | | |_____| | | ____/ |_____| | | | | | \ |
- __|__ | \_| __|__ | __|__ | | |_____ __|__ /_____ | | | __|__ |_____| | \_|
-
- Code below this point is related to initialization. (e.g. wait for chess board and create the instance)
- */
-
- async function isAcasBackendReady() {
- const res = await CommLink.commands.ping();
-
- return res ? true : false;
- }
-
- async function start() {
- await CommLink.commands.createInstance(commLinkInstanceID);
-
- const pathname = window.location.pathname;
- const adjustSizeByDimensions = domain === 'chess.com' && pathname?.includes('/variants');
- const ignoreBodyRectLeft = domain === 'app.edchess.io';
-
- const boardOrientation = getBoardOrientation();
-
- instanceVars.playerColor.set(commLinkInstanceID, boardOrientation);
- instanceVars.fen.set(commLinkInstanceID, getFen());
-
- if(isBoardDrawerNeeded()) {
- BoardDrawer = new UniversalBoardDrawer(chessBoardElem, {
- 'window': window,
- 'boardDimensions': getBoardDimensions(),
- 'playerColor': getPlayerColorVariable(),
- 'zIndex': domain === 'chessarena.com' ? 9999 : 500,
- 'prepend': true,
- 'debugMode': debugModeActivated,
- 'adjustSizeByDimensions': adjustSizeByDimensions ? true : false,
- 'adjustSizeConfig': {
- 'noLeftAdjustment': true
- },
- 'ignoreBodyRectLeft': ignoreBodyRectLeft
- });
- }
-
- await updatePlayerColor();
-
- observeNewMoves();
-
- CommLink.setIntervalAsync(async () => {
- await CommLink.commands.createInstance(commLinkInstanceID);
- }, 1000);
- }
-
- function startWhenBackendReady() {
- let timesUrlForceOpened = 0;
-
- const interval = CommLink.setIntervalAsync(async () => {
- if(await isAcasBackendReady()) {
- start();
-
- interval.stop();
- } else if(timesUrlForceOpened < 1) {
- timesUrlForceOpened++;
-
- GM_openInTab(getCurrentBackendURL(), true);
- }
- }, 1000);
- }
-
- function initializeIfSiteReady() {
- const boardElem = getBoardElem();
- const firstPieceElem = getPieceElem();
-
- const bothElemsExist = boardElem && firstPieceElem;
- const isChessComImageBoard = domain === 'chess.com' && boardElem?.className.includes('webgl-2d');
- const boardElemChanged = chessBoardElem != boardElem;
-
- if((bothElemsExist || isChessComImageBoard) && boardElemChanged) {
- chessBoardElem = boardElem;
-
- chessBoardElem.addEventListener('mousedown', () => { isUserMouseDown = true; });
- chessBoardElem.addEventListener('mouseup', () => { isUserMouseDown = false; });
- chessBoardElem.addEventListener('touchstart', () => { isUserMouseDown = true; });
- chessBoardElem.addEventListener('touchend', () => { isUserMouseDown = false; });
-
- if(!blacklistedURLs.includes(window.location.href)) {
- startWhenBackendReady();
- }
- }
- }
-
- if(typeof GM_registerMenuCommand === 'function') {
- GM_registerMenuCommand('[u] Open GreasyFork Page', e => {
- GM_openInTab(greasyforkURL, true);
- }, 'u');
-
- GM_registerMenuCommand('[o] Open GUI Manually', e => {
- GM_openInTab(getCurrentBackendURL(), true);
- }, 'o');
-
- GM_registerMenuCommand('[s] Start Manually', e => {
- if(chessBoardElem) {
- start();
- } else {
- displayImportantNotification('Failed to start manually', 'No chessboard element found!');
- }
- }, 's');
-
- GM_registerMenuCommand('[g] Get Moves Manually', e => {
- if(chessBoardElem) {
- onNewMove(null, true);
- } else {
- displayImportantNotification('Failed to get moves', 'No chessboard element found!');
- }
- }, 'g');
-
- GM_registerMenuCommand('[r] Render BoardDrawer Manually', e => {
- if(typeof BoardDrawer?.updateDimensions === 'function') {
- BoardDrawer.updateDimensions();
- } else {
- displayImportantNotification('Failed to render BoardDrawer', 'BoardDrawer not initialized or something else went wrong!');
- }
- }, 'r');
-
- if(typeof GM_setClipboard === 'function') {
- GM_registerMenuCommand('[c] Copy FEN to Clipboard', e => {
- if(chessBoardElem) {
- GM_setClipboard(getFen());
- } else {
- displayImportantNotification('Failed to get FEN', 'No chessboard element found!');
- }
- }, 'c');
- }
- }
-
- setInterval(initializeIfSiteReady, 1000);
-
- })(); // wraps around the whole userscript to enable async