Tic Tac Toe AI for papergames

Adds an AI player to Tic Tac Toe on papergames.io

目前為 2023-08-31 提交的版本,檢視 最新版本

// ==UserScript==
// @name         Tic Tac Toe AI for papergames
// @namespace    https://github.com/longkidkoolstar
// @version      0.1
// @description  Adds an AI player to Tic Tac Toe on papergames.io
// @author       longkidkoolstar
// @match        https://papergames.io/*
// @license      none 
// @grant        none
// ==/UserScript==

(function() {
    'use strict';
    var depth = 5; // Set the desired depth value


    function getBoardState() {
        var boardState = [];
        var rows = document.querySelectorAll("#tic-tac-toe table tr");
        rows.forEach(function(row) {
            var cells = row.querySelectorAll("td");
            var rowState = [];
            cells.forEach(function(cell) {
                var svg = cell.querySelector("svg");
                if (svg) {
                    var label = svg.getAttribute("aria-label");
                    rowState.push(label === 'O' ? 'o' : 'x');
                } else {
                    rowState.push('_'); // An empty cell
                }
            });
            boardState.push(rowState);
        });
        return boardState;
    }

    function simulateCellClick(cell) {
        var event = new MouseEvent('click', {
            bubbles: true,
            cancelable: true,
            view: null // Pass null as the view parameter
        });
        cell.dispatchEvent(event);
    }

    var prevChronometerValue = null;


      // Check if username is stored in local storage
      var username = localStorage.getItem('username');

      if (!username) {
          // Alert the user
          alert('Username is not stored in local storage.');
      
          // Prompt the user to enter the username
          username = prompt('Please enter your Papergames username (case-sensitive):');
      
          // Save the username to local storage
          localStorage.setItem('username', username);
      }
      
function logout() {
        localStorage.removeItem('username');
        location.reload();
    }

    function createLogoutButton() {
        var logoutButton = document.createElement('button');
        logoutButton.textContent = 'Logout';
        logoutButton.style.position = 'fixed';
        logoutButton.style.bottom = '20px';
        logoutButton.style.right = '20px';
        logoutButton.style.zIndex = '9999';
        logoutButton.style.color = 'white'; // Set the text color to white
        logoutButton.classList.add('btn', 'btn-secondary', 'mb-2', 'ng-star-inserted');
        logoutButton.addEventListener('click', logout);
        logoutButton.addEventListener('mouseover', function() {
            logoutButton.style.opacity = '0.5'; // Dim the button when hovered over
        });
        logoutButton.addEventListener('mouseout', function() {
            logoutButton.style.opacity = '1'; // Restore the button opacity when mouse leaves
        });
        document.body.appendChild(logoutButton);
    }
    createLogoutButton();
//------------------------------------------------

function toggleButtonClick() {
    var leaveRoomButton = document.querySelector("button.btn-light.ng-tns-c189-7");
    var playOnlineButton = document.querySelector("button.btn-secondary.flex-grow-1");

    if (leaveRoomButton) {
        leaveRoomButton.click();
    }

    if (playOnlineButton) {
        playOnlineButton.click();
    }

    // Toggle the state
    var isToggled = localStorage.getItem('isToggled');
    isToggled = isToggled === 'true' ? 'false' : 'true';
    localStorage.setItem('isToggled', isToggled);

    // Update the button text and style based on the state
    toggleButton.textContent = isToggled === 'true' ? 'On' : 'Off';
    toggleButton.style.backgroundColor = isToggled === 'true' ? 'green' : 'red';

    if (isToggled === 'true') {
        setInterval(function() {
            var leaveRoomButton = document.querySelector("button.btn-light.ng-tns-c189-7");
            var playOnlineButton = document.querySelector("button.btn-secondary.flex-grow-1");

            if (leaveRoomButton) {
                leaveRoomButton.click();
            }

            if (playOnlineButton) {
                playOnlineButton.click();
            }
        }, 1000);
    }
}



var toggleButton = document.createElement('button');
toggleButton.textContent = 'Off';
toggleButton.style.position = 'fixed';
toggleButton.style.bottom = '60px';
toggleButton.style.right = '20px';
toggleButton.style.zIndex = '9999';
toggleButton.style.color = 'white';
toggleButton.classList.add('btn', 'btn-primary', 'mb-2', 'ng-star-inserted');
toggleButton.addEventListener('click', toggleButtonClick);
document.body.appendChild(toggleButton);

// Check if the toggle state is stored in local storage
var isToggled = localStorage.getItem('isToggled');

// If the toggle state is not stored, set it to 'false'
if (!isToggled) {
    localStorage.setItem('isToggled', 'false');
} else {
    // Update the button text and style based on the stored state
    toggleButton.textContent = isToggled === 'true' ? 'On' : 'Off';
    toggleButton.style.backgroundColor = isToggled === 'true' ? 'green' : 'red';
}

//------------------------------------------------
        


function updateBoard(squareId) {
            var row = parseInt(squareId[0]);
            var col = parseInt(squareId[1]);
            var cell = document.querySelector("#tic-tac-toe table tr:nth-child(" + (row + 1) + ") td:nth-child(" + (col + 1) + ")");
            console.log("Selected Cell: ", cell); // Debug log for the selected cell
        
            var profileOpeners = document.querySelectorAll(".text-truncate.cursor-pointer");
            var profileOpener = null;
        
            profileOpeners.forEach(function(opener) {
                if (opener.textContent.trim() === username) {
                    profileOpener = opener;
                }
            });
       
            console.log("Profile Opener: ", profileOpener); // Debug log for the profile opener element
    
            var chronometer = document.querySelector("app-chronometer");
            console.log("Chronometer Element: ", chronometer); // Debug log for the chronometer element
        
            var numberElement = profileOpener.parentNode.querySelectorAll("span[_ngcontent-serverapp-c155]")[2]; // Select the third element with the number
            var profileOpenerParent = profileOpener.parentNode.parentNode;
            console.log("Profile Opener Parent: ", profileOpenerParent); // Debug log for the profile opener parent element
                                                var svgElement = profileOpenerParent.querySelector("circle[_ngcontent-serverApp-c176][cx='50'][cy='50'][r='35'][class='shape circle-dark-stroked']");
                        if (!svgElement) {
                            svgElement = profileOpenerParent.querySelector("svg[role='img'][aria-hidden='true'][focusable='false'][data-prefix='fas'][data-icon='xmark'][class='svg-inline--fa fa-xmark']");
                        }
                        if (svgElement && svgElement === profileOpenerParent.querySelector("circle[_ngcontent-serverApp-c176][cx='50'][cy='50'][r='35'][class='shape circle-dark-stroked']")) {
                            
                            player = 'o'; // Player is playing as "O"
                        }
            if (svgElement == profileOpenerParent.querySelector("svg[role='img'][aria-hidden='true'][focusable='false'][data-prefix='fas'][data-icon='xmark'][class='svg-inline--fa fa-xmark']")) {
                player = 'x'; // Player is playing as "X"
            }
            console.log("svgElement", svgElement);

            console.log("Number Element: ", numberElement); // Debug log for the number element
            var currentElement = chronometer || numberElement; // Use chronometer if it exists, otherwise use the number element
            console.log("Current Element: ", currentElement); // Debug log for the current element

            console.log("Cell: ", cell);
            console.log("Current Element: ", currentElement);

            if (cell && currentElement.textContent !== prevChronometerValue && profileOpener) {
                prevChronometerValue = currentElement.textContent;
                simulateCellClick(cell);
            } else {
                console.log("Waiting for AI's turn...");
            }
        
            return player;
        }

var player;



    function initGame() {
        var observer = new MutationObserver(function(mutations) {
            mutations.forEach(function(mutation) {
                if (mutation.target.id === 'tic-tac-toe-board') {
                    initAITurn();
                }
            });
        });
    
        observer.observe(document.getElementById('tic-tac-toe-board'), { attributes: true, childList: true, subtree: true });
    }
console.log(player)

function initAITurn() {
    displayBoardAndPlayer();
    var boardState = getBoardState();
    var bestMove = findBestMove(boardState, player);
    updateBoard(bestMove.row.toString() + bestMove.col.toString());
}

function findBestMove(board, player) {
    console.log("Current player: " + player); // Debug statement to show the value of the player variable

    var bestVal = -1000;
    var bestMove = { row: -1, col: -1 };

    for (var i = 0; i < 3; i++) {
        for (var j = 0; j < 3; j++) {
            if (board[i][j] === '_') {
                board[i][j] = player;
                var moveVal = minimax(board, 0, false, depth);
                board[i][j] = '_';

                if (moveVal > bestVal) {
                    bestMove.row = i;
                    bestMove.col = j;
                    bestVal = moveVal;
                }
            }
        }
    }

    console.log("The value of the best Move is: " + bestVal);
    return bestMove;
}
    
    function displayBoardAndPlayer() {
        var boardState = getBoardState();
        //console.log("AI Player: " + player);
        console.log("Board State:");
        boardState.forEach(function(row) {
            console.log(row.join(' | '));
        });
    }

    function getOpponent(player) {
        return player === 'x' ? 'o' : 'x';
    }
    function minimax(board, depth, isMaximizingPlayer, maxDepth) {
        var score = evaluateBoard(board);
    
        if (depth === maxDepth) {
            return evaluateBoard(board);
        }
    
        if (score === 10)
            return score - depth;
    
        if (score === -10)
            return score + depth;
    
        if (areMovesLeft(board) === false)
            return 0;
    
        if (isMaximizingPlayer) {
            var best = -1000;
    
            for (var i = 0; i < 3; i++) {
                for (var j = 0; j < 3; j++) {
                    if (board[i][j] === '_') {
                        board[i][j] = player; // AI places the current player's symbol
                        best = Math.max(best, minimax(board, depth + 1, !isMaximizingPlayer));
                        board[i][j] = '_';
                    }
                }
            }
            return best;
        } else {
            var best = 1000;
    
            for (var i = 0; i < 3; i++) {
                for (var j = 0; j < 3; j++) {
                    if (board[i][j] === '_') {
                        board[i][j] = getOpponent(player); // Opponent places the opposite symbol of the current player
                        best = Math.min(best, minimax(board, depth + 1, !isMaximizingPlayer));
                        board[i][j] = '_';
                    }
                }
            }
            return best;
        }
    }
    function evaluateBoard(board) {
        // Check rows for victory
        for (let row = 0; row < 3; row++) {
            if (board[row][0] === board[row][1] && board[row][1] === board[row][2]) {
                if (board[row][0] === player) return +10;
                else if (board[row][0] !== '_') return -10;
            }
        }
    
        // Check columns for victory
        for (let col = 0; col < 3; col++) {
            if (board[0][col] === board[1][col] && board[1][col] === board[2][col]) {
                if (board[0][col] === player) return +10;
                else if (board[0][col] !== '_') return -10;
            }
        }
    
        // Check diagonals for victory
        if (board[0][0] === board[1][1] && board[1][1] === board[2][2]) {
            if (board[0][0] === player) return +10;
            else if (board[0][0] !== '_') return -10;
        }
    
        if (board[0][2] === board[1][1] && board[1][1] === board[2][0]) {
            if (board[0][2] === player) return +10;
            else if (board[0][2] !== '_') return -10;
        }
    
        // If no one has won, return 0
        return 0;
    }
    
    function areMovesLeft(board) {
        for (let i = 0; i < 3; i++) {
            for (let j = 0; j < 3; j++) {
                if (board[i][j] === '_') return true;
            }
        }
        return false;
    }

    setInterval(function() {
        initAITurn();
    }, 1000);

    document.addEventListener('DOMContentLoaded', function() {
        initGame();
        console.log(player);
    });
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址