Why is the last move of the previous player not displayed in this game of tic tac toe?

Why does the last move of the previous player not show up in this tic-tac-toe game? When it's the final turn, the square remains empty with neither an "O" nor an "X". What could be causing this issue?

Here is the code snippet:

The HTML code:

<div id="tictactoe" align="center"></div>
        <div align="center">
            <span id='turn'>Turn of Player X</span>
        </div>          

The CSS code:

table {
    border-collapse:collapse;
}

td {
    background-color: black;
    border: 3px solid white;
    font-size:60px;
    color:#ffffff;
    border-radius: 10px 10px 10px 10px;
}

And here is the JavaScript code:

    var N_SIZE = 3,
        EMPTY = "&nbsp;",
        boxes = [],
        turn = "X",
        score,
        moves;

    /*
     * Initializes the Tic Tac Toe board and starts the game.
     */
    function init() {
        var board = document.createElement('table');
    board.setAttribute("border", 1);
    board.setAttribute("cellspacing", 0);
    
        var identifier = 1;
        for (var i = 0; i < N_SIZE; i++) {
            var row = document.createElement('tr');
            board.appendChild(row);
            for (var j = 0; j < N_SIZE; j++) {
        var cell = document.createElement('td');
        cell.setAttribute('height', 50);
        cell.setAttribute('width', 50);
        cell.setAttribute('align', 'center');
        cell.setAttribute('valign', 'center');
                cell.classList.add('col' + j,'row' + i);
                if (i == j) {
                    cell.classList.add('diagonal0');
                }
                if (j == N_SIZE - i - 1) {
                    cell.classList.add('diagonal1');
                }
                cell.identifier = identifier;
                cell.addEventListener("click", set);
                row.appendChild(cell);
                boxes.push(cell);
                identifier += identifier;
            }
        }

        document.getElementById("tictactoe").appendChild(board);
        startNewGame();
    }

    /*
     * New game
     */
    function startNewGame() {
        score = {
            "X": 0,
            "O": 0
        };
        moves = 0;
        turn = "X";
        boxes.forEach(function (square) {
            square.innerHTML = EMPTY;
        });
    }

    /*
     * Check if a win or not
     */
    function win(clicked) {
        // Get all cell classes
        var memberOf = clicked.className.split(/\s+/);
        for (var i = 0; i < memberOf.length; i++) {
            var testClass = '.' + memberOf[i];
      var items = contains('#tictactoe ' + testClass, turn);
            // winning condition: turn == N_SIZE
            if (items.length == N_SIZE) {
                return true;
            }
        }
        return false;
    }

function contains(selector, text) {
  var elements = document.querySelectorAll(selector);
  return [].filter.call(elements, function(element){
    return RegExp(text).test(element.textContent);
  });
}

    /*
     * Sets clicked square and also updates the turn.
     */
    function set() {
        if (this.innerHTML !== EMPTY) {
            return;
        }
        this.innerHTML = turn;
        moves += 1;
        score[turn] += this.identifier;
        if (win(this)) {
            alert('Winner: Player ' + turn);
            startNewGame();
        } else if (moves === N_SIZE * N_SIZE) {
            alert("Draw");
            startNewGame();
        } else {
            turn = turn === "X" ? "O" : "X";
            document.getElementById('turn').textContent = 'Turn of Player ' + turn;
        }
    }

    init();

Check out the link to view the code: https://codepen.io/sp2012/pen/gOmXqOO

Thank you.

Answer №1

Ensure to include a timeout in the set() function before commencing a new game: setTimeout(...,0)

Feel free to refer to this code snippet:

var N_SIZE = 3,
  EMPTY = "&nbsp;",
  boxes = [],
  turn = "X",
  score,
  moves;

/*
 * Setting up the Tic Tac Toe board and initializing the game.
 */
