Alpha-Beta Pruning Minimax Algorithm Fails to Determine the Best Move in Tic-Tac-Toe Artificial Intelligence

I have been developing a Tic-Tac-Toe AI system that uses the Alpha-Beta Pruning Minimax algorithm to determine the best move for the AI player (X) on the game board. Unfortunately, I am running into an issue where the algorithm is not returning the correct optimal move index.

According to my calculations, the ideal move for the AI player should be at index 4. However, when I run the minimax function, it returns bestAIMove.index = 8 instead.

Below is the code snippet I have been working on:

let humanPlayer = "O";
let aiPlayer = "X";
let origBoard = ["X", "O", 2, "X", 4, 5, "O", 7, 8];
let MAX = {index: 99, score: 1000};
let MIN = {index: 99, score: -1000}
let fc = 0;

function checkAvailableMoves(board) {
    return board.filter(s => s !== "O" && s !== "X");
}

function winning(board, player) {
    const winningCombinations = [
        [0, 1, 2],
        [3, 4, 5],
        [6, 7, 8],
        [0, 3, 6],
        [1, 4, 7],
        [2, 5, 8],
        [0, 4, 8],
        [2, 4, 6]
    ];
    return winningCombinations.some(combination =>
        combination.every(cell => board[cell] === player)
    );
}

function max(a,b) {return a.score > b.score ? a : b;}
function min(a,b) {return a.score < b.score ? a : b;}

function minimax(newBoard, depth, player, alpha, beta) {
    const availableMoves = checkAvailableMoves(newBoard);
    let theBestMove = {};
    fc++
    if (winning(newBoard, humanPlayer)) {return { score: -10 + depth }}
    else if (winning(newBoard, aiPlayer)) {return { score: 10 - depth }}
    else if (availableMoves.length === 0) {return { score: 0 }};

    if (player === aiPlayer) {
        for (let i = 0; i < availableMoves.length; i++) {
            const index = availableMoves[i];
            newBoard[index] = player;
            let result = minimax(newBoard, depth + 1, humanPlayer, alpha, beta);
            result.index = index;
            alpha = max(alpha,result)
            newBoard[index] = index;
            if (alpha.score >= beta.score) {break}
        }
        theBestMove = alpha;
    } else if (player === humanPlayer) {
        for (let i = 0; i < availableMoves.length; i++) {
            const index = availableMoves[i];
            newBoard[index] = player;
            let result = minimax(newBoard, depth + 1, aiPlayer, alpha, beta);
            result.index = index;
            beta = min(beta, result);
            newBoard[index] = index;
            if (alpha.score >= beta.score){break}
        }
        theBestMove = beta;
    }
    return theBestMove;
}

bestAIMove = minimax(origBoard,0,aiPlayer,MIN,MAX)
console.log(bestAIMove)
console.log(fc)

Could you please help me identify what might be causing this problem?

Answer №1

There are a couple of issues present in your code that need to be addressed:

  1. Your use of the min and max functions may result in prioritizing the wrong values when scores are equal. To ensure a correct order, consider changing the parameters passed to these functions or modifying them to prioritize specific moves (like alpha or beta) over others.

  2. The line result.index = index modifies an object that should ideally remain immutable. Instead of directly mutating the object, opt for something like result = {...result, index} to preserve immutability.

Implementing these adjustments will resolve the issues in your code.

Demo

Here's the corrected version of your code with a human player making the first move in an interactive snippet:

const humanPlayer = "X";
const aiPlayer = "O";
const MAX = {
  index: 99,
  score: 1000
};
const MIN = {
  index: 99,
  score: -1000
}

// Rest of the JavaScript code goes here...

function game() {
  const board = [...Array(9).keys()];
  if (aiPlayer === "X") playAiMove(board);
  else listen(board);

}
game();
table {
  border-collapse: collapse
}

td {
  border: 1px solid;
  width: 25px;
  height: 25px;
  text-align: center
}
<table>
  <tr>
    <td></td>
    <td></td>
    <td></td>
  </tr>
  <tr>
    <td></td>
    <td></td>
    <td></td>
  </tr>
  <tr>
    <td></td>
    <td></td>
    <td></td>
  </tr>
</table>
<div></div>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Steps to update the active state in the "reducer" when clicking on elements outside of the "reducer" area

Working with react-redux has presented some challenges for me. I've created multiple reducers such as action, root reducer, active_step reducer, and list_step reducer. One aspect that I find intriguing is the ability to dynamically map and change the ...

There appears to be an issue with Javascript's ability to handle JSON API requests

I'm currently working on a webpage that utilizes the openweathermap API to showcase the user's city and local temperature. Unfortunately, I'm encountering an issue where the JSON API is not being processed and nothing is happening. Despite r ...

