Home > database >  How would I write a win condition check for a Tic Tac Toe game based on using arrays?
How would I write a win condition check for a Tic Tac Toe game based on using arrays?

Time:01-10

Basically, I wrote a simple tic tac toe game. it uses a 2d array to represent the board (0 being empty, 1 being X and 2 being O)

I checked out solutions by other people but they used a whole different way to represent the board

here is the HTML

let board = [
  [0, 0, 0],
  [0, 0, 0],
  [0, 0, 0]
];
//the team symbols
let teamcross = "x";
let teamcircle = "o";
//for the tileclick function
let x = 0;
let y = 0;
var tile = "test";
//1 is Team X and 2 is Team O
var currentteam = 1;
//the tileclick function
function tileclick(tile, y, x) {
  switch (currentteam) {
    case 1:
      document.getElementById(tile).innerHTML = "x";
      board[y][x] = 1;
      console.log(board)
      currentteam = 2;
      document.getElementById("turndisplay").innerHTML = "current team is: O";
      break;
    case 2:
      document.getElementById(tile).innerHTML = "o"
      board[y][x] = 2;
      console.log(board)
      currentteam = 1;
      document.getElementById("turndisplay").innerHTML = "current team is: X";
      break;
    default:
      window.alert("something is broken. Current Team is "   currentteam);
      break;
  }

}
<table id='gameboard'>
  <tr id="toprow">
    <td  onclick="tileclick('tl',0,0)" id="tl">*</td>
    <td  onclick="tileclick('tc',0,1)" id="tc">*</td>
    <td  onclick="tileclick('tr',0,2)" id="tr">*</td>
  </tr>
  <tr id="middlerow">
    <td  onclick="tileclick('cl',1,0)" id="cl">*</td>
    <td  onclick="tileclick('cc',1,1)" id="cc">*</td>
    <td  onclick="tileclick('cr',1,2)" id="cr">*</td>
  </tr>
  <tr id="bottomrow">
    <td  onclick="tileclick('bl',2,0)" id="bl">*</td>
    <td  onclick="tileclick('bc',2,1)" id="bc">*</td>
    <td  onclick="tileclick('br',2,2)" id="br">*</td>
  </tr>

  <h3 id="turndisplay">current team is: X</h3>

CodePudding user response:

Some comments on your code:

  • x, y and tile do not need to be defined as global variables. They are defined as local variables already in your function, and that is already what you need.
  • Instead of two variables teamcross and teamcircle, just define one variable for both: teams = "XO" and then you can use currentteam-1 as an index in that string.
  • The "something is broken" case really is not needed: this can never happen.
  • Instead of a switch statement that has some code repetition, you can apply the logic for the two cases with one block of code, dynamically doing the right thing based on currentteam.
  • The HTML is missing a closing </table> tag.
  • It is not needed to give the td elements an id attribute: they are not used, and if needed you can always address then with their sequence number -- either in JavaScript or CSS.
  • Don't define onclick attributes, but add one event handler on the table level through JavaScript. That handler can then check which cell triggered the event and act accordingly.
  • There is no prevention of a user clicking a tile that was already played, replacing the previous move there.
  • Besides detecting a win, you also need to detect a draw.

As to the question itself. One of the ways to do this, is to temporarily turn your board into a string (with 9 characters, "0", "1" and "2") and test for a three-in-a-row with a regular expression.

Here is an implementation:

const board = [
  [0, 0, 0],
  [0, 0, 0],
  [0, 0, 0]
];
// the team symbols
const teams = "XO";
// 1 is Team X and 2 is Team O
let currentteam = 1;
let gameWon = false;
const output = document.getElementById("turndisplay");

document.getElementById("gameboard").addEventListener("click", function (e) {
  const tile = e.target;
  // ignore clicks when game is over or the click was not on a tile or it is occupied
  if (gameWon || !tile.classList.contains("tile") || !tile.textContent.includes("*")) return;
  // derive x and y dynamically -- no need for id attribute
  const x = tile.cellIndex;
  const y = tile.parentNode.rowIndex;
  tile.textContent = teams[currentteam-1];
  board[y][x] = currentteam;
  // Update the gameWon boolean, using a regex to test the board
  gameWon = /([12])(\1\1(...)*$|.\1.\1..$|..\1..\1|...\1...\1)/
            .test(board.flat().join(""));
  if (gameWon) {
    output.textContent = teams[currentteam-1]   " has won!";
  } else if (!board.flat().includes(0)) { // Test for a draw
    output.textContent = "It's a draw";
  } else {
    currentteam = 3 - currentteam; // toggle between 1 and 2
    output.textContent = "current team is: "   teams[currentteam-1];
  }
});
td { width: 1em }
<table id='gameboard'>
  <tr>  <!-- no need for id or onclick attributes -->
    <td >*</td>
    <td >*</td>
    <td >*</td>
  </tr>
  <tr>
    <td >*</td>
    <td >*</td>
    <td >*</td>
  </tr>
  <tr>
    <td >*</td>
    <td >*</td>
    <td >*</td>
  </tr>
</table> <!-- close the table tag! -->
<h3 id="turndisplay">current team is: X</h3>

Explanation of the regex

  • ([12]) this will match the first occurrence of "1" or "2". It is captured in a capture group so that the rest of the regex can reference that character with \1.
  • \1\1 checks whether that character appears three times in a row
  • (...)*$ checks that a multiple of three characters follow (. is any character) and then the end of the string ($). This way we know the three consecutive characters (mentioned in the previous bullet point) are on a single row.
  • |: separates an alternative
  • .\1.\1: the character repeats with one intermediate character. This could be a diagonal from top-right to bottom left, provided that we are two characters away from the end of the string:
  • ..$ matches two characters and then the end of the string
  • ..\1..\1 matches the character reoccurring with 2 intermediate characters, i.e. it is a "vertical" three-in-a-row
  • ...\1...\1 matches the character reoccurring with 3 intermediate characters, i.e. it forms a diagonal from top-left to bottom-right.

CodePudding user response:

If you want to represent the tic tac toe as a 2d array, then this is roughly what the function would look like. Consider the different cases for winning a tic tac toe game. A player must have 3 of the same characters adjacent to win. A player can also win diagonally, but only when their character is in the center.

function isGameOver() {
  // check upper row
  if (board[0][1] == board[0][0] && board[0][1] == board[0][2] && board[0][1] != 0) {
    return board[0][1]
  }
  // check lower row
  if (board[2][1] == board[2][0] && board[2][1] == board[2][2] && board[2][1] != 0) {
    return board[2][1]
  }
  // check left column
  if (board[1][0] == board[0][0] && board[1][0] == board[2][0] && board[1][0] != 0) {
    return board[1][0]
  }
  // check right column
  if (board[1][2] == board[0][2] && board[1][2] == board[2][2] && board[1][2] != 0) {
    return board[1][2]
  }
  // check center row, column, and diagonals
  if (
    board[1][1] != 0 &&
    ((board[1][1] == board[1][0] && board[1][1] == board[1][2]) ||
      (board[1][1] == board[0][1] && board[1][1] == board[2][1]) ||
      (board[1][1] == board[0][0] && board[1][1] == board[2][2]) ||
      (board[1][1] == board[2][0] && board[1][1] == board[0][2]))
  ) {
    return board[1][1]
  }

  return 0
}

  • Related