function init() {
  var board = document.createElement('table');
  board.setAttribute("border", 1);
  board.setAttribute("cellspacing", 0);

  var identifier = 1;
  for (var i = 0; i < N_SIZE; i++) {
    var row = document.createElement('tr');
    board.appendChild(row);
    for (var j = 0; j < N_SIZE; j++) {
      var cell = document.createElement('td');
      cell.setAttribute('height', 50);
      cell.setAttribute('width', 50);
      cell.setAttribute('align', 'center');
      cell.setAttribute('valign', 'center');
      cell.classList.add('col' + j, 'row' + i);
      if (i == j) {
        cell.classList.add('diagonal0');
      }
      if (j == N_SIZE - i - 1) {
        cell.classList.add('diagonal1');
      }
      cell.identifier = identifier;
      cell.addEventListener("click", set);
      row.appendChild(cell);
      boxes.push(cell);
      identifier += identifier;
    }
  }

  document.getElementById("tictactoe").appendChild(board);
  startNewGame();
}

/*
 * Starting a fresh round
 */
function startNewGame() {
  score = {
    "X": 0,
    "O": 0
  };
  moves = 0;
  turn = "X";
  boxes.forEach(function(square) {
    square.innerHTML = EMPTY;
  });
}

/*
 * Checking for win condition
 */
function win(clicked) {
  // Retrieve all cell classes
  var memberOf = clicked.className.split(/\s+/);
  for (var i = 0; i < memberOf.length; i++) {
    var testClass = '.' + memberOf[i];
    var items = contains('#tictactoe ' + testClass, turn);
    // Winning criteria: turn == N_SIZE
    if (items.length == N_SIZE) {
      return true;
    }
  }
  return false;
}

function contains(selector, text) {
  var elements = document.querySelectorAll(selector);
  return [].filter.call(elements, function(element) {
    return RegExp(text).test(element.textContent);
  });
}

/*
 * Updating the selected square and switching turns.
 */
function set() {
  if (this.innerHTML !== EMPTY) {
    return;
  }
  this.innerHTML = turn;
  moves += 1;
  score[turn] += this.identifier;
  if (win(this)) {
    setTimeout(()=>{
      if (confirm('Winner: Player ' + turn))
        startNewGame();
    }, 0)
  } else if (moves === N_SIZE * N_SIZE) {
    setTimeout(()=>{
      if (confirm('Winner: Player ' + turn))
        startNewGame();
    }, 0)
  } else {
    turn = turn === "X" ? "O" : "X";
    document.getElementById('turn').textContent = 'Turn of Player ' + turn;
  }
}

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

td {
  background-color: black;
  border: 3px solid white;
  font-size: 60px;
  color: #ffffff;
  border-radius: 10px 10px 10px 10px;
}
<div id="tictactoe" align="center"></div>
<div align="center">
  <span id='turn'>Turn of Player X</span>
</div>

Answer №2

While verifying if the player has won, there may be a delay in the rerender process. When the alert message appears, the execution is paused momentarily and right after clicking OK, a new game begins. One workaround is to use setTimeout to display the alert after the rerender has completed.

if (checkWinCondition(this)) {
    setTimeout(function() {
      alert('Congratulations! Player ' + currentPlayer + ' has won!');
      startNewGame();
    }, 0);
} else if (moves === BOARD_SIZE * BOARD_SIZE) {
    setTimeout(function() {
       alert("It's a draw!");
       startNewGame();
    }, 0);
}

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

Implementing folder-specific routing in Express js

I'm struggling to figure out how to implement a solution that functions like this code snippet. I need to serve different folders based on various parameters, but I'm hitting a roadblock. An example of this in action would be incredibly helpful. ...

Using jQuery idle timeout to abort jQuery AJAX calls in Laravel 5.2

Currently, I have implemented the jQuery Idle Timeout plugin in my Laravel 5.2 system. Everything works perfectly on my local setup using MAMP Pro, but upon uploading it to the development server, I encountered an "Aborted" error in the AJAX get request: ...

Ways to align the `<ol>` number at the top vertically