Tips for accessing user-defined headers within CORS middleware

I've developed a CORS middleware utilizing the CORS package. This middleware is invoked before each request. Here's how I implemented it: const corsMiddleware = async (req, callback) => { const { userid } = req.headers|| req.cookies {}; l ...

After removing an element from an array in Vue.js, how can we adjust its class to reflect the change?

Apologies for my lack of proficiency in English. I am eager to find a solution to resolve these issues. I am working on a todolist and encountering an issue where the class ('centerLine') continues to affect the next element after deleting an a ...

GUI interface for interactive three.js fragment shaders

I am currently experimenting with the three.js webGL shader example and am attempting to implement a GUI that can dynamically control the parameters of the shader in real time. Is this achievable? It appears that when I place the effectController variable ...

The div element is not adjusting its size according to the content it

Essentially, I want the #main-content div to expand so that its content fits inside without overlapping, as shown in the codepen example. I've been unsuccessful in implementing the clearfix or overflow:hidden solutions so far. It's puzzling why ...

Creating an NPM package using Visual Studio 2017: A Step-by-Step Guide

I enjoy developing and publishing NPM packages using Visual Studio 2017. My preferred method is using TypeScript to generate the JavaScript files. Which project template would be best suited for this particular project? ...

Steps for incrementing a number in an integer field with Node.js and MongoDB

I have a dataset that looks like this: { "_id": "6137392141bbb7723", "email": "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="95f7e7fafafef0d5f6f4f2f9f0bbf6faf8">[email protected]</a>", ...

extract data from text node using selenium

Currently, I am working on a web application and testing it with Selenium. Within my code, there is a specific node that I am trying to extract data from. <span>Profile Run <span class="current">23</span> of 29</span> My main obje ...

Setting radio button selection programmatically in React Material-UI: A step-by-step guide

Currently utilizing a radio button group from material-ui. I have successfully set a default selection using defaultSelected, however, after rendering, I am unable to programmatically change it. The selection only updates upon clicking the radio. Is ther ...

What is the best way to implement a dynamic sidebar component using React and Material UI?

I have been attempting to implement a responsive sidebar using React Material Design, but I am struggling to achieve the desired outcome. The main goal is for the page content to remain responsive when the sidebar is opened, without overlapping on the pag ...

What would be the ideal labels for the parameters within Array.reduce?

When it comes to enhancing code readability, what naming convention should be employed when naming callback arguments in Array.reduce for optimal best practices? const studentAges= [15,16,14,15,14,20] Generalized Approach const sum = studentAges.reduce ...

When running through Selenium web driver, JS produces inaccurate results

Currently, I am utilizing JavaScript to determine the number of classes of a specific type. However, when I run the JS code in Webdriver, it provides me with an incorrect value. Surprisingly, when I execute the same JavaScript on the Firebug console, it gi ...

Establishing header cache control within the KOA framework

I'm currently working on an app that is using the KOA framework, and I am struggling to understand why a specific page is being cached. Even after attempting a hard reload in all browsers, the changes do not appear unless the cache is cleared. I woul ...

Importing vs Referencing Videos in Next.js - What is the Best Practice for Video Integration in Next.js?

I'm looking to use a video as a background in my banner design. Typically, with images in Next.js, I would import them and then pass the import as src (for example: import img from '@images/about-us-page/img.svg'). However, when attempting ...

What is the best method to reset numerous select boxes within a form using jQuery?

What is the best way to reset multiple select boxes within a dynamically generated form using jQuery? There are several select boxes that may have selected options The select boxes are not known in advance as they are generated dynamically Some option ta ...

Turn off the ability to click on images, but allow users to access the right

https://i.stack.imgur.com/jriaP.png Example image sourced from reddit.com Illustration represents the desired effect using CSS and possibly JS. In essence: I aim to make an image on a website unclickable to its imageURL There should be a context menu ...

"Prevent users from taking screenshots by disabling the print screen key

Can someone help me figure out how to prevent users from taking a screenshot of my website by disabling the print screen key? Here's the code I've tried so far: <SCRIPT type="text/javascript"> focusInput = function() { document.focus() ...

Step-by-step guide to implementing dynamic field autocomplete using AJAX techniques

Seeking assistance in merging ajax autocomplete with dynamic field input. The autocomplete feature is currently working on the first field, but when adding another field, the autocomplete stops functioning. Any help would be greatly appreciated. Here is t ...

Combining TypeScript and JavaScript for efficient mixins

I came across an article on MDN discussing the usage and creation of mix-ins (link). Intrigued, I decided to try implementing it in TypeScript: type Constructor = new (...args: any) => any; function nameMixin(Base: Constructor) { return class extends ...