Home > Back-end >  Looking for a cleaner way to get a winner on a Tic Tac Toe Game
Looking for a cleaner way to get a winner on a Tic Tac Toe Game

Time:09-19

I am writing a function that takes a 2d array that represents a grid for a tic tac toe game. Initially it is [[0, 0, 0], [0, 0, 0], [0, 0, 0]] and there is player 1 which is represented as 1 and player 2 as -1.

The function to check the winner should return the winner if there is actually a winner.

My attempt is this:

  const checkIfWin = (b) => {
    if (b[0][0] == b[0][1] && b[0][1] == b[0][2]) return b[0][0]
    if (b[1][0] == b[1][1] && b[1][2] == b[1][1]) return b[1][0]
    if (b[2][0] == b[2][1] && b[2][1] == b[2][2]) return b[2][2]
    if (b[0][0] == b[1][0] && b[1][0] == b[2][0]) return b[2][0]
    if (b[0][1] == b[1][1] && b[1][1] == b[2][1]) return b[2][1]
    if (b[0][2] == b[1][2] && b[1][2] == b[2][2]) return b[2][2]
    if (b[0][0] == b[1][1] && b[1][1] == b[2][2]) return b[2][2]
    if (b[0][2] == b[1][1] && b[1][1] == b[2][0]) return b[2][0]
  }

checkIfWin([ [ 1, 0, 0 ], [ 0, 1, 0 ], [ -1, -1, 1 ] ]) // 1

It works but I don't think it is really elegant or clean and I wonder if there is a better way to check a winner and return the winner?

CodePudding user response:

  1. Use two helper functions: checkRow(i) and checkColumn(i), which check if all values are same in a given row and column respectively.
  2. For i in range [0, number of rows], use checkRow(i)
  3. For i in range [0, number of columns], use checkColumn(i)
  4. If still no winner is found, check the diagonals (for which you can use another helper function)

This is extremely simple to program, so I'll leave that up to you. But you can use this approach to break down the problem into simpler pieces.

CodePudding user response:

You could write a generic function to check how many cells in a row have the same value, given a starting cell and an offset.

  • For rows the offset would be 1.
  • For columns the offset would be the board size.
  • For diagonals the offset would be the board size plus or minus one.

const boardSize = 3;

// board state in a flat array to
// simplify jumping to the next cell
// via a given offset
const state = [
 1, 0, 0,
 1, 1, 0,
 1, 1, 0,
]

function numInARow(cellIndex, offset) {
  let value = state[cellIndex];
  let result = 1;
  while(state[cellIndex  = offset] === value) {
    result  ;
  }
  return result;
}

console.log(numInARow(0, 1)); // first row (1)
console.log(numInARow(0, 3)); // first column (3)
console.log(numInARow(0, 4)); // diagonal from top left (2)

With that established you could use a loop to check each row and column.

for(let i = 0; i < boardSize; i  ) {
  if(numInARow(state, i, 1) >= boardSize) {
    // row win
  }

  if(numInARow(state, boardSize * i >= boardSize) {
    // column win
  }

  if(numInARow(state, i, boardSize   1) >= boardSize {
    // diagonal pointing downhill to the right
  }

  if(numInARow(state, i, boardSize - 1) >= boardSize {
    // diagonal pointing uphill to the right
  }
}

The diagonals don't really need the loop if you're only checking from corner to corner, but it doesn't hurt anything to have them in there for a board this small.

Another thing to consider is that you only need to check around the last square played. My demo code only checks forward from the starting cell (to the right and down) but it could easily be tweaked to also check backwards. This would improve performance for massive board sizes because you wouldn't be checkin the entire board every time.

  • Related