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

What could be the reason for the undefined value of my ID retrieved from the next JS router.query upon page refresh?

When using the id obtained from the next router.query to dynamically render elements, it works fine when accessing the room from next/link. However, upon refreshing the page, an error is thrown. https://i.stack.imgur.com/yEjGS.png Below is the code snipp ...

Retrieve the value of a variable in a Bootstrap modal using Jade

I am looking to accomplish the following: On my Jade page, I have a for-loop that generates a list of items. Each item has some information displayed through Jade variables and a delete button. When this delete button is clicked, I want a Bootstrap Modal ...

Retrieve the height of a div element in an AngularJS framework, and assign this value to a corresponding attribute of another element

Is it possible to retrieve the height of an element using AngularJS and assign it to another div's attribute? In my Bootstrap website, I have a fixed sidebar that needs to stop before reaching the footer. To achieve this, I currently have to manually ...

Mastering the placement of script tags in your Next.js application with Next Script

I'm in the process of integrating a third-party script into my Next.js website. This particular script adds an iframe right below its corresponding script tag. Therefore, it is crucial for me to have precise control over the placement of the script ta ...

Issue: In an Angular electron app, a ReferenceError is thrown indicating that 'cv' is

I have been working on a face detection app using OpenCv.js within an Angular electron application. To implement this, I decided to utilize the ng-open-cv module from npm modules. However, when attempting to inject the NgOpenCVService into the constructor ...

"Upon submitting a form in React JS, the components will automatically trigger a

Within my application, there is a Mobx storage in conjunction with a modal window component. The form within the modal window allows me to collect all the properties and push them into an array named 'cart' within the storage as an object. Take a ...

Utilize Bootstrap button dropdown to automatically assign a selected value to a list item

I have a form with a select box that transforms into a bootstrap button after the page loads. However, I am unable to set the selected value for the converted bootstrap button drop-down li. <button type="button" class="btn dropdown-toggle btn-default" ...

The font in my Next.js / Tailwind CSS project starts off bold, but unexpectedly switches back to its original style

I recently integrated the Raleway font into my minimalist Next.js application with Tailwind CSS. I downloaded the font family in .ttf format and converted it to .woff2, but I'm having trouble changing the font weight using custom classes like font-bol ...

Steps to modify the button color within a sub-menu by leveraging vue js3 and element-plus

After creating a button in a sub-menu that should change color when clicked, I am struggling to make it work. Below is the code for the button: In addition to this code snippet, I have added more code which can be found here I also want to change the co ...

Incorporate an object property value into an established Angular/Node app by using the syntax " :12 " instead of just " 12 "

My current project is an Angular/Node MEAN stack application, but my primary concern revolves around JavaScript. When I receive a response object, it includes an SQL identity id console.log(response.recordset[0]); The output is "":12 I want to assign t ...

Unveiling Fresh URLs within an iFrame

Here is the current situation: www.mywebsite.com/Pagex.html - On this page, there is an iFrame with a default URL (src) from a different domain than the host page (Pagex.html). The code inside the iFrame is a user input form with a submit button. Upon su ...

Removing an event from Fullcalendar

With the help of a post on StackOverflow, I managed to modify my select: method to prevent users from adding an event on a date before NOW. However, there is a drawback. When a user clicks on an empty time slot and the system displays an alert message, th ...

My API is feeding data to the Material UI CardMedia image

Has anyone encountered a similar error while using the CardMedia API provided by Material-UI? I am currently utilizing the Card & CardMedia components from material-ui to display data fetched from an api. However, I am facing difficulty in displaying ...

Discovering identical objects in two arrays in Angular using TypeScript is a breeze

I've hit a roadblock with a TypeScript problem in my Angular service. I have an array of ingredients: private ingredients: Ingredient[] = [ new Ingredient('farina', 500), new Ingredient('burro', 80), new Ingredient('ucc ...

Utilizing Angular2 Observables for Time Interval Tracking

I'm working on a function that needs to be triggered every 500ms. My current approach in angular2 involves using intervals and observables. Here's the code snippet I've implemented so far: counter() { return Observable.create(observer =&g ...

Instructions for user to upload and play an MP4 file on their PC

For my web project that utilizes node.js, HTML, and JavaScript, I am looking to incorporate a video player element. I want users to have the ability to click a button and play an MP4 file directly on the webpage. How can I achieve this? I currently have s ...

Show drawer when modal is open in React Native

Currently, I am working on a project in react-native and facing an issue where the modal is appearing over the drawer navigator. Despite trying to adjust the zIndex property, it has not been effective. Details of my modal: <Modal visible={isVisible} ...

Steps to enable checkbox functionality in Chakra UI

I need help implementing a checkbox functionality in my project. I am using Chakra UI and trying to achieve a scenario where clicking on the parent checkbox checks all the children checkboxes, but also allows for individual selection. The challenge I am ...

Create a fresh instance of an object in node.js by utilizing the require/new method

I am encountering a beginner problem with node.js where I cannot seem to create objects using the 'new' operator in the index.js file. My goal is to define a simple Person object within a Person.js file, located in the same directory as my index ...

Updating the Background Color of a Selected Checkbox in HTML

I have a straightforward question that I've been struggling to find a simple answer for. Can anyone help me with this? Here's the checkbox code I'm working with: <input type="checkbox"> All I want to do is change the backgr ...