I'm having trouble aligning my <ol> numbers vertically at the top of the list. I've created a sample on CodePen. ol { list-style-type: decimal-leading-zero; font-size: 11px; } a { font-size: 80px; tex ...

Transmit information from JavaScript to a servlet and receive a response in return

I'm new to using ajax and JavaScript. Currently, I have a simple jsp page containing HTML and an accompanying JavaScript file with all my functions defined. One of these functions sends JSON data to a servlet and now I need assistance in receiving th ...

Using Javascript to update text content by utilizing `innerText` property instead of checking if it is

How can I selectively use Javascript to replace the specific word "Rindan" in the text below, but not if it appears within an attribute such as img alt=="Rindan"? I only want to replace instances of the word "Rindans" when it is part of the inner text an ...

Zurb Foundation's sections have a strange issue where navigating between them introduces unexpected whitespace

I'm feeling frustrated as I try to work with Zurb Foundation sections. I've updated to the latest version 4.3.1. Following the documentation provided here: but encountering strange behavior while navigating through results. Refer to the screen ...

The categories in Vue are not being displayed after they have been fetched

Looking for assistance with Vue rendering. Currently, I am developing a Vue-Wordpress application and facing an issue with retrieving a list of categories for each post. The categories for every post are fetched from the WP API as their respective IDs. I ...

Can you tell me why the jquery datepicker control is only loading a portion of the CSS?

Working on a new C#/ASP.Net application and I decided to incorporate the datepicker control that I've used before. I transferred all the necessary components from my previous app to this one. The appearance of the datepicker in the old app was like th ...

Having trouble getting data to send to node.js server using $.ajax post

Trying to convert a table date into an array and send it to the server side using Ajax post for the first time. Despite following all the suggestions in previous posts, I am still struggling. Utilizing body-parser to extract data on the server side. Any he ...

Encountering an infinite $digest() loop in AngularJS 1.4.8 while using ngOptions in a select when setting the model programmatically before the option is available in ngOptions

When upgrading from version 1.2.14 to 1.4.8, I encountered a problem that worked fine in the former but resulted in an infinite $digest() loop in the latter. To demonstrate this issue, you can view the following Fiddle. The Fiddle provides a clearer visual ...

A guide to saving an ArrayBuffer as a file using JavaScript

I am currently developing a file uploader for the Meteor framework. The approach involves breaking down the file on the client side from an ArrayBuffer into small packets of 4096 bits, which are then sent to the server through a Meteor.method. The abridge ...

Instructions for running a script from a personalized NPM package

I have developed a unique NPM package that includes a script I want to run from another project. Here is an overview of my package: myScript.js console.log("The script has been executed!"); package.json { "name": "@myscope/my-script", "version": "0. ...

Utilize JavaScript in conjunction with encfs for enhanced encryption capabilities

I am attempting to access an encfs filesystem using JavaScript, but am struggling to understand the correct approach. I am utilizing the CryptoJS library for this task. The .ecnfs6.xml file contains standard settings and uses the password "123456": < ...

Trouble Logging In: User Login Issue with SailsJS and PassportJS Plugin (sails-generate-auth)

I'm currently facing an issue with user authentication in my SailsJS app using PassportJS. I followed a tutorial on setting up authentication in SailsJS using sails-generate-auth, which can be found here. The POST request seems to be routed correctl ...

MongoDB Integration of Collections - No Data Population

Having trouble merging a client and an account collection. When I use res.send(client), only the account id's are returned. Unsure how to include account information in clients. Have seen one to many solutions, but struggling with this two-way relati ...

Activate AngularJS autocomplete when populating the input field using JavaScript

I'm currently facing an issue with updating the value of an input field on a website using JavaScript. Although I can successfully update the input field's value, I find that I am unable to trigger the autocomplete feature. Interestingly, when ...

When utilizing an API to render text into a div, the offsetHeight function may return 0

I'm working with a div that displays text fetched from an API call. I'm trying to implement a See more button if the text exceeds 3 lines. Here is my approach: seeMore(){ this.setState({ seeMore: !this.state.seeMo ...

Angular failing to reflect changes in my select element according to the model

One issue I'm facing in my application is with grouped select elements that share the same options. Whenever one select element changes, it checks the others to see if the new option selected has already been chosen in any of the other selects. In suc ...

Having trouble with the Express.js app.post request functionality

I'm facing a challenge with the Express.js library. I've been attempting to set up a basic post request with the route. I have body-parser installed and am using it for the post data. If I specifically include app.post, it doesn't work, ...

Illumination scope for directional lights in three.js

In the world of three.js, calculating shadows for directional lights involves setting a range based on a bounding box that extends from the light source. This means that if I want to limit how far shadows are rendered, I need to adjust the bounding box geo ...