Skip to content Skip to sidebar Skip to footer

Javascript Code To Recognize All Possible Combinations Of Winning

I am working on making a tic-tac-toe game for the science fair and I need help for recognizing all possible ways of finding a win rather than writing all of them down with brute fo

Solution 1:

Some issues:

  • border-length is not valid CSS, it shoud be border-width
  • You are not doing comparisons in this if statement, but assignments:

    if (!(id1="s1" ...
    

    Use triple equality signs ( === ) to perform (strict) string comparisons.

  • Your code would greatly improve if you would use an array instead of 9 separate variables: it will result in less duplication of almost identical code. For example, use this as your representation of the board:

    var board = Array(9);
    

    ... and put a 0 or 1 in a particular array element when the corresponding square receives and 'X' or and 'O'. For instance:

    board[0] = 1 
    
  • With the array presentation you can list a winning configuration as a triplet of indices in that array. For instance, if the values at index 0, 1 and 2 are all 1, then player 1 ('O') has won. All such presentations can be listed as follows:

    var wins = [
        [0, 1, 2],
        [3, 4, 5],
        [6, 7, 8],
        [0, 3, 6],
        [1, 4, 7],
        [2, 5, 8],
        [0, 4, 8],
        [2, 4, 6]
    ];
    

    The check for a win would be a matter of checking board entries at these indices to see they contain all 1 or all 0 (see code in snippet below).

  • It is better not to write JavaScript code in HTML onclick attributes. For one, it results again in duplication of code, but it is also harder to debug when errors happen. Overall, it is considered better to separate code (JavaScript) from layout (HTML).

    Remove the onclick attributes and use addEventListener method instead inside your script:

    Array.from(document.querySelectorAll('td a'), function (link, i) {
        link.addEventListener('click', gotClicked.bind(null, link, i));
    });
    
    functiongotClicked(link, i) {
        // From the `turn` value we can know who the current player is:  0 or 1.// We use the modulo operator to get the remainder when dividing by 2: var player = turn % 2; // alternates between 0 and 1.// Use this 0 or 1 as index in the XO string and display it.// When player is 0 this gives 'X', when 1 it gives 'O':
        link.parentNode.textContent = 'XO'[player];
        // Also put the 0 or 1 in our array
        board[i] = player;
        // Player played, so now increment `turn`.
        turn++;
    }
    

    For this to work, you should move your script to the end of your document, just before the closing </body>. Or, if you prefer it at the top, then wrap the code in:

    document.addEventListener('DOMContentLoaded', function () {
        // your code that needs access to the document elements comes here
    });
    
  • The click event handler should prevent the click from initiating a navigation. For that you can use e.preventDefault(), but you must then also get that e argument in your function.

    function gotClicked(link, i, e) { e.preventDefault(); // ... etc. }

  • Don't use innerHTML when you just want to put text. For that purpose textContent is more suited.

Here is a working snippet:

document.addEventListener('DOMContentLoaded', function () {
  // *** Initialise board with "empty" cells -- we use -1 for that:var board = [-1,-1,-1,-1,-1,-1,-1,-1,-1];
  var wins = [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8],
    [0, 3, 6],
    [1, 4, 7],
    [2, 5, 8],
    [0, 4, 8],
    [2, 4, 6] ];
  var turn = 0;
  Array.from(document.querySelectorAll('td a'), function (link, i) {
    link.addEventListener('click', gotClicked.bind(null, link, i));
  });

  functiongotClicked(link, i, e) {
    // Prevent the click on the link to initiate a navigation
    e.preventDefault();
    // *** call function to perform move: will return true when game is overif (doMove(i, 'You won!')) return;
    // ***Let AI play a move.randomAi();
  }

  // ***Separate function to perform a particular move: can be used for // both playersfunctiondoMove(i, msg) {
    // *** Get td-element that corresponds to the given indexvar td = document.querySelectorAll('td')[i];
    // From the `turn` value we can know who the current player is:  0 or 1.// We use the modulo operator to get the remainder when dividing by 2: var player = turn % 2;
    // Use this 0 or 1 as index in the XO string and display it.// When player is 0 this gives 'X', when 1 it gives 'O':
    td.textContent = 'XO'[player];
    // Also put the 0 or 1 in our array
    board[i] = player;
    // Player played, so now increment `turn`.
    turn++;
    // Now board has changed, check if there is a 3-in-a-row for this player,// *** and return it as the result of this functionif (isWinFor(player)) {
        // *** Show appropriate messagealert(msg);
        returntrue; // signify end of game
    }
    // *** Detect a drawif (turn == 9) {
        alert("It's a draw");
        returntrue; // signify end of game
    }
  }

  // *** Modified completelyfunctionrandomAi() {
    // ***Pick random number among the free cells. Make sure it is integer.var randomPick = Math.floor(Math.random()*(9-turn));
    // ***Find out its position on the boardfor (var i in board) {
        if (board[i] !== -1) continue; // used cell -- skip itif (randomPick == 0) break; // found it
        randomPick--;
    }
    // ***Perform this AI movedoMove(i, 'You lost');
  }

  functionisWinFor(player) {
    // Player is 0 or 1.// Iterate over all the 8 potential winsfor (var win of wins) {
      // `win` is a triplet of indices, which when they contain the player's value// constitute a winning position. Let's count how many of those three// have the player's value (0 or 1)var count = 0;
      for (var index of win) {
        // Is the value on the board the value that corresponds to the player?if (board[index] !== player) break; // don't bother looking further.
        count++;
      }
      // If we have a count of 3 here, it is a winning position.if (count == 3) {
        // Don't try other wins: one is enough :-)// ***Remove all hyperlinked cells -- no move should be played anymoreArray.from(document.querySelectorAll('td a'), function (link, i) {
          link.parentNode.innerHTML = ''; // remove link
        });
        returntrue;
      }
    }
  }
});
table,tr,td{
  border-style:solid;
  border-width:1px;
  text-align: center
}
/* ***Fix the size of the board */td { width: 30px; height: 30px }
<table><tr><td><ahref="#">N/A</a></td><td><ahref="#">N/A</a></td><td><ahref="#">N/A</a></td><tr/><tr><td><ahref="#">N/A</a></td><td><ahref="#">N/A</a></td><td><ahref="#">N/A</a></td><tr/><tr><td><ahref="#">N/A</a></td><td><ahref="#">N/A</a></td><td><ahref="#">N/A</a></td><tr/></table>

