4

I'm creating an 'advanced' Tic Tac Toe game in which each turn disappears after 10 seconds (ie: each X or O placed reverts to a blank square after 10 seconds). This all works fine, but the issue arises if a user decides to cancel the current game and start a new one.

If a user starts a new game and clicks on a square that was previously occupied, the timeout function will clear that square in accordance with the click from the previous game -- ie, in less than 10 seconds.

Using clearTimeout resets the timer for the most recent instance of SetTimeout, but doesn't help if there were multiple squares selected when the user reset the game board. And because setTimeout is applied to each X and O in an onclick function, I don't have a way to track multiple instance IDs.

Any thoughts would be much appreciated, code below.

EDIT: You can see a live version of the game (WIP) here: http://kylechadha.com/projects/tic-tac-toe/

Global variables:

var elements = document.getElementsByClassName('cell');
var rows = document.getElementsByClassName('row');
var alternate = 0;
var counter = 0;
var timerX; // Handles setTimeout instances for 'X'
var timerO; // Handles setTimeout instances for 'O'

Function to set X's and O's:

  var play = function() {
  for (i = 0; i < elements.length; i++) {
    elements[i].onclick = function () {
      if (this.className[0] == "c" && win == false) {
        if (alternate % 2 == 0) {
          this.className = "xmove";
          alternate++;
          counter++;
          var paramPass = this;
          timerX = setTimeout(function() {disappear(paramPass);}, 10000) // Handles ID of most recent instance of setTimeout for 'X'
        } else {
          this.className = "omove";
          alternate++;
          counter++;
          var paramPass = this;
          timerO = setTimeout(function() {disappear(paramPass);}, 10000) // Handles ID of most recent instance of setTimeout for 'O'
        }
      }
      position(this);
      analysis();
    }
  }
}

Reset function when user clicks 'New Game':

var reset = function() {
  header[0].innerHTML = "Tic Tac Toe";
  for (i = 0; i < rows.length; i++) {
    for (j = 1; j < 6; j += 2) {
      rows[i].childNodes[j].className = "cell animated bounceIn";
    }
  }
  clearTimeout(timerX); // Clears Timeout for most recent instance (last 'X' clicked) before the game was reset
  clearTimeout(timerO); // Clears Timeout for most recent instance (last 'O' clicked) before the game was reset
  board = [["","",""],["","",""],["","",""]];
  counter = 0;
  alternate = 0;
  win = false;
}
Kyle Chadha
  • 3,741
  • 2
  • 33
  • 42

2 Answers2

6

Keep a list of pending timeouts. Each timeout removes itself from the list when it triggers. When you reset, iterate over the list and clearTimeout on each.

Something like this:

var pending = {};
function mySetTimeout(callback, delay) {
  var t;
  t = setTimeout(function() {delete pending[t];callback()}, delay)
  pending[t]=1;
}
function clearAllTimeouts() {
  for (var t in pending) if (pending.hasOwnProperty(t)) {
    clearTimeout(t);
    delete pending[t];
  }
}
Adam Bliss
  • 645
  • 4
  • 9
  • 1
    You're missing a `pending[t]=true` there – user123444555621 Jan 13 '14 at 02:06
  • Unfortunately I'm getting an error "Uncaught SyntaxError: Unexpected identifier" on line 'for (var t in pending) if pending.hasOwnProperty(t) {'. What is the reason for 'if pending.hasOwnProperty(t)'? – Kyle Chadha Jan 14 '14 at 04:23
  • Oops, you need some more parens (fixed). Read about hasOwnProperty here: http://stackoverflow.com/questions/684672/loop-through-javascript-object – Adam Bliss Jan 14 '14 at 13:03
  • Got it, thanks Adam. It actually seems to be working fine without the if statement -- can you tell me why it's necessary to test 'pending' for 't'? In what situations would the if statement return false (only if no cells were clicked, I suppose)? – Kyle Chadha Jan 14 '14 at 21:28
  • Kyle--even if it works without the check, it's a good habit to always include it. Otherwise, if any other code modifies Object.constructor.prototype, your code will start to misbehave. (You can google around for more details on this topic.) – Adam Bliss Jan 15 '14 at 01:55
1

The easiest solution would be to create an array of timer IDs and then whenever you want to clear them, iterate through the array with a for loop and use clearTimeout on each. You can use Array.push to add IDs to the timer array.

K. S.
  • 586
  • 5
  • 20