Connect 4 Board Evaluation for papergames

Visually shows you the best moves for both teams. Now works at the same time as the AI script I made.

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください。
// ==UserScript==
// @name         Connect 4 Board Evaluation for papergames
// @namespace    https://github.com/longkidkoolstar
// @version      0.4.1
// @description  Visually shows you the best moves for both teams. Now works at the same time as the AI script I made.
// @author       longkidkoolstar
// @license      none
// @match        https://papergames.io/*
// @icon         https://i.imgur.com/IQi878N.png
// @require      https://code.jquery.com/jquery-3.6.0.min.js
// @grant        GM.xmlHttpRequest
// @grant        GM.setValue
// @grant        GM.getValue
// ==/UserScript==

(async function() {
    'use strict';

    var username = await GM.getValue('username');
    var moveHistory = [];
    var lastBoardState = [];

    if (!username) {
        username = prompt('Please enter your Papergames username (case-sensitive):');
       await GM.setValue('username', username);
    }

    function getBoardState() {
        const boardContainer = document.querySelector(".grid.size6x7");
        if (!boardContainer) {
            console.error("Board container not found");
            return [];
        }
    
        let boardState = [];
    
        // Iterate over cells in a more flexible way
        for (let row = 1; row <= 6; row++) {
            let rowState = [];
            for (let col = 1; col <= 7; col++) {
                // Use a selector that matches the class names correctly
                const cellSelector = `.grid-item.cell-${row}-${col}`;
                const cell = boardContainer.querySelector(cellSelector);
                if (cell) {
                    // Check the circle class names to determine the cell's state
                    const circle = cell.querySelector("circle");
                    if (circle) {
                        if (circle.classList.contains("circle-dark")) {
                            rowState.push("R");
                        } else if (circle.classList.contains("circle-light")) {
                            rowState.push("Y");
                        } else {
                            rowState.push("E");
                        }
                    } else {
                        rowState.push("E");
                    }
                } else {
                    console.error(`Cell not found: ${cellSelector}`);
                    rowState.push("E");
                }
            }
            boardState.push(rowState);
        }
    
        return boardState;
    }
    
    function detectNewMove() {
        const currentBoardState = getBoardState();
        let newMove = false;
    
        for (let row = 0; row < 6; row++) {
            for (let col = 0; col < 7; col++) {
                if (lastBoardState[row] && lastBoardState[row][col] === 'E' && currentBoardState[row][col] !== 'E') {
                    moveHistory.push(col + 1);
                    newMove = true;
                }
            }
        }
    
        lastBoardState = currentBoardState;
        return newMove;
    }
    

    function getAPIEvaluation() {
        if (!detectNewMove()) return;

        let pos = moveHistory.join("");
        const apiUrl = `https://connect4.gamesolver.org/solve?pos=${pos}`;

        GM.xmlHttpRequest({
            method: "GET",
            url: apiUrl,
            onload: function(response) {
                const data = JSON.parse(response.responseText);
                displayEvaluations(data.score);
            },
            onerror: function(error) {
                console.error("API request failed:", error);
            }
        });
    }

function displayEvaluations(scores) {
    const boardContainer = document.querySelector(".grid.size6x7");
    let evalContainer = document.querySelector("#evaluation-container");

    if (!evalContainer) {
        evalContainer = document.createElement("div");
        evalContainer.id = "evaluation-container";
        evalContainer.style.display = "flex";
        evalContainer.style.justifyContent = "space-around";
        evalContainer.style.marginTop = "10px";
        boardContainer.parentNode.insertBefore(evalContainer, boardContainer.nextSibling);
    }

    // Clear existing evaluation cells
    evalContainer.innerHTML = '';

    scores.forEach((score, index) => {
        const evalCell = document.createElement("div");
        evalCell.textContent = score;
        evalCell.style.textAlign = 'center';
        evalCell.style.fontWeight = 'bold';
        evalCell.style.color = score > 0 ? 'green' : (score < 0 ? 'red' : 'black');
        evalCell.style.flexGrow = '1';
        evalCell.style.padding = '5px';
        evalContainer.appendChild(evalCell);
    });
}

function simulateCellClick(column) {
    console.log(`Attempting to click on column ${column}`);
    const boardContainer = document.querySelector(".grid.size6x7");
    if (!boardContainer) {
        console.error("Board container not found");
        return;
    }

    for (let row = 5; row >= 0; row--) {
        const cellSelector = `.cell-${row}-${column}`;
        const cell = boardContainer.querySelector(cellSelector);
        if (cell && cell.classList.contains('selectable')) {
            console.log(`Found selectable cell at row ${row}, column ${column}`);
            console.log(`Dispatching click event on row ${row}, column ${column}`);
            var event = new MouseEvent('click', {
                bubbles: true,
                cancelable: true,
            });
            cell.dispatchEvent(event);
            console.log(`Click event dispatched on row ${row}, column ${column}`);
            return;
        }
    }
    console.log(`No selectable cell found in column ${column}`);
}

    function resetVariables() {
        moveHistory = [];
        lastBoardState = [];
        console.log("Variables reset to default states");
    }
    function checkForResetButtons() {
        var playOnlineButton = document.querySelector("button.btn-secondary.flex-grow-1");
        var leaveRoomButton = document.querySelector("button.btn-light.ng-tns-c189-7");
        var customResetButton = document.querySelector("button.btn.btn-outline-dark.ng-tns-c497539356-18.ng-star-inserted");
    
        if (playOnlineButton || leaveRoomButton || customResetButton) {
            resetVariables();
        }
    }

    //Checking If the game is over so it can reset variables
setInterval(function() {
    checkForResetButtons();
}, 500);
    setInterval(getAPIEvaluation, 10);

    console.log("Modified Connect 4 script loaded and running");

    //---GUI

// Check if username is stored in local storage
var username = await GM.getValue('username');

if (!username) {
    alert('Username is not stored in local storage.');
    username = prompt('Please enter your Papergames username (case-sensitive):');
    await GM.setValue('username', username);
}

function logout() {
    GM.setValue('username', '');
    location.reload();
}

function createLogoutButton() {
    $('<button>')
        .text('Logout')
        .addClass('btn btn-secondary mb-2 ng-star-inserted')
        .css({
            position: 'fixed',
            bottom: '20px',
            right: '20px',
            zIndex: '9999',
            color: 'white'
        })
        .on('click', logout)
        .on('mouseover', function() { $(this).css('opacity', '0.5'); })
        .on('mouseout', function() { $(this).css('opacity', '1'); })
        .appendTo('body');
}

$(function() {
    createLogoutButton();

    var $dropdownContainer = $('<div>')
        .css({
            position: 'fixed',
            bottom: '20px',
            left: '20px',
            zIndex: '9998',
            backgroundColor: '#1b2837',
            border: '1px solid #18bc9c',
            borderRadius: '5px'
        })
        .appendTo('body');

    var $toggleButton = $('<button>')
        .text('Settings')
        .addClass('btn btn-secondary mb-2 ng-star-inserted')
        .css({
            padding: '5px 10px',
            border: 'none',
            backgroundColor: '#007bff',
            color: 'white',
            borderRadius: '5px'
        })
        .on('mouseover', function() { $(this).css('opacity', '0.5'); })
        .on('mouseout', function() { $(this).css('opacity', '1'); })
        .appendTo($dropdownContainer);

    var $dropdownContent = $('<div>')
        .css({
            display: 'none',
            padding: '8px'
        })
        .appendTo($dropdownContainer);

    var $autoQueueTab = $('<div>')
        .text('Auto Queue')
        .css({
            padding: '5px 0',
            cursor: 'pointer'
        })
        .appendTo($dropdownContent);

    var $autoQueueSettings = $('<div>')
        .css('padding', '10px')
        .appendTo($dropdownContent);

    var isAutoQueueOn = false;

    var $autoQueueToggleButton = $('<button>')
        .text('Auto Queue Off')
        .addClass('btn btn-secondary mb-2 ng-star-inserted')
        .css({
            marginTop: '10px',
            backgroundColor: 'red',
            color: 'white'
        })
        .on('click', toggleAutoQueue)
        .appendTo($autoQueueSettings);

    function toggleAutoQueue() {
        isAutoQueueOn = !isAutoQueueOn;
        localStorage.setItem('isToggled', isAutoQueueOn);
        $autoQueueToggleButton.text(isAutoQueueOn ? 'Auto Queue On' : 'Auto Queue Off')
            .css('backgroundColor', isAutoQueueOn ? 'green' : 'red');
    }

    function clickLeaveRoomButton() {
        $("button.btn-light.ng-tns-c189-7").click();
    }

    function clickPlayOnlineButton() {
        $("button.btn-secondary.flex-grow-1").click();
    }

    function checkButtonsPeriodically() {
        if (isAutoQueueOn) {
            clickLeaveRoomButton();
            clickPlayOnlineButton();
        }
    }

    setInterval(checkButtonsPeriodically, 1000);

    let previousNumber = null;

    function trackAndClickIfDifferent() {
        const $spanElement = $('app-count-down span');
        if ($spanElement.length) {
            const number = parseInt($spanElement.text(), 10);
            if (!isNaN(number) && previousNumber !== null && number !== previousNumber && isAutoQueueOn) {
                $spanElement.click();
            }
            previousNumber = number;
        }
    }

    setInterval(trackAndClickIfDifferent, 1000);

    $toggleButton.on('click', function() {
        $dropdownContent.toggle();
    });
});

//---GUI
})();