Creating an AI adversary for a simple Tic Tac Toe game board: a step-by-step guide

Being new to programming, I recently completed a basic Tic Tac Toe gameboard successfully. However, as I progress to the next phase of my assignment which involves implementing an AI opponent, I encountered several challenges.

As a novice in programming, I am seeking guidance on how to enhance and modify my existing code for the regular Tic Tac Toe gameboard to incorporate an AI opponent.

The technologies used for my basic Tic Tac Toe gameboard include HTML, CSS, and JS. Below are the codes:

HTML

`<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Tic Tac Toe</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <div class="container">
    <h1>Tic Tac Toe</h1>
    <div id="board">
      <div class="square" id="0"></div>
      <div class="square" id="1"></div>
      <div class="square" id="2"></div>
      <div class="square" id="3"></div>
      <div class="square" id="4"></div>
      <div class="square" id="5"></div>
      <div class="square" id="6"></div>
      <div class="square" id="7"></div>
      <div class="square" id="8"></div>
    </div>
    <button id="reset">Reset Game</button>
    <div id="message"></div>
  </div>
  <script src="index.js"></script>
</body>
</html>`

CSS

`.container {
  text-align: center;
}

#board {
  display: flex;
  flex-wrap: wrap;
  width: 300px;
  margin: 0 auto;
}

.square {
  width: 90px;
  height: 90px;
  background-color: black;
  margin: 5px;
  border-radius: 5px;
  font-size: 50px;
  display: flex;
  justify-content: center;
  align-items: center;
  cursor: pointer;
}

.square:hover {
  background-color: whitesmoke;
}

.square.X {
  color: #ff5e5e;
}

.square.O {
  color: #0077ff;
}
`

JS

`let board = ["", "", "", "", "", "", "", "", ""];
let currentPlayer = "X";
let gameOver = false;

const winningConditions = [
  [0, 1, 2],
  [3, 4, 5],
  [6, 7, 8],
  [0, 3, 6],
  [1, 4, 7],
  [2, 5, 8],
  [0, 4, 8],
  [2, 4, 6]
];

const squares = document.querySelectorAll(".square");
const resetButton = document.querySelector("#reset");

// Add click event listener to each square
squares.forEach(square => {
  square.addEventListener("click", handleClick);
});

// Add click event listener to reset button
resetButton.addEventListener("click", resetGame);

function handleClick(event) {
  const square = event.target;
  const index = square.getAttribute("id");

  // If square is already clicked or game is over, return
  if (board[index] !== "" || gameOver) {
    return;
  }

  // Add X or O to board and update UI
  board[index] = currentPlayer;
  square.classList.add(currentPlayer);
  square.innerHTML = currentPlayer;

  // Check for winner or tie game
  checkForWinner();
  checkForTieGame();

  // Switch current player
  currentPlayer = currentPlayer === "X" ? "O" : "X";
}

function checkForWinner() {
  for (let i = 0; i < winningConditions.length; i++) {
    const [a, b, c] = winningConditions[i];
    if (board[a] === board[b] && board[b] === board[c] && board[a] !== "") {
      gameOver = true;
      highlightWinnerSquares(a, b, c);
      displayWinner(board[a]);
      break;
    }
  }
}

function checkForTieGame() {
  if (!board.includes("") && !gameOver) {
    gameOver = true;
    displayTieGame();
  }
}

function highlightWinnerSquares(a, b, c) {
  document.getElementById(a).classList.add("winner");
  document.getElementById(b).classList.add("winner");
  document.getElementById(c).classList.add("winner");
}

function displayWinner(player) {
  const message = document.getElementById("message");
  message.innerHTML = `${player} wins!`;
}

function displayTieGame() {
  const message = document.getElementById("message");
  message.innerHTML = "It's a tie game!";
}

function resetGame() {
  board = ["", "", "", "", "", "", "", "", ""];
  currentPlayer = "X";
  gameOver = false;

  squares.forEach(square => {
    square.classList.remove("X", "O", "winner");
    square.innerHTML = "";
  });

  const message = document.getElementById("message");
  message.innerHTML = "";
}`

Answer №1

Transform the code within handleClick into a separate function:

function handleClick(event) {
  const square = event.target;
  click(square);
}

function click(square) {
  const index = square.getAttribute("id");

  // If square is already clicked or game is over, exit
  if (board[index] !== "" || gameOver) {
    return;
  }

  // Place X or O on board and update UI
  board[index] = currentPlayer;
  square.classList.add(currentPlayer);
  square.innerHTML = currentPlayer;

  // Check for winner or tie game
  checkForWinner();
  checkForTieGame();

  // Switch player turn
  currentPlayer = currentPlayer === "X" ? "O" : "X";
}

Now your AI player can utilize the click function.

click(document.getElementById("4"));
, etc.