Explanation

In comments you asked about this piece of code:

Array.from(document.querySelectorAll('td a'), function (link, i) {
    link.addEventListener('click', gotClicked.bind(null, link, i));
});

It looks for all a elements that are descendants of td elements with querySelectorAll:

document.querySelectorAll('td a')

As the returned list is not an array, I convert it to one for easier looping, using Array.from:

Array.from(  )

The above returns array that I want to visit each element of. There are many ways to do this, but here I chose Array.from and using the callback function that can be provided to it: that callback is called for each element in the first argument.

At each call the next array element is passed to it as first argument, and a sequence number (the array index) is passed to it as second argument:

function (link, i) {   }

Within the function I call addEventListener on the element (an a element) to listen to the click event:

link.addEventListener('click',   );

This really does something similar as the onclick attribute. The second argument passed to it must be the function that will be called whenever there is a click on this particular a element. For that I reference your gotClicked function. But as I want to pass it some arguments I use bind:

gotClicked.bind(null, link, i)

The first argument of bind is whatever this should be inside gotClicked. As I don't care about that, I pass null. But I do care about the real arguments passed to gotClicked: link (the element clicked) and i (the index of that element). Note that bind does not call the function, it just creates a reference to the function already specifying what will be passed as arguments once it will get called.

Solution 2:

You can create an array of all possible winning combinations; create an array to store number at clicked element id; attach click event to each p element within for..of loop; use Array.prototype.some(), Array.prototype.every(), Array.prototype.indexOf() to check if each element in array containing clicked element digit portion of id is within an array containing a set of possible winning combinations and element .innerHTML is equal to "x"; utilize setTimeout() to allow time for last "x" to be rendered, then reset <p> elements .innerHTML to <a href='#'>N/A</a>, set array containing clicked element digit portion of id to 0.length

<!DOCTYPE html><html><head><style>table,
    tr,
    td {
      border: 1px solid #000;
    }
  </style><script>window.onload = function() {
      var winners = [
        [1, 2, 3],
        [4, 5, 6],
        [7, 8, 9],
        [1, 5, 9],
        [3, 5, 7],
        [1, 4, 7],
        [2, 5, 8],
        [3, 6, 9]
      ];
      var clicked = [];
      var p = document.querySelectorAll("table td p");
      for (el of p) {
        el.onclick = function() {
          if (this.innerHTML !== "x") {
            this.innerHTML = "x";
            clicked.push(+this.id.match(/\d/).pop());
          };
          var win = winners.some(function(winner) {
            return winner.every(function(n) {
              return clicked.indexOf(n) > -1 
                     && document.querySelector("[id$='" + n + "']")
                        .innerHTML === "x"
            })
          });
          setTimeout(function() {
            if (win) {
              alert("winner");
              clicked.length = 0;       
              for (el of p) {
                el.innerHTML = "<a href='#'>N/A</a>"
              }
            }
          }, 1000)
        }
      }
    }
  </script></head><body><table><tr><td><pid="s1"><ahref="#">N/A</a></p></td><td><pid="s2"><ahref="#">N/A</a></p></td><td><pid="s3"><ahref="#">N/A</a></p></td></tr><tr><td><pid="s4"><ahref="#">N/A</a></p></td><td><pid="s5"><ahref="#">N/A</a></p></td><td><pid="s6"><ahref="#">N/A</a></p></td></tr><tr><td><pid="s7"><ahref="#">N/A</a></p></td><td><pid="s8"><ahref="#">N/A</a></p></td><td><pid="s9"><ahref="#">N/A</a></p></td></tr></table></body></html>

Post a Comment for "Javascript Code To Recognize All Possible Combinations Of Winning"