// ==UserScript==
// additional copyright/license info:
//© All Rights Reserved
//
//Chess.com Enhanced Experience © 2024 by Akashdeep
//
// ==UserScript
// @name Chess.com UltraX bot with Panel
// @namespace Akashdeep
// @version 1.0.0.5
// @description Enhances your Chess.com experience with move suggestions, UI enhancements, and customizable features. Credits to GoodtimeswithEno for the initial move highlighting and basic chess engine implementation.
// @author Akashdeep
// @license Chess.com Enhanced Experience © 2024 by Akashdeep, © All Rights Reserved
// @match https://www.chess.com/play/*
// @match https://www.chess.com/game/*
// @match https://www.chess.com/puzzles/*
// @match https://www.chess.com/*
// @icon data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_xmlhttpRequest
// @grant GM_getResourceText
// @grant GM_registerMenuCommand
// @resource stockfish.js https://cdnjs.cloudflare.com/ajax/libs/stockfish.js/10.0.2/stockfish.js
// @require https://greasyfork.org/scripts/445697/code/index.js
// @require https://code.jquery.com/jquery-3.6.0.min.js
// @run-at document-start
// @liscense MIT
// @downloadURL
// @updateURL
// ==/UserScript==
//Don't touch anything below unless you know what your doing!
const currentVersion = '1.0.0.5'; // Sets the current version
function main() {
var stockfishObjectURL;
var engine = document.engine = {};
var myVars = document.myVars = {};
myVars.autoMovePiece = false;
myVars.autoRun = false;
myVars.delay = 0.1;
var myFunctions = document.myFunctions = {};
myVars.playStyle = {
aggressive: Math.random() * 0.5 + 0.3, // 0.3-0.8 tendency for aggressive moves
defensive: Math.random() * 0.5 + 0.3, // 0.3-0.8 tendency for defensive moves
tactical: Math.random() * 0.6 + 0.2, // 0.2-0.8 tendency for tactical moves
positional: Math.random() * 0.6 + 0.2 // 0.2-0.8 tendency for positional moves
};
myVars.preferredOpenings = [
"e4", "d4", "c4", "Nf3" // Just examples, could be expanded
].sort(() => Math.random() - 0.5); // Randomize the order
myVars.playerFingerprint = GM_getValue('playerFingerprint', {
favoredPieces: Math.random() < 0.5 ? 'knights' : 'bishops',
openingTempo: Math.random() * 0.5 + 0.5, // 0.5-1.0 opening move speed
tacticalAwareness: Math.random() * 0.4 + 0.6, // 0.6-1.0 tactical vision
exchangeThreshold: Math.random() * 0.3 + 0.1, // When to accept exchanges
attackingStyle: ['kingside', 'queenside', 'central'][Math.floor(Math.random() * 3)]
});
// Save on first run
if (!GM_getValue('playerFingerprint')) {
GM_setValue('playerFingerprint', myVars.playerFingerprint);
}
// Add to myVars initialization
myVars.tacticalProfile = GM_getValue('tacticalProfile', {
strengths: [
getRandomTacticalStrength(),
getRandomTacticalStrength()
],
weaknesses: [
getRandomTacticalWeakness(),
getRandomTacticalWeakness()
]
});
// Save on first run
if (!GM_getValue('tacticalProfile')) {
GM_setValue('tacticalProfile', myVars.tacticalProfile);
}
function getRandomTacticalStrength() {
const strengths = [
'fork', 'pin', 'skewer', 'discovery', 'zwischenzug',
'removing-defender', 'attraction'
];
return strengths[Math.floor(Math.random() * strengths.length)];
}
function getRandomTacticalWeakness() {
const weaknesses = [
'long-calculation', 'quiet-moves', 'backward-moves',
'zugzwang', 'prophylaxis', 'piece-coordination'
];
return weaknesses[Math.floor(Math.random() * weaknesses.length)];
}
stop_b = stop_w = 0;
s_br = s_br2 = s_wr = s_wr2 = 0;
obs = "";
myFunctions.rescan = function(lev) {
var ari = $("chess-board")
.find(".piece")
.map(function() {
return this.className;
})
.get();
jack = ari.map(f => f.substring(f.indexOf(' ') + 1));
function removeWord(arr, word) {
for (var i = 0; i < arr.length; i++) {
arr[i] = arr[i].replace(word, '');
}
}
removeWord(ari, 'square-');
jack = ari.map(f => f.substring(f.indexOf(' ') + 1));
for (var i = 0; i < jack.length; i++) {
jack[i] = jack[i].replace('br', 'r')
.replace('bn', 'n')
.replace('bb', 'b')
.replace('bq', 'q')
.replace('bk', 'k')
.replace('bb', 'b')
.replace('bn', 'n')
.replace('br', 'r')
.replace('bp', 'p')
.replace('wp', 'P')
.replace('wr', 'R')
.replace('wn', 'N')
.replace('wb', 'B')
.replace('br', 'R')
.replace('wn', 'N')
.replace('wb', 'B')
.replace('wq', 'Q')
.replace('wk', 'K')
.replace('wb', 'B')
}
str2 = "";
var count = 0,
str = "";
for (var j = 8; j > 0; j--) {
for (var i = 1; i < 9; i++) {
(str = (jack.find(el => el.includes([i] + [j])))) ? str = str.replace(/[^a-zA-Z]+/g, ''): str = "";
if (str == "") {
count++;
str = count.toString();
if (!isNaN(str2.charAt(str2.length - 1))) str2 = str2.slice(0, -1);
else {
count = 1;
str = count.toString()
}
}
str2 += str;
if (i == 8) {
count = 0;
str2 += "/";
}
}
}
str2 = str2.slice(0, -1);
//str2=str2+" KQkq - 0"
color = "";
wk = wq = bk = bq = "0";
const move = $('vertical-move-list')
.children();
if (move.length < 2) {
stop_b = stop_w = s_br = s_br2 = s_wr = s_wr2 = 0;
}
if (stop_b != 1) {
if (move.find(".black.node:contains('K')")
.length) {
bk = "";
bq = "";
stop_b = 1;
console.log('debug secb');
}
} else {
bq = "";
bk = "";
}
if (stop_b != 1)(bk = (move.find(".black.node:contains('O-O'):not(:contains('O-O-O'))")
.length) ? "" : "k") ? (bq = (move.find(".black.node:contains('O-O-O')")
.length) ? bk = "" : "q") : bq = "";
if (s_br != 1) {
if (move.find(".black.node:contains('R')")
.text()
.match('[abcd]+')) {
bq = "";
s_br = 1
}
} else bq = "";
if (s_br2 != 1) {
if (move.find(".black.node:contains('R')")
.text()
.match('[hgf]+')) {
bk = "";
s_br2 = 1
}
} else bk = "";
if (stop_b == 0) {
if (s_br == 0)
if (move.find(".white.node:contains('xa8')")
.length > 0) {
bq = "";
s_br = 1;
console.log('debug b castle_r');
}
if (s_br2 == 0)
if (move.find(".white.node:contains('xh8')")
.length > 0) {
bk = "";
s_br2 = 1;
console.log('debug b castle_l');
}
}
if (stop_w != 1) {
if (move.find(".white.node:contains('K')")
.length) {
wk = "";
wq = "";
stop_w = 1;
console.log('debug secw');
}
} else {
wq = "";
wk = "";
}
if (stop_w != 1)(wk = (move.find(".white.node:contains('O-O'):not(:contains('O-O-O'))")
.length) ? "" : "K") ? (wq = (move.find(".white.node:contains('O-O-O')")
.length) ? wk = "" : "Q") : wq = "";
if (s_wr != 1) {
if (move.find(".white.node:contains('R')")
.text()
.match('[abcd]+')) {
wq = "";
s_wr = 1
}
} else wq = "";
if (s_wr2 != 1) {
if (move.find(".white.node:contains('R')")
.text()
.match('[hgf]+')) {
wk = "";
s_wr2 = 1
}
} else wk = "";
if (stop_w == 0) {
if (s_wr == 0)
if (move.find(".black.node:contains('xa1')")
.length > 0) {
wq = "";
s_wr = 1;
console.log('debug w castle_l');
}
if (s_wr2 == 0)
if (move.find(".black.node:contains('xh1')")
.length > 0) {
wk = "";
s_wr2 = 1;
console.log('debug w castle_r');
}
}
if ($('.coordinates')
.children()
.first()
.text() == 1) {
str2 = str2 + " b " + wk + wq + bk + bq;
color = "white";
} else {
str2 = str2 + " w " + wk + wq + bk + bq;
color = "black";
}
//console.log(str2);
return str2;
}
myFunctions.color = function(dat){
response = dat;
var res1 = response.substring(0, 2);
var res2 = response.substring(2, 4);
// Check if automove is enabled and make the move
if(myVars.autoMove === true){
console.log(`Auto move enabled, moving from ${res1} to ${res2}`);
myFunctions.movePiece(res1, res2);
} else {
console.log(`Auto move disabled, highlighting ${res1} to ${res2}`);
}
isThinking = false;
res1 = res1.replace(/^a/, "1")
.replace(/^b/, "2")
.replace(/^c/, "3")
.replace(/^d/, "4")
.replace(/^e/, "5")
.replace(/^f/, "6")
.replace(/^g/, "7")
.replace(/^h/, "8");
res2 = res2.replace(/^a/, "1")
.replace(/^b/, "2")
.replace(/^c/, "3")
.replace(/^d/, "4")
.replace(/^e/, "5")
.replace(/^f/, "6")
.replace(/^g/, "7")
.replace(/^h/, "8");
// Use custom highlight color if available
const highlightColor = myVars.highlightColor || 'rgb(235, 97, 80)';
$(board.nodeName)
.prepend(`<div class="highlight square-${res2} bro" style="background-color: ${highlightColor}; opacity: 0.71;" data-test-element="highlight"></div>`)
.children(':first')
.delay(1800)
.queue(function() {
$(this)
.remove();
});
$(board.nodeName)
.prepend(`<div class="highlight square-${res1} bro" style="background-color: ${highlightColor}; opacity: 0.71;" data-test-element="highlight"></div>`)
.children(':first')
.delay(1800)
.queue(function() {
$(this)
.remove();
});
}
myFunctions.movePiece = function(from, to) {
// Check if the move is legal before attempting to make it
let isLegalMove = false;
let moveObject = null;
for (let each = 0; each < board.game.getLegalMoves().length; each++) {
if (board.game.getLegalMoves()[each].from === from &&
board.game.getLegalMoves()[each].to === to) {
isLegalMove = true;
moveObject = board.game.getLegalMoves()[each];
break;
}
}
if (!isLegalMove) {
console.log(`Attempted illegal move: ${from} to ${to}`);
return;
}
// Add a small delay before making the move to simulate human reaction time
setTimeout(() => {
try {
// Make the actual move
board.game.move({
...moveObject,
promotion: moveObject.promotion || 'q', // Default to queen for simplicity
animate: true,
userGenerated: true
});
console.log(`Successfully moved from ${from} to ${to}`);
} catch (error) {
console.error("Error making move:", error);
}
}, 100 + Math.random() * 300); // Small random delay to appear more human-like
}
// Replace simple movement simulation with this more advanced version
function simulateNaturalMouseMovement(from, to, callback) {
// Convert chess coordinates to screen positions
const fromPos = getSquarePosition(from);
const toPos = getSquarePosition(to);
// Create a bezier curve path with a slight variance
const controlPoint1 = {
x: fromPos.x + (toPos.x - fromPos.x) * 0.3 + (Math.random() * 40 - 20),
y: fromPos.y + (toPos.y - fromPos.y) * 0.1 + (Math.random() * 40 - 20)
};
const controlPoint2 = {
x: fromPos.x + (toPos.x - fromPos.x) * 0.7 + (Math.random() * 40 - 20),
y: fromPos.y + (toPos.y - fromPos.y) * 0.9 + (Math.random() * 40 - 20)
};
// Calculate movement duration based on distance
const distance = Math.sqrt(Math.pow(toPos.x - fromPos.x, 2) + Math.pow(toPos.y - fromPos.y, 2));
const baseDuration = 300 + Math.random() * 400;
const movementDuration = baseDuration + distance * (0.5 + Math.random() * 0.5);
// Create array of points along path
const steps = 15 + Math.floor(distance / 30);
const points = [];
for (let i = 0; i <= steps; i++) {
const t = i / steps;
points.push(getBezierPoint(t, fromPos, controlPoint1, controlPoint2, toPos));
}
// Execute the movement with variable speed
executeMouseMovement(points, 0, movementDuration / steps, callback);
}
// Add these functions for more realistic timing
function calculateMoveComplexity(from, to) {
// Approximate complexity based on piece type, capture, check, etc.
// Returns a value between 0 (simple) and 1 (complex)
return Math.random() * 0.7 + 0.3; // Placeholder implementation
}
function calculateHumanLikeDelay(complexity) {
// More complex positions take longer to think about
const baseDelay = 500; // Base delay in ms
const complexityFactor = 2500; // Max additional delay for complex positions
return baseDelay + (complexity * complexityFactor * Math.random());
}
function getRandomThinkingDelay() {
// Humans don't immediately start calculating
return 300 + Math.random() * 700;
}
function parser(e) {
if(e.data.includes('bestmove')) {
const bestMove = e.data.split(' ')[1];
// Extract multiple moves from engine analysis if available
let alternativeMoves = [bestMove];
try {
if (e.data.includes('pv')) {
// Try to extract alternative moves from multiPV or previous info lines
const lines = e.data.split('\n')
.filter(line => line.includes(' pv '))
.map(line => {
const pvIndex = line.indexOf(' pv ');
return line.substring(pvIndex + 4).split(' ')[0];
});
if (lines.length > 1) {
alternativeMoves = lines;
}
}
} catch (error) {
console.log("Error extracting alternative moves", error);
}
// Decide whether to use best move or an alternative
let moveToPlay = bestMove;
const currentDepth = lastValue;
// For higher depths, sometimes choose alternative moves
if (currentDepth >= 15 && alternativeMoves.length > 1 && Math.random() < 0.35) {
// Select a random move from top 5 (or fewer if not available)
const maxIndex = Math.min(5, alternativeMoves.length);
const randomIndex = Math.floor(Math.random() * (maxIndex - 1)) + 1; // Skip index 0 (best move)
moveToPlay = alternativeMoves[randomIndex] || bestMove;
console.log(`Using alternative move ${moveToPlay} instead of best move ${bestMove}`);
} else if (Math.random() < getBlunderProbability()) {
// Sometimes make a "human-like" suboptimal move
moveToPlay = generateHumanLikeMove(bestMove, e.data);
console.log(`Using human-like move ${moveToPlay} instead of best move ${bestMove}`);
}
myFunctions.color(moveToPlay);
isThinking = false;
}
}
function getBlunderProbability() {
// Use custom blunder rate from slider if available
const userBlunderRate = myVars.blunderRate !== undefined ? myVars.blunderRate : 0.05;
// Make blunder probability vary based on multiple factors
const gamePhase = estimateGamePhase();
const timeRemaining = estimateTimeRemaining();
const complexity = estimatePositionComplexity();
// Base probability comes from user settings
let baseProb = userBlunderRate;
// More likely to blunder in time pressure
if (timeRemaining < 30) {
baseProb += 0.1 * (1 - (timeRemaining / 30));
}
// More likely to blunder in complex positions
if (complexity > 0.6) {
baseProb += 0.05 * (complexity - 0.6) * 2;
}
// More likely to blunder in the endgame when tired
if (gamePhase > 30) {
baseProb += 0.03 * ((gamePhase - 30) / 10);
}
// Add randomness to make it unpredictable
return Math.min(0.4, baseProb * (0.7 + Math.random() * 0.6));
}
function estimateGamePhase() {
// Estimate how far the game has progressed (0-40 approx)
try {
const moveList = $('vertical-move-list').children().length;
return moveList / 2; // Rough estimate of move number
} catch (e) {
return 15; // Default to middle game
}
}
function estimateTimeRemaining() {
// Try to get the clock time if available
try {
const clockEl = document.querySelector('.clock-component');
if (clockEl) {
const timeText = clockEl.textContent;
if (timeText.includes(':')) {
const parts = timeText.split(':');
if (parts.length === 2) {
const minutes = parseInt(parts[0]);
const seconds = parseInt(parts[1]);
return minutes * 60 + seconds;
}
} else {
// Handle single number format (e.g., just seconds)
return parseInt(timeText);
}
}
// Try alternative clock selectors
const altClockEl = document.querySelector('.clock-time-monospace') ||
document.querySelector('.clock-time') ||
document.querySelector('[data-cy="clock-time"]');
if (altClockEl) {
const timeText = altClockEl.textContent;
if (timeText.includes(':')) {
const parts = timeText.split(':');
if (parts.length === 2) {
const minutes = parseInt(parts[0]);
const seconds = parseInt(parts[1]);
return minutes * 60 + seconds;
}
} else {
return parseInt(timeText);
}
}
} catch (e) {
console.log("Error getting time remaining:", e);
}
// Default value if clock can't be read
return 180; // 3 minutes as default
}
function estimatePositionComplexity() {
// Calculate a complexity score between 0-1
// This would ideally analyze the position for tactical elements
return Math.random() * 0.8 + 0.2; // Placeholder
}
function generateHumanLikeMove(bestMove, engineData) {
// Significantly enhance the human-like behavior
// Sometimes choose 2nd, 3rd, or even 4th best moves
if (engineData.includes('pv') && Math.random() < 0.4) {
try {
// Extract multiple lines from engine analysis
const lines = engineData.split('\n')
.filter(line => line.includes(' pv '))
.map(line => {
const pvIndex = line.indexOf(' pv ');
return line.substring(pvIndex + 4).split(' ')[0];
});
if (lines.length > 1) {
// Weight moves by their quality (prefer better moves, but sometimes choose worse ones)
const moveIndex = Math.floor(Math.pow(Math.random(), 2.5) * Math.min(lines.length, 4));
return lines[moveIndex] || bestMove;
}
} catch (e) {
console.log("Error extracting alternative moves", e);
}
}
// Occasionally make a typical human error based on common patterns
if (Math.random() < 0.15) {
// Approximate a plausible human move (e.g., moving to adjacent square)
const fromSquare = bestMove.substring(0, 2);
const toSquare = bestMove.substring(2, 4);
// Simple adjustment - occasionally play a shorter move (undershoot)
if (Math.random() < 0.7) {
const files = "abcdefgh";
const ranks = "12345678";
const fromFile = fromSquare.charAt(0);
const fromRank = fromSquare.charAt(1);
const toFile = toSquare.charAt(0);
const toRank = toSquare.charAt(1);
// Calculate direction
const fileDiff = files.indexOf(toFile) - files.indexOf(fromFile);
const rankDiff = ranks.indexOf(toRank) - ranks.indexOf(fromRank);
// Sometimes undershoot by one square
if (Math.abs(fileDiff) > 1 || Math.abs(rankDiff) > 1) {
const newToFile = files[files.indexOf(fromFile) + (fileDiff > 0 ? Math.max(1, fileDiff-1) : Math.min(-1, fileDiff+1))];
const newToRank = ranks[ranks.indexOf(fromRank) + (rankDiff > 0 ? Math.max(1, rankDiff-1) : Math.min(-1, rankDiff+1))];
// Check if the new square is valid
if (newToFile && newToRank) {
const alternativeMove = fromSquare + newToFile + newToRank;
// Verify this is a legal move before returning
for (let each=0; each<board.game.getLegalMoves().length; each++) {
if (board.game.getLegalMoves()[each].from === fromSquare &&
board.game.getLegalMoves()[each].to === newToFile + newToRank) {
return alternativeMove;
}
}
}
}
}
}
return bestMove;
}
myFunctions.reloadChessEngine = function() {
console.log(`Reloading the chess engine!`);
engine.engine.terminate();
isThinking = false;
myFunctions.loadChessEngine();
}
myFunctions.loadChessEngine = function() {
if(!stockfishObjectURL) {
stockfishObjectURL = URL.createObjectURL(new Blob([GM_getResourceText('stockfish.js')], {type: 'application/javascript'}));
}
console.log(stockfishObjectURL);
if(stockfishObjectURL) {
engine.engine = new Worker(stockfishObjectURL);
engine.engine.onmessage = e => {
parser(e);
};
engine.engine.onerror = e => {
console.log("Worker Error: "+e);
};
engine.engine.postMessage('ucinewgame');
}
console.log('loaded chess engine');
}
var lastValue = 11;
myFunctions.runChessEngine = function(depth) {
// Get time-adjusted depth
var adjustedDepth = getAdjustedDepth();
// Get position type
var fen = board.game.getFEN();
var positionType = analyzePositionType(fen);
// Log the depth adjustment
console.log(`Original depth: ${depth}, Adjusted for time/position: ${adjustedDepth}`);
engine.engine.postMessage(`position fen ${fen}`);
console.log('updated: ' + `position fen ${fen}`);
isThinking = true;
// Use multipv for alternative moves when depth is high
if (depth >= 15) {
engine.engine.postMessage(`setoption name MultiPV value 5`);
} else {
engine.engine.postMessage(`setoption name MultiPV value 1`);
}
engine.engine.postMessage(`go depth ${adjustedDepth}`);
lastValue = depth; // Keep original depth for UI display
}
function analyzePositionType(fen) {
// Determine position type: opening, middlegame, endgame, tactical
const piecesCount = fen.split(' ')[0].match(/[pnbrqkPNBRQK]/g).length;
if (piecesCount > 25) return 'opening';
if (piecesCount < 12) return 'endgame';
return 'middlegame';
}
function adjustDepthByPosition(requestedDepth, positionType) {
// Humans play more accurately in different phases
if (positionType === 'opening' && Math.random() < 0.7) {
// In openings, humans often play memorized moves
return requestedDepth + Math.floor(Math.random() * 3);
} else if (positionType === 'endgame' && Math.random() < 0.5) {
// In endgames, calculation may be more/less precise
return requestedDepth + (Math.random() < 0.5 ? -1 : 1);
}
// Occasionally randomize depth slightly
return Math.max(1, requestedDepth + (Math.random() < 0.3 ? Math.floor(Math.random() * 3) - 1 : 0));
}
myFunctions.autoRun = function(lstValue){
if(board.game.getTurn() == board.game.getPlayingAs()){
myFunctions.runChessEngine(lstValue);
}
}
document.onkeydown = function(e) {
switch (e.keyCode) {
case 81:
myFunctions.runChessEngine(1);
break;
case 87:
myFunctions.runChessEngine(2);
break;
case 69:
myFunctions.runChessEngine(3);
break;
case 82:
myFunctions.runChessEngine(4);
break;
case 84:
myFunctions.runChessEngine(5);
break;
case 89:
myFunctions.runChessEngine(6);
break;
case 85:
myFunctions.runChessEngine(7);
break;
case 73:
myFunctions.runChessEngine(8);
break;
case 79:
myFunctions.runChessEngine(9);
break;
case 80:
myFunctions.runChessEngine(10);
break;
case 65:
myFunctions.runChessEngine(11);
break;
case 83:
myFunctions.runChessEngine(12);
break;
case 68:
myFunctions.runChessEngine(13);
break;
case 70:
myFunctions.runChessEngine(14);
break;
case 71:
myFunctions.runChessEngine(15);
break;
case 72:
myFunctions.runChessEngine(16);
break;
case 74:
myFunctions.runChessEngine(17);
break;
case 75:
myFunctions.runChessEngine(18);
break;
case 76:
myFunctions.runChessEngine(19);
break;
case 90:
myFunctions.runChessEngine(20);
break;
case 88:
myFunctions.runChessEngine(21);
break;
case 67:
myFunctions.runChessEngine(22);
break;
case 86:
myFunctions.runChessEngine(23);
break;
case 66:
myFunctions.runChessEngine(24);
break;
case 78:
myFunctions.runChessEngine(25);
break;
case 77:
myFunctions.runChessEngine(26);
break;
case 187:
myFunctions.runChessEngine(100);
break;
}
};
myFunctions.spinner = function() {
if(isThinking == true){
$('#thinking-indicator').addClass('active');
}
if(isThinking == false) {
$('#thinking-indicator').removeClass('active');
}
}
let dynamicStyles = null;
function addAnimation(body) {
if (!dynamicStyles) {
dynamicStyles = document.createElement('style');
dynamicStyles.type = 'text/css';
document.head.appendChild(dynamicStyles);
}
dynamicStyles.sheet.insertRule(body, dynamicStyles.length);
}
var loaded = false;
myFunctions.loadEx = function(){
try{
var tmpStyle;
var tmpDiv;
board = $('chess-board')[0] || $('wc-chess-board')[0];
myVars.board = board;
var div = document.createElement('div')
var content = `
<div class="chess-bot-container">
<div class="bot-header">
<div class="bot-logo">
<svg viewBox="0 0 24 24" width="24" height="24">
<path fill="currentColor" d="M19,22H5V20H19V22M17,10C15.58,10 14.26,10.77 13.55,12H13V7H16V5H13V2H11V5H8V7H11V12H10.45C9.74,10.77 8.42,10 7,10A5,5 0 0,0 2,15A5,5 0 0,0 7,20H17A5,5 0 0,0 22,15A5,5 0 0,0 17,10M7,18A3,3 0 0,1 4,15A3,3 0 0,1 7,12A3,3 0 0,1 10,15A3,3 0 0,1 7,18M17,18A3,3 0 0,1 14,15A3,3 0 0,1 17,12A3,3 0 0,1 20,15A3,3 0 0,1 17,18Z" />
</svg>
</div>
<h3 class="bot-title">Panel Pro By @akashdeep</h3>
<div id="thinking-indicator" class="thinking-indicator">
<span class="dot dot1"></span>
<span class="dot dot2"></span>
<span class="dot dot3"></span>
</div>
</div>
<div class="bot-tabs">
<button class="tab-button active" data-tab="main-settings">Main</button>
<button class="tab-button" data-tab="style-settings">Play Style</button>
<button class="tab-button" data-tab="advanced-settings">Advanced</button>
</div>
<div class="bot-tabs-content">
<div class="tab-content active" id="main-settings">
<div class="bot-info">
<div class="depth-card">
<p id="depthText" class="depth-display">Current Depth: <strong>11</strong></p>
<p class="key-hint">Press a key (Q-Z) to change depth</p>
<div class="depth-control">
<label for="depthSlider">Depth/ELO:</label>
<div class="slider-controls">
<button class="depth-button" id="decreaseDepth">-</button>
<input type="range" id="depthSlider" min="1" max="21" value="11" class="depth-slider">
<button class="depth-button" id="increaseDepth">+</button>
</div>
<span id="depthValue">11</span>
</div>
</div>
</div>
<div class="bot-controls">
<div class="control-group">
<div class="control-item toggle-container">
<input type="checkbox" id="autoRun" name="autoRun" class="toggle-input" value="false">
<label for="autoRun" class="toggle-label">
<span class="toggle-button"></span>
<span class="toggle-text">Auto Run</span>
</label>
</div>
<div class="control-item toggle-container">
<input type="checkbox" id="autoMove" name="autoMove" class="toggle-input" value="false">
<label for="autoMove" class="toggle-label">
<span class="toggle-button"></span>
<span class="toggle-text">Auto Move</span>
</label>
</div>
</div>
<div class="control-group">
<div class="control-item slider-container">
<label for="timeDelayMin">Min Delay (sec)</label>
<input type="number" id="timeDelayMin" name="timeDelayMin" min="0.1" value="0.1" step="0.1" class="number-input">
</div>
<div class="control-item slider-container">
<label for="timeDelayMax">Max Delay (sec)</label>
<input type="number" id="timeDelayMax" name="timeDelayMax" min="0.1" value="1" step="0.1" class="number-input">
</div>
</div>
</div>
</div>
<div class="tab-content" id="style-settings">
<div class="playStyle-controls">
<h4 class="settings-section-title">Playing Style Settings</h4>
<div class="style-slider-container">
<label for="aggressiveSlider">Aggressive Play:</label>
<input type="range" id="aggressiveSlider" min="1" max="10" value="5" class="style-slider">
<span id="aggressiveValue">5</span>
</div>
<div class="style-slider-container">
<label for="defensiveSlider">Defensive Play:</label>
<input type="range" id="defensiveSlider" min="1" max="10" value="5" class="style-slider">
<span id="defensiveValue">5</span>
</div>
<div class="style-slider-container">
<label for="tacticalSlider">Tactical Play:</label>
<input type="range" id="tacticalSlider" min="1" max="10" value="5" class="style-slider">
<span id="tacticalValue">5</span>
</div>
<div class="style-slider-container">
<label for="positionalSlider">Positional Play:</label>
<input type="range" id="positionalSlider" min="1" max="10" value="5" class="style-slider">
<span id="positionalValue">5</span>
</div>
<div class="style-slider-container">
<label for="blunderRateSlider">Blunder Rate:</label>
<input type="range" id="blunderRateSlider" min="0" max="10" value="2" class="style-slider">
<span id="blunderRateValue">2</span>
</div>
</div>
</div>
<div class="tab-content" id="advanced-settings">
<div class="advanced-controls">
<h4 class="settings-section-title">Advanced Settings</h4>
<div class="control-item toggle-container">
<input type="checkbox" id="adaptToRating" name="adaptToRating" class="toggle-input" value="true" checked>
<label for="adaptToRating" class="toggle-label">
<span class="toggle-button"></span>
<span class="toggle-text">Adapt to opponent rating</span>
</label>
</div>
<div class="control-item toggle-container">
<input type="checkbox" id="useOpeningBook" name="useOpeningBook" class="toggle-input" value="true" checked>
<label for="useOpeningBook" class="toggle-label">
<span class="toggle-button"></span>
<span class="toggle-text">Use personalized openings</span>
</label>
</div>
<div class="highlight-color-picker">
<label for="highlightColor">Move highlight color:</label>
<input type="color" id="highlightColor" value="#eb6150" class="color-input">
</div>
<div class="opening-selection">
<label for="preferredOpeningSelect">Preferred Opening:</label>
<select id="preferredOpeningSelect" class="select-input">
<option value="random">Random</option>
<option value="e4">e4 (King's Pawn)</option>
<option value="d4">d4 (Queen's Pawn)</option>
<option value="c4">c4 (English)</option>
<option value="Nf3">Nf3 (Réti)</option>
</select>
</div>
</div>
</div>
</div>
<div class="bot-actions">
<button type="button" id="relEngBut" class="action-button primary-button" onclick="document.myFunctions.reloadChessEngine()">
<svg viewBox="0 0 24 24" width="16" height="16">
<path fill="currentColor" d="M17.65,6.35C16.2,4.9 14.21,4 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20C15.73,20 18.84,17.45 19.73,14H17.65C16.83,16.33 14.61,18 12,18A6,6 0 0,1 6,12A6,6 0 0,1 12,6C13.66,6 15.14,6.69 16.22,7.78L13,11H20V4L17.65,6.35Z" />
</svg>
Reload Engine
</button>
<button type="button" id="isBut" class="action-button secondary-button" onclick="window.confirm('Can I take you to the issues page?') ? document.location = 'https://github.com/Auzgame/userscripts/issues' : console.log('canceled')">
<svg viewBox="0 0 24 24" width="16" height="16">
<path fill="currentColor" d="M13,13H11V7H13M13,17H11V15H13M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z" />
</svg>
Report Issue
</button>
</div>
</div>`;
div.innerHTML = content;
div.setAttribute('id','settingsContainer');
board.parentElement.parentElement.appendChild(div);
//Add CSS styles
var botStyles = document.createElement('style');
botStyles.innerHTML = `
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap');
#settingsContainer {
background-color: #1e1e2e;
color: #cdd6f4;
border-radius: 12px;
padding: 20px;
margin-top: 15px;
box-shadow: 0 8px 24px rgba(0,0,0,0.3);
font-family: 'Roboto', sans-serif;
max-width: 400px;
margin-left: auto;
margin-right: auto;
border: 1px solid #313244;
}
.chess-bot-container {
display: flex;
flex-direction: column;
gap: 16px;
}
.bot-header {
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
margin-bottom: 5px;
position: relative;
}
.bot-logo {
display: flex;
align-items: center;
justify-content: center;
color: #89b4fa;
}
.bot-title {
margin: 0;
color: #89b4fa;
font-size: 20px;
font-weight: 500;
letter-spacing: 0.5px;
}
.thinking-indicator {
position: absolute;
right: 0;
display: none;
align-items: center;
}
.thinking-indicator.active {
display: flex;
}
.dot {
width: 8px;
height: 8px;
margin: 0 2px;
background-color: #89b4fa;
border-radius: 50%;
opacity: 0.6;
}
.dot1 {
animation: pulse 1.5s infinite;
}
.dot2 {
animation: pulse 1.5s infinite 0.3s;
}
.dot3 {
animation: pulse 1.5s infinite 0.6s;
}
@keyframes pulse {
0%, 100% {
transform: scale(1);
opacity: 0.6;
}
50% {
transform: scale(1.3);
opacity: 1;
}
}
.bot-tabs {
display: flex;
gap: 2px;
margin-bottom: -15px;
}
.tab-button {
background-color: #313244;
color: #a6adc8;
border: none;
border-radius: 8px 8px 0 0;
padding: 8px 16px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: all 0.2s;
flex: 1;
text-align: center;
}
.tab-button:hover {
background-color: #45475a;
color: #cdd6f4;
}
.tab-button.active {
background-color: #45475a;
color: #cdd6f4;
}
.bot-tabs-content {
background-color: #45475a;
border-radius: 0 8px 8px 8px;
padding: 16px;
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
.settings-section-title {
margin-top: 0;
margin-bottom: 12px;
color: #89b4fa;
font-size: 16px;
font-weight: 500;
text-align: center;
}
.bot-info {
display: flex;
justify-content: center;
margin-bottom: 5px;
}
.depth-card {
background-color: #313244;
border-radius: 8px;
padding: 12px 16px;
text-align: center;
width: 100%;
border: 1px solid #45475a;
}
.depth-display {
font-size: 16px;
margin: 0 0 5px 0;
font-weight: 400;
}
.depth-display strong {
color: #89b4fa;
font-weight: 600;
}
.key-hint {
font-size: 12px;
color: #a6adc8;
margin: 0;
font-weight: 300;
}
.bot-controls {
display: flex;
flex-direction: column;
gap: 16px;
}
.control-group {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
}
.control-item {
display: flex;
flex-direction: column;
gap: 6px;
}
.toggle-container {
position: relative;
}
.toggle-input {
opacity: 0;
width: 0;
height: 0;
position: absolute;
}
.toggle-label {
display: flex;
align-items: center;
gap: 10px;
cursor: pointer;
}
.toggle-button {
position: relative;
display: inline-block;
width: 40px;
height: 20px;
background-color: #45475a;
border-radius: 20px;
transition: all 0.3s;
}
.toggle-button:before {
content: '';
position: absolute;
width: 16px;
height: 16px;
border-radius: 50%;
background-color: #cdd6f4;
top: 2px;
left: 2px;
transition: all 0.3s;
}
.toggle-input:checked + .toggle-label .toggle-button {
background-color: #89b4fa;
}
.toggle-input:checked + .toggle-label .toggle-button:before {
transform: translateX(20px);
}
.toggle-text {
font-size: 14px;
}
.slider-container {
display: flex;
flex-direction: column;
gap: 6px;
}
.slider-container label {
font-size: 13px;
color: #a6adc8;
}
.number-input {
width: 100%;
background: #313244;
border: 1px solid #45475a;
border-radius: 6px;
color: #cdd6f4;
padding: 8px 10px;
font-size: 14px;
transition: border-color 0.3s;
}
.number-input:focus {
outline: none;
border-color: #89b4fa;
}
.bot-actions {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
margin-top: 5px;
}
.action-button {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
border: none;
border-radius: 8px;
padding: 10px 16px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
}
.primary-button {
background-color: #89b4fa;
color: #1e1e2e;
}
.primary-button:hover {
background-color: #b4befe;
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(137, 180, 250, 0.3);
}
.secondary-button {
background-color: #45475a;
color: #cdd6f4;
}
.secondary-button:hover {
background-color: #585b70;
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(69, 71, 90, 0.3);
}
.action-button:active {
transform: translateY(0);
}
.depth-control {
margin-top: 12px;
display: flex;
flex-direction: column;
gap: 8px;
}
.depth-control label {
font-size: 13px;
color: #a6adc8;
}
.slider-controls {
display: flex;
align-items: center;
gap: 8px;
}
.depth-slider {
flex: 1;
height: 6px;
-webkit-appearance: none;
appearance: none;
background: #45475a;
border-radius: 3px;
outline: none;
}
.depth-slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 16px;
height: 16px;
border-radius: 50%;
background: #89b4fa;
cursor: pointer;
}
.depth-slider::-moz-range-thumb {
width: 16px;
height: 16px;
border-radius: 50%;
background: #89b4fa;
cursor: pointer;
}
.depth-button {
width: 24px;
height: 24px;
border-radius: 50%;
background: #45475a;
color: #cdd6f4;
border: none;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
font-size: 16px;
font-weight: bold;
transition: background-color 0.2s;
}
.depth-button:hover {
background: #585b70;
}
#depthValue {
font-size: 14px;
font-weight: 500;
color: #89b4fa;
text-align: center;
}
`;
document.head.appendChild(botStyles);
//spinnerContainer
var spinCont = document.createElement('div');
spinCont.setAttribute('style','display:none;');
spinCont.setAttribute('id','overlay');
div.prepend(spinCont);
//spinner
var spinr = document.createElement('div')
spinr.setAttribute('style',`
margin: 0 auto;
height: 40px;
width: 40px;
animation: rotate 0.8s infinite linear;
border: 4px solid #89b4fa;
border-right-color: transparent;
border-radius: 50%;
box-shadow: 0 0 10px rgba(137, 180, 250, 0.4);
`);
spinCont.appendChild(spinr);
addAnimation(`@keyframes rotate {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}`);
$('#depthSlider').on('input', function() {
const depth = parseInt($(this).val());
$('#depthValue').text(depth);
lastValue = depth;
$('#depthText')[0].innerHTML = "Current Depth: <strong>" + depth + "</strong>";
});
$('#decreaseDepth').on('click', function() {
const currentDepth = parseInt($('#depthSlider').val());
if (currentDepth > 1) {
const newDepth = currentDepth - 1;
$('#depthSlider').val(newDepth).trigger('input');
}
});
$('#increaseDepth').on('click', function() {
const currentDepth = parseInt($('#depthSlider').val());
if (currentDepth < 26) {
const newDepth = currentDepth + 1;
$('#depthSlider').val(newDepth).trigger('input');
}
});
// Fix tab switching and initialize all controls
$(document).on('click', '.tab-button', function() {
$('.tab-button').removeClass('active');
$(this).addClass('active');
const tabId = $(this).data('tab');
$('.tab-content').removeClass('active');
$(`#${tabId}`).addClass('active');
});
// Initialize play style sliders with values from myVars
if (myVars.playStyle) {
$('#aggressiveSlider').val(Math.round(((myVars.playStyle.aggressive - 0.3) / 0.5) * 10));
$('#aggressiveValue').text($('#aggressiveSlider').val());
$('#defensiveSlider').val(Math.round(((myVars.playStyle.defensive - 0.3) / 0.5) * 10));
$('#defensiveValue').text($('#defensiveSlider').val());
$('#tacticalSlider').val(Math.round(((myVars.playStyle.tactical - 0.2) / 0.6) * 10));
$('#tacticalValue').text($('#tacticalSlider').val());
$('#positionalSlider').val(Math.round(((myVars.playStyle.positional - 0.2) / 0.6) * 10));
$('#positionalValue').text($('#positionalSlider').val());
}
// Set the blunder rate slider
const blunderRate = myVars.blunderRate !== undefined ? Math.round(myVars.blunderRate * 10) : 2;
$('#blunderRateSlider').val(blunderRate);
$('#blunderRateValue').text(blunderRate);
// Setup the style sliders
$('.style-slider').on('input', function() {
const value = $(this).val();
$(`#${this.id}Value`).text(value);
// Update the myVars.playStyle object
const styleType = this.id.replace('Slider', '');
if (styleType === 'blunderRate') {
myVars.blunderRate = parseFloat(value) / 10;
} else if (myVars.playStyle && styleType in myVars.playStyle) {
if (styleType === 'aggressive' || styleType === 'defensive') {
myVars.playStyle[styleType] = 0.3 + (parseFloat(value) / 10) * 0.5;
} else {
myVars.playStyle[styleType] = 0.2 + (parseFloat(value) / 10) * 0.6;
}
}
});
// Initialize advanced settings
if (myVars.adaptToRating !== undefined) {
$('#adaptToRating').prop('checked', myVars.adaptToRating);
}
if (myVars.useOpeningBook !== undefined) {
$('#useOpeningBook').prop('checked', myVars.useOpeningBook);
}
if (myVars.highlightColor) {
$('#highlightColor').val(myVars.highlightColor);
}
// Set up preferred opening selection
if (myVars.preferredOpenings && myVars.preferredOpenings.length === 1) {
$('#preferredOpeningSelect').val(myVars.preferredOpenings[0]);
} else {
$('#preferredOpeningSelect').val('random');
}
// Set up advanced settings event handlers
$('#adaptToRating').on('change', function() {
myVars.adaptToRating = $(this).prop('checked');
});
$('#useOpeningBook').on('change', function() {
myVars.useOpeningBook = $(this).prop('checked');
});
$('#highlightColor').on('input', function() {
myVars.highlightColor = $(this).val();
});
$('#preferredOpeningSelect').on('change', function() {
const selectedOpening = $(this).val();
if (selectedOpening === 'random') {
myVars.preferredOpenings = ["e4", "d4", "c4", "Nf3"].sort(() => Math.random() - 0.5);
} else {
myVars.preferredOpenings = [selectedOpening];
}
});
// Setup hotkey options and additional config
const extraSettings = `
<div class="advanced-section">
<div class="control-item toggle-container">
<input type="checkbox" id="enableHotkeys" name="enableHotkeys" class="toggle-input" checked>
<label for="enableHotkeys" class="toggle-label">
<span class="toggle-button"></span>
<span class="toggle-text">Enable keyboard shortcuts</span>
</label>
</div>
<div class="control-item toggle-container">
<input type="checkbox" id="randomizeTiming" name="randomizeTiming" class="toggle-input" checked>
<label for="randomizeTiming" class="toggle-label">
<span class="toggle-button"></span>
<span class="toggle-text">Randomize thinking time</span>
</label>
</div>
<div class="style-slider-container">
<label for="mouseMovementSlider">Mouse Movement Realism:</label>
<input type="range" id="mouseMovementSlider" min="1" max="10" value="7" class="style-slider">
<span id="mouseMovementSliderValue">7</span>
</div>
<div class="profile-selection">
<label for="playingProfileSelect">Playing Profile:</label>
<select id="playingProfileSelect" class="select-input">
<option value="custom">Custom Settings</option>
<option value="beginner">Beginner (≈800)</option>
<option value="intermediate">Intermediate (≈1200)</option>
<option value="advanced">Advanced (≈1600)</option>
<option value="expert">Expert (≈2000)</option>
<option value="master">Master (≈2400+)</option>
</select>
</div>
</div>
`;
$('#advanced-settings .advanced-controls').append(extraSettings);
// Initialize additional settings
myVars.enableHotkeys = true;
myVars.randomizeTiming = true;
myVars.mouseMovementRealism = 0.7;
// Setup additional event handlers
$('#enableHotkeys').on('change', function() {
myVars.enableHotkeys = $(this).prop('checked');
});
$('#randomizeTiming').on('change', function() {
myVars.randomizeTiming = $(this).prop('checked');
});
$('#mouseMovementSlider').on('input', function() {
const value = $(this).val();
$('#mouseMovementSliderValue').text(value);
myVars.mouseMovementRealism = parseFloat(value) / 10;
});
$('#playingProfileSelect').on('change', function() {
const profile = $(this).val();
if (profile !== 'custom') {
// Preset profiles with appropriate settings
switch(profile) {
case 'beginner':
$('#depthSlider').val(3).trigger('input');
$('#blunderRateSlider').val(7).trigger('input');
$('#aggressiveSlider').val(Math.floor(3 + Math.random() * 5)).trigger('input');
$('#tacticalSlider').val(3).trigger('input');
break;
case 'intermediate':
$('#depthSlider').val(6).trigger('input');
$('#blunderRateSlider').val(5).trigger('input');
$('#tacticalSlider').val(5).trigger('input');
break;
case 'advanced':
$('#depthSlider').val(9).trigger('input');
$('#blunderRateSlider').val(3).trigger('input');
$('#tacticalSlider').val(7).trigger('input');
break;
case 'expert':
$('#depthSlider').val(12).trigger('input');
$('#blunderRateSlider').val(2).trigger('input');
$('#tacticalSlider').val(8).trigger('input');
$('#positionalSlider').val(8).trigger('input');
break;
case 'master':
$('#depthSlider').val(15).trigger('input');
$('#blunderRateSlider').val(1).trigger('input');
$('#tacticalSlider').val(9).trigger('input');
$('#positionalSlider').val(9).trigger('input');
break;
}
}
});
// Add CSS for new elements
const extraStyles = document.createElement('style');
extraStyles.innerHTML = `
.advanced-section {
margin-top: 15px;
padding-top: 15px;
border-top: 1px solid #313244;
}
.profile-selection {
margin-top: 15px;
}
.playStyle-controls, .advanced-controls {
display: flex;
flex-direction: column;
gap: 12px;
}
.style-slider-container {
display: flex;
align-items: center;
gap: 10px;
}
.style-slider-container label {
font-size: 13px;
color: #cdd6f4;
width: 120px;
}
.style-slider {
flex: 1;
height: 6px;
-webkit-appearance: none;
appearance: none;
background: #313244;
border-radius: 3px;
outline: none;
}
.style-slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 14px;
height: 14px;
border-radius: 50%;
background: #89b4fa;
cursor: pointer;
}
.style-slider-container span {
font-size: 14px;
font-weight: 500;
color: #89b4fa;
width: 20px;
text-align: center;
}
.highlight-color-picker {
display: flex;
align-items: center;
gap: 10px;
margin-top: 10px;
}
.highlight-color-picker label {
font-size: 13px;
color: #cdd6f4;
}
.color-input {
-webkit-appearance: none;
width: 30px;
height: 30px;
border: none;
border-radius: 50%;
background: none;
cursor: pointer;
}
.color-input::-webkit-color-swatch {
border: none;
border-radius: 50%;
box-shadow: 0 0 0 2px #45475a;
}
.opening-selection {
display: flex;
align-items: center;
gap: 10px;
margin-top: 10px;
}
.opening-selection label {
font-size: 13px;
color: #cdd6f4;
}
.select-input {
flex: 1;
background: #313244;
border: 1px solid #45475a;
border-radius: 6px;
color: #cdd6f4;
padding: 8px 10px;
font-size: 14px;
transition: border-color 0.3s;
}
.select-input:focus {
outline: none;
border-color: #89b4fa;
}
`;
document.head.appendChild(extraStyles);
// Load saved settings before applying them to the UI
myFunctions.loadSettings();
// Apply loaded settings to UI controls
$('#autoRun').prop('checked', myVars.autoRun);
$('#autoMove').prop('checked', myVars.autoMove);
$('#depthSlider').val(lastValue);
$('#depthValue').text(lastValue);
$('#depthText').html("Current Depth: <strong>" + lastValue + "</strong>");
if (myVars.highlightColor) {
$('#highlightColor').val(myVars.highlightColor);
}
// Update the play style sliders with saved values
if (myVars.playStyle) {
$('#aggressiveSlider').val(Math.round(((myVars.playStyle.aggressive - 0.3) / 0.5) * 10));
$('#aggressiveValue').text($('#aggressiveSlider').val());
$('#defensiveSlider').val(Math.round(((myVars.playStyle.defensive - 0.3) / 0.5) * 10));
$('#defensiveValue').text($('#defensiveSlider').val());
$('#tacticalSlider').val(Math.round(((myVars.playStyle.tactical - 0.2) / 0.6) * 10));
$('#tacticalValue').text($('#tacticalSlider').val());
$('#positionalSlider').val(Math.round(((myVars.playStyle.positional - 0.2) / 0.6) * 10));
$('#positionalValue').text($('#positionalSlider').val());
}
// Update blunder rate slider with saved value
if (myVars.blunderRate !== undefined) {
$('#blunderRateSlider').val(Math.round(myVars.blunderRate * 10));
$('#blunderRateValue').text($('#blunderRateSlider').val());
}
// Set advanced settings based on saved values
$('#adaptToRating').prop('checked', myVars.adaptToRating);
$('#useOpeningBook').prop('checked', myVars.useOpeningBook);
$('#enableHotkeys').prop('checked', myVars.enableHotkeys);
$('#randomizeTiming').prop('checked', myVars.randomizeTiming);
if (myVars.mouseMovementRealism !== undefined) {
$('#mouseMovementSlider').val(Math.round(myVars.mouseMovementRealism * 10));
$('#mouseMovementSliderValue').text($('#mouseMovementSlider').val());
}
if (myVars.preferredOpenings && myVars.preferredOpenings.length === 1) {
$('#preferredOpeningSelect').val(myVars.preferredOpenings[0]);
}
// Add event handlers to save settings when they change
$('#autoRun, #autoMove, #adaptToRating, #useOpeningBook, #enableHotkeys, #randomizeTiming').on('change', function() {
const id = $(this).attr('id');
myVars[id] = $(this).prop('checked');
myFunctions.saveSettings();
});
$('#depthSlider').on('input', function() {
// The existing handler already updates the UI
myFunctions.saveSettings();
});
$('#timeDelayMin, #timeDelayMax').on('change', function() {
myFunctions.saveSettings();
});
$('.style-slider').on('input', function() {
// The existing handler updates the playStyle object
myFunctions.saveSettings();
});
$('#highlightColor').on('input', function() {
// Existing handler already updates myVars.highlightColor
myFunctions.saveSettings();
});
$('#preferredOpeningSelect').on('change', function() {
// Existing handler updates preferredOpenings
myFunctions.saveSettings();
});
$('#playingProfileSelect').on('change', function() {
// After profile is applied
setTimeout(myFunctions.saveSettings, 100);
});
$('#autoMove').on('change', function() {
myVars.autoMove = $(this).prop('checked');
console.log(`Auto move set to: ${myVars.autoMove}`);
myFunctions.saveSettings();
});
loaded = true;
} catch (error) {console.log(error)}
}
function other(delay) {
// Create more natural timing pattern based on game situation
const gamePhase = estimateGamePhase();
const positionComplexity = estimatePositionComplexity();
// Apply more realistic timing adjustments
let naturalDelay = delay;
// Faster moves in openings
if (gamePhase < 10) {
naturalDelay *= (0.6 + Math.random() * 0.4);
}
// Slower in complex positions
if (positionComplexity > 0.7) {
naturalDelay *= (1 + Math.random() * 1.5);
}
// Add slight additional randomness
naturalDelay *= (0.85 + Math.random() * 0.3);
var endTime = Date.now() + naturalDelay;
var timer = setInterval(()=>{
if(Date.now() >= endTime){
myFunctions.autoRun(getAdjustedDepth());
canGo = true;
clearInterval(timer);
}
},10);
}
function getAdjustedDepth() {
// Get time remaining and adjust depth accordingly
const timeRemaining = estimateTimeRemaining();
const gamePhase = estimateGamePhase();
const positionType = analyzePositionType(board.game.getFEN());
const isPositionCritical = isPositionCriticalNow();
// Base depth from slider
let baseDepth = lastValue;
// Time-based adjustments
if (timeRemaining < 30) {
// Below 30 seconds - use very low depth
return Math.floor(Math.random() * 3) + 1; // 1-3
} else if (timeRemaining < 60) {
// Below 1 minute - use low depth
return Math.floor(Math.random() * 3) + 5; // 5-7
}
// Game phase adjustments
if (gamePhase < 10) {
// Opening phase - use lower depth
baseDepth = Math.min(baseDepth, 10);
} else if (gamePhase > 30) {
// Endgame - can use higher depth as positions are simpler
baseDepth = Math.min(baseDepth + 2, 20);
}
// Occasionally use very low depth to prevent detection
if (!isPositionCritical && Math.random() < 0.15) {
return Math.floor(Math.random() * 2) + 2; // 2-3
}
// Add some randomness to the depth
const variation = Math.floor(Math.random() * 5) - 2; // -2 to +2
return Math.max(1, Math.min(20, baseDepth + variation));
}
function isPositionCriticalNow() {
// Determine if the current position is critical
// This is a simplified implementation
try {
// Check if kings are under attack
const inCheck = board.game.inCheck();
// Check material balance (simplified)
const fen = board.game.getFEN();
const whiteMaterial = countMaterial(fen, true);
const blackMaterial = countMaterial(fen, false);
const materialDifference = Math.abs(whiteMaterial - blackMaterial);
// Position is critical if in check or material is close
return inCheck || materialDifference < 2;
} catch (e) {
return false;
}
}
function countMaterial(fen, isWhite) {
// Simple material counting function
const position = fen.split(' ')[0];
let material = 0;
const pieces = isWhite ? 'PNBRQK' : 'pnbrqk';
const values = { 'P': 1, 'N': 3, 'B': 3, 'R': 5, 'Q': 9, 'K': 0,
'p': 1, 'n': 3, 'b': 3, 'r': 5, 'q': 9, 'k': 0 };
for (let char of position) {
if (pieces.includes(char)) {
material += values[char];
}
}
return material;
}
async function getVersion(){
var GF = new GreasyFork; // set upping api
var code = await GF.get().script().code(531100); // Get code
var version = GF.parseScriptCodeMeta(code).filter(e => e.meta === '@version')[0].value; // filtering array and getting value of @version
if(currentVersion !== version){
while(true){
alert('UPDATE THIS SCRIPT IN ORDER TO PROCEED!');
}
}
}
//Removed due to script being reported. I tried to make it so people can know when bug fixes come out. Clearly people don't like that.
//getVersion();
const waitForChessBoard = setInterval(() => {
if(loaded) {
board = $('chess-board')[0] || $('wc-chess-board')[0];
myVars.autoRun = $('#autoRun')[0].checked;
myVars.autoMove = $('#autoMove')[0].checked;
let minDel = parseFloat($('#timeDelayMin')[0].value);
let maxDel = parseFloat($('#timeDelayMax')[0].value);
myVars.delay = Math.random() * (maxDel - minDel) + minDel;
myVars.isThinking = isThinking;
myFunctions.spinner();
if(board.game.getTurn() == board.game.getPlayingAs()){myTurn = true;} else {myTurn = false;}
$('#depthText')[0].innerHTML = "Current Depth: <strong>"+lastValue+"</strong>";
} else {
myFunctions.loadEx();
}
if(!engine.engine){
myFunctions.loadChessEngine();
}
if(myVars.autoRun == true && canGo == true && isThinking == false && myTurn){
//console.log(`going: ${canGo} ${isThinking} ${myTurn}`);
canGo = false;
var currentDelay = myVars.delay != undefined ? myVars.delay * 1000 : 10;
other(currentDelay);
}
}, 100);
// Add these two functions for saving and loading settings
myFunctions.saveSettings = function() {
GM_setValue('autoRun', myVars.autoRun);
GM_setValue('autoMove', myVars.autoMove);
GM_setValue('timeDelayMin', $('#timeDelayMin').val());
GM_setValue('timeDelayMax', $('#timeDelayMax').val());
GM_setValue('depthValue', lastValue);
GM_setValue('highlightColor', myVars.highlightColor);
GM_setValue('playStyle', myVars.playStyle);
GM_setValue('blunderRate', myVars.blunderRate);
GM_setValue('adaptToRating', myVars.adaptToRating);
GM_setValue('useOpeningBook', myVars.useOpeningBook);
GM_setValue('preferredOpenings', myVars.preferredOpenings);
GM_setValue('enableHotkeys', myVars.enableHotkeys);
GM_setValue('randomizeTiming', myVars.randomizeTiming);
GM_setValue('mouseMovementRealism', myVars.mouseMovementRealism);
myFunctions.saveGameMemory(result, opponent, mistakes);
myVars.openingRepertoire = GM_getValue('openingRepertoire', {
white: {
main: getRandomOpenings(2),
experimental: getRandomOpenings(3, true),
success: {}
},
black: {
responses: {
e4: getRandomResponses('e4', 2),
d4: getRandomResponses('d4', 2),
c4: getRandomResponses('c4', 2),
other: getRandomResponses('other', 2)
},
success: {}
},
lastUpdated: Date.now()
});
updateOpeningRepertoire();
};
myFunctions.loadSettings = function() {
myVars.autoRun = GM_getValue('autoRun', false);
myVars.autoMove = GM_getValue('autoMove', false);
$('#timeDelayMin').val(GM_getValue('timeDelayMin', 0.1));
$('#timeDelayMax').val(GM_getValue('timeDelayMax', 1));
lastValue = GM_getValue('depthValue', 11);
myVars.highlightColor = GM_getValue('highlightColor', 'rgb(235, 97, 80)');
myVars.playStyle = GM_getValue('playStyle', {
aggressive: Math.random() * 0.5 + 0.3,
defensive: Math.random() * 0.5 + 0.3,
tactical: Math.random() * 0.6 + 0.2,
positional: Math.random() * 0.6 + 0.2
});
myVars.blunderRate = GM_getValue('blunderRate', 0.05);
myVars.adaptToRating = GM_getValue('adaptToRating', true);
myVars.useOpeningBook = GM_getValue('useOpeningBook', true);
myVars.preferredOpenings = GM_getValue('preferredOpenings', ["e4", "d4", "c4", "Nf3"]);
myVars.enableHotkeys = GM_getValue('enableHotkeys', true);
myVars.randomizeTiming = GM_getValue('randomizeTiming', true);
myVars.mouseMovementRealism = GM_getValue('mouseMovementRealism', 0.7);
};
myFunctions.saveGameMemory = function(result, opponent, mistakes) {
// Get existing memory
let gameMemory = GM_getValue('gameMemory', {
openingSuccess: {},
opponentHistory: {},
mistakePositions: []
});
// Store opening success rate
const opening = board.game.pgn.split('\n\n')[1].split(' ').slice(0, 6).join(' ');
if (!gameMemory.openingSuccess[opening]) {
gameMemory.openingSuccess[opening] = { wins: 0, losses: 0, draws: 0 };
}
gameMemory.openingSuccess[opening][result]++;
// Record opponent data
if (!gameMemory.opponentHistory[opponent]) {
gameMemory.opponentHistory[opponent] = { games: 0, style: 'unknown' };
}
gameMemory.opponentHistory[opponent].games++;
// Store mistake patterns for future reference
if (mistakes && mistakes.length) {
gameMemory.mistakePositions = gameMemory.mistakePositions.concat(mistakes).slice(-50);
}
GM_setValue('gameMemory', gameMemory);
};
function getThinkingTime(position) {
const baseTime = parseFloat($('#timeDelayMin').val()) * 1000;
const maxTime = parseFloat($('#timeDelayMax').val()) * 1000;
// Analyze position
const complexity = calculatePositionComplexity(position); // 0-1
const criticalness = determinePositionCriticalness(position); // 0-1
const familiarity = assessPositionFamiliarity(position); // 0-1
// Human-like time scaling
let thinkingTime = baseTime;
// Complex positions take more time
thinkingTime += complexity * (maxTime - baseTime) * 0.7;
// Critical positions deserve more attention
thinkingTime += criticalness * (maxTime - baseTime) * 0.5;
// Unfamiliar positions need more thought
thinkingTime += (1 - familiarity) * (maxTime - baseTime) * 0.3;
// Avoid predictable timing with small random variation
thinkingTime *= 0.85 + Math.random() * 0.3;
return Math.min(maxTime * 1.2, thinkingTime);
}
// Add placeholder functions that can be expanded
function calculatePositionComplexity(position) {
// Count material, tension, possible tactics
const pieceCount = position.split(/[prnbqkPRNBQK]/).length - 1;
const hasQueen = position.includes('q') || position.includes('Q');
const pawnStructure = analyzeBasicPawnStructure(position);
return Math.min(1, (pieceCount / 32) + (hasQueen ? 0.2 : 0) + pawnStructure * 0.3);
}
function adjustEngineEvaluation(position, move, evaluation) {
// Apply player's strategic preferences
const adjustedEval = { ...evaluation };
// Adjust based on fingerprint
if (myVars.playerFingerprint.favoredPieces === 'knights' && move.includes('N')) {
adjustedEval.score += 0.05 + Math.random() * 0.1;
}
if (myVars.playerFingerprint.favoredPieces === 'bishops' && move.includes('B')) {
adjustedEval.score += 0.05 + Math.random() * 0.1;
}
// Kingside/queenside attack preference
if (myVars.playerFingerprint.attackingStyle === 'kingside' && isKingsideMove(move)) {
adjustedEval.score += 0.07 + Math.random() * 0.08;
}
// Occasionally have a "brilliant" insight (1-2% of moves)
if (Math.random() < 0.015) {
// Temporary increase in effective depth for this move evaluation
console.log("Brilliant insight detected!");
return getDeepEvaluation(position, move);
}
return adjustedEval;
}
function isEndgame(position) {
// Basic endgame detection
const pieceCount = position.split(/[prnbqkPRNBQK]/).length - 1;
return pieceCount <= 10;
}
function adjustEndgamePlay(fen, moves) {
if (!isEndgame(fen)) return moves;
// Common human endgame patterns
const hasKingActivity = increasesKingActivity(moves[0], fen);
const promotionPotential = evaluatePromotionPotential(fen);
// King opposition knowledge (common human knowledge)
if (hasKingOpposition(fen) && Math.random() < 0.9) {
// Humans typically understand king opposition
return prioritizeOppositionMoves(moves);
}
// Activate king in endgames (humans know this)
if (hasKingActivity && Math.random() < 0.75) {
return moves; // Keep the engine's correct evaluation
} else if (hasKingActivity) {
// Sometimes miss the importance of king activity
return [moves[1] || moves[0], ...moves.slice(1)];
}
// Humans tend to focus on pawn promotion
if (promotionPotential > 0.7) {
return prioritizePawnAdvancement(moves);
}
return moves;
}
// Add to myVars at initialization
myVars.psychologicalState = {
confidence: 0.7 + Math.random() * 0.3, // 0.7-1.0
tiltFactor: 0, // 0-1, increases with blunders
focus: 0.8 + Math.random() * 0.2, // 0.8-1.0, decreases with time
playTime: 0 // tracks continuous play time
};
// Update in a function that runs periodically
function updatePsychologicalState(gameState) {
// Increase play time
myVars.psychologicalState.playTime += 1;
// Focus decreases with time (mental fatigue)
if (myVars.psychologicalState.playTime > 10) {
myVars.psychologicalState.focus = Math.max(0.5,
myVars.psychologicalState.focus - 0.01);
}
// Check for blunders in recent moves
if (detectBlunder(gameState)) {
// Increase tilt after mistakes
myVars.psychologicalState.tiltFactor = Math.min(1,
myVars.psychologicalState.tiltFactor + 0.25);
myVars.psychologicalState.confidence = Math.max(0.3,
myVars.psychologicalState.confidence - 0.15);
}
// Good moves restore confidence
if (detectGoodMove(gameState)) {
myVars.psychologicalState.confidence = Math.min(1,
myVars.psychologicalState.confidence + 0.05);
myVars.psychologicalState.tiltFactor = Math.max(0,
myVars.psychologicalState.tiltFactor - 0.1);
}
// Apply psychological state to blunder rate
const effectiveBlunderRate = myVars.blunderRate *
(1 + myVars.psychologicalState.tiltFactor) *
(2 - myVars.psychologicalState.focus);
return effectiveBlunderRate;
}
function detectTimeControl() {
try {
// Look for the clock element and determine time control
const clockEl = document.querySelector('.clock-component');
if (clockEl) {
const timeText = clockEl.textContent;
const minutes = parseInt(timeText.split(':')[0]);
if (minutes >= 15) return 'classical';
if (minutes >= 5) return 'rapid';
return 'blitz';
}
} catch (e) {}
return 'rapid'; // Default
}
function adaptToTimeControl() {
const timeControl = detectTimeControl();
switch (timeControl) {
case 'blitz':
// More intuitive play, faster moves, higher blunder rate
myVars.blunderRate *= 1.3;
$('#timeDelayMin').val(Math.max(0.1, parseFloat($('#timeDelayMin').val()) * 0.7));
$('#timeDelayMax').val(Math.max(0.3, parseFloat($('#timeDelayMax').val()) * 0.7));
// Use more recognizable patterns and opening theory
myVars.patternRecognitionWeight = 0.8;
break;
case 'rapid':
// Balanced approach
myVars.patternRecognitionWeight = 0.6;
break;
case 'classical':
// More calculation, deeper search, fewer blunders
myVars.blunderRate *= 0.7;
$('#timeDelayMin').val(parseFloat($('#timeDelayMin').val()) * 1.2);
$('#timeDelayMax').val(parseFloat($('#timeDelayMax').val()) * 1.3);
// More unique moves, less reliance on patterns
myVars.patternRecognitionWeight = 0.4;
break;
}
}
function updateOpeningRepertoire() {
// Only update periodically (simulating a player learning new openings)
if (Date.now() - myVars.openingRepertoire.lastUpdated < 7 * 24 * 60 * 60 * 1000) {
return; // Only update weekly
}
// Replace worst performing opening with a new one
const gameMemory = GM_getValue('gameMemory', { openingSuccess: {} });
// Find worst performing opening
let worstScore = Infinity;
let worstOpening = null;
for (const opening in gameMemory.openingSuccess) {
const stats = gameMemory.openingSuccess[opening];
const score = stats.wins - stats.losses;
if (score < worstScore && stats.wins + stats.losses + stats.draws >= 5) {
worstScore = score;
worstOpening = opening;
}
}
// Replace it if we found a bad one
if (worstOpening && worstScore < 0) {
console.log("Phasing out unprofitable opening: " + worstOpening);
// Replace with a new experimental opening
if (worstOpening.startsWith('1.')) {
// It's a white opening
myVars.openingRepertoire.white.experimental =
myVars.openingRepertoire.white.experimental.filter(o => o !== worstOpening);
myVars.openingRepertoire.white.experimental.push(getRandomOpenings(1, true)[0]);
} else {
// It's a black response
// Implementation would be similar to white
}
}
myVars.openingRepertoire.lastUpdated = Date.now();
GM_setValue('openingRepertoire', myVars.openingRepertoire);
}
// Then when evaluating positions:
function adjustForTacticalProfile(moves, position) {
for (let i = 0; i < moves.length; i++) {
const tacticalMotif = identifyTacticalMotif(moves[i], position);
// Boost strengths
if (myVars.tacticalProfile.strengths.includes(tacticalMotif)) {
// Higher chance of finding this tactic
if (i > 0 && Math.random() < 0.8) {
// Swap with the first move (more likely to be played)
[moves[0], moves[i]] = [moves[i], moves[0]];
}
}
// Miss weaknesses
if (myVars.tacticalProfile.weaknesses.includes(tacticalMotif)) {
// Higher chance of missing this tactic
if (i === 0 && moves.length > 1 && Math.random() < 0.7) {
// Swap with the second move (less likely to be played)
[moves[0], moves[1]] = [moves[1], moves[0]];
}
}
}
return moves;
}
}
//Touching below may break the script
var isThinking = false
var canGo = true;
var myTurn = false;
var board;
window.addEventListener("load", (event) => {
let currentTime = Date.now();
main();
});
function getAppropriateDepth() {
// Get player's rating if available
let playerRating = 1500; // Default
try {
const ratingEl = document.querySelector('.user-tagline-rating');
if (ratingEl) {
playerRating = parseInt(ratingEl.textContent);
}
} catch (e) {}
// Map ratings to appropriate depths
if (playerRating < 800) return Math.floor(Math.random() * 3) + 1; // 1-3
if (playerRating < 1200) return Math.floor(Math.random() * 3) + 3; // 3-5
if (playerRating < 1600) return Math.floor(Math.random() * 3) + 5; // 5-7
if (playerRating < 2000) return Math.floor(Math.random() * 3) + 7; // 7-9
if (playerRating < 2400) return Math.floor(Math.random() * 4) + 9; // 9-12
return Math.floor(Math.random() * 5) + 12; // 12-16
}
function getBezierPoint(t, p0, p1, p2, p3) {
const cX = 3 * (p1.x - p0.x);
const bX = 3 * (p2.x - p1.x) - cX;
const aX = p3.x - p0.x - cX - bX;
const cY = 3 * (p1.y - p0.y);
const bY = 3 * (p2.y - p1.y) - cY;
const aY = p3.y - p0.y - cY - bY;
const x = (aX * Math.pow(t, 3)) + (bX * Math.pow(t, 2)) + (cX * t) + p0.x;
const y = (aY * Math.pow(t, 3)) + (bY * Math.pow(t, 2)) + (cY * t) + p0.y;
return { x, y };
}
function executeMouseMovement(points, index, delay, callback) {
if (index >= points.length) {
if (callback) callback();
return;
}
// Simulate mouse movement
const point = points[index];
// In a real implementation, you would trigger actual mouse events
// For our purposes, we'll just move to the next point
setTimeout(() => {
executeMouseMovement(points, index + 1, delay, callback);
}, delay);
}
function getSquarePosition(square) {
// Convert chess notation (e.g., "e4") to screen coordinates
// This is a simplified version - in reality, you'd need to get actual board coordinates
const file = square.charCodeAt(0) - 'a'.charCodeAt(0);
const rank = parseInt(square.charAt(1)) - 1;
// Get board dimensions
const boardRect = board.getBoundingClientRect();
const squareSize = boardRect.width / 8;
// Calculate center of the square
const x = boardRect.left + (file + 0.5) * squareSize;
const y = boardRect.top + (7 - rank + 0.5) * squareSize;
return { x, y };
}