I suggest letting the AI make its moves within the click event handler (after the player's move)

function handleClick(event) {
  const playerMove = event.target;
  click(playerMove);
  const computerMove = chooseComputerMove();
  click(chooseComputerMove);
}

function chooseComputerMove() {
  // TODO: implement this method
  // determine which square the AI should play on this turn,
  // return the associated HTML element
}

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

AngularJS - Custom directive to extract a property value from an object

Currently, I am using the following for loop to retrieve the parent category: angular.forEach(queryTicketCategories, function(category) { if(category.id === $scope.ticketCategory.parentId) { $scope.parent = category; } }); I am looking fo ...

What is the correct regex expression for validating decimal numbers between 1.0 and 4.5?

I'm having trouble creating an expression to validate numbers between 1.0 to 4.5 accurately. The current expression I'm using is not working as intended: /^[1-4]{0,1}(?:[.]\d{1,2})?$/ The requirement is to only allow values between 1.0 to ...

Some of the picture remains concealed without spilling over

I've been struggling to get my logo to display properly on my website. I have managed to position it, but the image isn't fully visible. Here's a screenshot for reference: http://gyazo.com/1d23c924cdb401fc0cca76b946e57dcb There doesn't ...

What is the best way to manage the connections in my D3 tree chart?

I've been working on customizing a tool from an open source library called angular-d3-tree, but I'm struggling with getting the links to connect properly in my D3 tree structure. This is what my current tree layout looks like: https://i.stack.im ...

Having trouble with jQuery loading for the first time

I am having trouble getting the jQuery to load properly. My goal is to toggle classes on specific items identified by their ID. When an item is clicked, I want it to have the class "menu-selected" while the other item has the class "unselected." I have i ...

The error occurred while attempting to save the file to disk: 'setHeader() requires both a name and a value to be set.'

I am working on enabling image file uploads to the Node.js server in a MEAN Stack application. Utilizing ng-file-upload for the client-side angular directive has been successful so far. However, I have encountered an issue when attempting to pass the image ...

Using Vuetify to display text fields in a single row

Check out this Vue component template (View it on CODEPEN): <div class="some-class"> <v-form > <v-container @click="someMethod()"> <v-row> <v-col cols="3" sm="3" v-for="p in placeholders" ...

Tips for positioning a label within the span element wpcf7-form-control-wrap

Is it possible to place the label for input elements inside the span element generated by the cf7 shortcode? I want to achieve this in order to make the label only visible when the input field is in focus. To accomplish this, both the label and the input ...

Finding a potential data breach in Node.js, MongoDB, and Chrome, exposed passwords are

My web app is built using nodejs with mongodb as the database backend, and I am encountering an issue with Chrome browser flagging my login process via passport.js as an exposed password. How can I prevent this warning? Should I implement something like Bc ...

How can I adjust the width of a handle/thumb for NoUiSlider?

Looking to adjust the width of a NoUiSlider using CSS: .noUi-horizontal .noUi-handle { width:8px; height:25px; left: 0px; top: -8px; border: 0px solid #000000; border-radius: 0px; background: #000; cursor: default; box- ...

The React component does not trigger a re-render

Using React Redux, I am able to pass values successfully. However, the component I intend to render only does so once. Interestingly, when I save my code in VSCode, it renders again and the data displays on the page as expected. return ( <div classN ...

JSON response is not being successfully passed in PHP Ajax form validation

I've been struggling to solve my code for the past few days but haven't had any success. I'm attempting to validate my login form using AJAX, however, it seems like there's an issue with my Jquery AJAX script. Every time I try, the con ...

When an element is set to a fixed position while scrolling, it causes the element to flicker

After implementing the jQuery plugin stickyjs on my website, I noticed a strange issue. While it works perfectly fine on my Mac, when using the mouse scroll wheel on my PC, the element blinks once rapidly as it reaches the top of the page. Interestingly, d ...

Discover the job specifications pages on Dice.Com by utilizing C programming language

I have taken on a student project involving web scraping job postings from Dice.Com for analysis. My main focus is on extracting the job description, but I am struggling to figure out how to access it. With limited knowledge in HTML and C#, I find it dif ...

Icon bar struggling to contain voluminous material card content

I've incorporated Material UI cards into my project and have an icon bar at the bottom of each card. However, the media within the card is overflowing onto the icon bar, causing a layout issue as illustrated in this screenshot: Below is the code snip ...

Joi mistakenly demanding certain fields that should not be mandatory

I've encountered an issue with posts validation using @hapi/joi 17.1.1. In my schema, I have two fields: textfield and picture. Although both fields are not required, the validation is still indicating that the picture field is mandatory. posts valid ...

The AutoComplete component in React Material UI does not automatically assign the initialValue

I've encountered an issue with my form.js component. In this component, I have two states: update and create. Within the component, there's an AutoComplete component that works perfectly fine in the create state (data creation works as intended). ...

Guide on accessing CGI Script response using AJAX

Greetings, Here is the situation: I'm working on a login form. Once the submit button is clicked, it needs to trigger a CGI script called "someurl?userName=user&password=pwd". Depending on the response from the script, I should be able to navigat ...

What is the process for integrating Internet Explorer 10 compatibility into a basic VueJs component?

I am facing an issue with a VueJs component that is not rendering in IE 10. Context: The Vue component I am working on is a company events list with basic filtering and sorting functionalities. Unfortunately, IE10 support is required for this project. Eve ...

Utilizing MongoDb and Node.js for efficient data input

I am currently facing an issue while trying to input data into a mongodb collection using node.js. I believe I have the necessary access to the collection in question. var collection = db.collection("whatsGoingOnEvents"); if(collection){ console.log("hitt ...