Search code examples
javascriptarraysconstructortic-tac-toe

How to make a 3 in a row detection system in tic tac toe?


I'm making a tic tac toe game, I've implemented marking down the fields that aren't taken. There is a turn based system, which switches the players mark after one of the fields is marked. The problem is, I have no idea how to detect 3 marks in a row or even a draw (when all of the fields are taken). The fields are taken from a Node List and then pushed into an array called gameboard, I've done that so that I can access them easier.

What should I pay the most attention to so that I can solve that? How do I come up with an idea that's possible to implement?

Here is the codePen link: https://codepen.io/-E33/pen/eYbrbaM

here is the JS code:


const gameboard = [];

//player constructor
function Player(sign) {
    this.sign = sign;
}

//field constructor
function gameboard_field(field) {
    this.field = field;
    this.taken = false;
    let self = this
    //this functions was made so that players can't play in a 
    //spot that's already taken, it also controls the 'players turn' system
    this.field.onclick = function() {
        if (self.taken === false) {
            self.field.innerHTML = t;
            self.taken = true;
        }
        if (t === player1.sign) {
            t = player2.sign
        } else if (t === player2.sign) {
            t = player1.sign
        }
    };
};

const player1 = new Player('X');
const player2 = new Player('O');

let t = player1.sign


//push a gameboard field into the gameboard array
//gameboard array was made so that we can access the fields easily
for (let i = 0; i < marker_field.length; i++) {
    let newField = new gameboard_field(marker_field[i]);
    gameboard.push(newField);
    
};```    

I tried to think of how to make the fields detect the mark of it's neighbor fields

Solution

  • Add "check_board( t, 3, 3 );" in the "onclick" function, immediately after setting the players marker and before you change the player sign. Where the 3s are the width and height of the board.

    Note: Code only works with square boards.


    /**
     * Check board after player makes their move.
     * 
     * @param string t the player characters
     * @param int width the board width
     * @param int height the board height
     * @issue SOF-77190125-1 Only works with square boards.
     */
    function check_board( t, width, height ){
      /**
       * Initialize some variables that we're going to use
       */
      let index,
      length;
    
      /**
       * @var string selector Class name for the buttons
       */
      let selector = 'marker-field';
    
      /**
       * @var array elements_data Store the HTML DOM elements
       */  
      let elements_data;
    
      /**
       * @var array board_data Store the configuration of the players placements
       * @var array winning_configurations Set of winning placement configurations
       */
      let board_data = [],
      winning_configurations = [];
    
      /**
       * Check for error in board configuration.
       */
      if( width != height ){
        throw new Error( 'Oh! So sorry, the board must be square. :(' );
      }
    
      /**
       * Get the buttons, because we need them to know who placed what where.
       */
      elements_data = document.getElementsByClassName(selector);
    
      /**
       * Get the board data. Note: We're only storing this players data.
       */
      index = 0;
      length = elements_data.length;
    
      for( ; index < length; index++ ){
        let marker_field_data = elements_data[ index ].innerText;
    
        board_data[ index ] = '';
    
        if( marker_field_data == t ){
          board_data[ index ] = t;
        }
      }
    
      /**
       * Use the board data to create an array of would-be winning configurations.
       * Note: There may be some nice mathematical way of doing this with matrices.
       * @obsolete Requirements changed
       * @url https://stackoverflow.com/questions/77189783/how-to-make-a-3-in-a-row-detection-system-in-tic-tac-toe/77190125#comment136080110_77190125
       */
      /**
       * winning_configurations = [
       * board_data[0] + board_data[1] + board_data[2],
       * board_data[3] + board_data[4] + board_data[5],
       * board_data[6] + board_data[7] + board_data[8],
       * board_data[0] + board_data[3] + board_data[6],
       * board_data[1] + board_data[4] + board_data[7],
       * board_data[2] + board_data[5] + board_data[8],
       * board_data[0] + board_data[4] + board_data[8],
       *  board_data[2] + board_data[4] + board_data[6]
       * ];
       */
    
      winning_configurations = get_winning_configurations( board_data, width, height );
    
      /**
       * If any of the winning configurations width number of the players characters, then they win.
       * Note: We've updated this portion of the script to create the 
       * string that we're looking for dynamically. This is all a bit ham-fisted.
       */
      if( winning_configurations.indexOf( new Array( width + 1 ).join( t ) ) != -1 ){
        alert( t + ' wins!');
      }
    }
    
    /**
     * Create an array of possible winning configurations.
     *
     * @param array board_data A one-dimensional array of dense board data.
     * @param int width the board width
     * @param int height the board height
     * @return array
     */
    function get_winning_configurations( board_data, width, height ){
      /**
       * Initialize some variables that we're going to use
       */
      let index,
      length;
      
      /**
       * @var array winning_configurations This is the return value.
       */
      let winning_configurations = [];
    
      /**
       * Used throughout the function to store configuration data.
       */
      let config_data;
    
      /**
       * Get the board data for the row wins
       */
      index = 0;
      length = height;
      
      for( ; index < length; index++ ){
          let _index = 0,
              _length = width;
    
          config_data = []
    
          for( ; _index < _length; _index++ ){
              config_data.push( board_data[ ( index * ( width ) ) + _index ] );
          }
    
          winning_configurations.push( config_data.join('') );
      }
      
      /**
       * Get the board data for the column wins
       */
      index = 0;
      length = width;
    
      for( ; index < length; index++ ){
          let _index = 0,
              _length = height;
    
          config_data = [];
    
          for( ; _index < _length; _index++ ){
              config_data.push( board_data[ ( index ) + ( _index * height ) ] );
          }
    
          winning_configurations .push( config_data.join('') );
      }
    
      /**
       * Get the board data for descending diagonal right win
       */
      index = 0;
      length = height;
    
      config_data = [];
    
      for( ; index < length; index++ ){
          config_data.push( board_data[ index + ( index * width ) ] );
      }
    
      winning_configurations.push( config_data.join('') );
    
      /**
       * Get the board data for ascending diagonal left win
       */
      index = height;
      length = 0;
    
      config_data = [];
    
      for( ; index > length; index-- ){
        config_data.push( board_data[ ( index - ( index * width ) ) * -1 ] );
      }
      
      winning_configurations .push( config_data.join('') );
      
      return( winning_configurations );
    }
    

    Here's some additional information that will provide some insight into how the get_winning_configurations function works. All of that arithmetic is calculating the array keys for the board_data array.

    Calculating a row of board_data array indice involves getting the consecutive number values of any row, while iterating the width and height of the board. A similar idea is used to get the column wins and diagonal wins.

    Numeric values needed for an 8 x 8 array