KDO
KDO

Reputation: 19

Javascript - Tic Tac Toe - How to loop through Win Condition?

I am writing a functioning Tic Tac Toe program and I am pretty much done. Except my win condition is too long and ugly. It looks like this.

function checkWin(){
  if(board[0].textContent === "X" &&
    board[1].textContent === "X" &&
    board[2].textContent === "X"
  ) { alert("Win")}
  else if (
    board[3].textContent === "X" &&
    board[4].textContent === "X" &&
    board[5].textContent === "X"
  ) { alert("Win")}
   else if (
    board[6].textContent === "X" &&
    board[7].textContent === "X" &&
    board[8].textContent === "X"
  ) { alert("Win")}

}

I only write a few win condition because if I write the whole thing it will be even longer. I was wondering how I can write a shorter version. I was thinking of doing an array and looping through it but I can't figure out how. It will be something like this.

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

but how can I use checkWin to loop through winConditions and make it equal to X and O?

Upvotes: 1

Views: 5614

Answers (5)

Scott Sauyet
Scott Sauyet

Reputation: 50787

Edit: Oops, just noticed that this was an old question that has recently been brought back up. I guess it still stands, though as a reasonable answer.


I think this does what you want, in a fairly straightforward manner.

We actually write three functions. anyWin tells us if either player has a win on the board. It calls tictactoeWin with 'X' and with 'O', returning true if either of those functions did so. tictactoeWin in turn is the result of calling win with the list of winning lines; it is a function accepting a player ('X' or 'O') and a board and returning true if that player wins on one of those winning lines. win does the bulk of the work, finding if there is some line in the winning lines for which every cell has that player for a value.

const win = (winConditions) => (player, board) =>
  winConditions .some (line => line .every (square => board [square] .textContent == player))

const tictactoeWin = win ([
  [0, 1, 2], [3, 4,5 ], [6, 7,8 ], // horizontal
  [0, 3, 6], [1, 4, 7], [2, 5, 8], // vertical
  [0, 4, 8], [6, 4, 2]             // diagonal
])

const anyWin = (board) => 
  tictactoeWin ('O', board) || tictactoeWin ('X', board)

const board = [
  {textContent: 'X'}, {textContent: ''},  {textContent: 'O'},
  {textContent: 'O'}, {textContent: 'O'}, {textContent: ''},
  {textContent: 'X'}, {textContent: 'X'}, {textContent: 'X'}
]

console .log (tictactoeWin ('O', board)) //=> false
console .log (tictactoeWin ('X', board)) //=> true
console .log (anyWin (board))            //=> true

If you wanted to report the winning line, it would be a fairly simple change to win and anyWin:

const win = (winConditions) => (
  player, 
  board,
  line = winConditions .find (line => line .every (square => board [square] .textContent == player) )
) => ({won: !!line, ... (line ? {winner: player, line} : {})})

const tictactoeWin = win ([
  [0, 1, 2], [3, 4,5 ], [6, 7,8 ], // horizontal
  [0, 3, 6], [1, 4, 7], [2, 5, 8], // vertical
  [0, 4, 8], [6, 4, 2]             // diagonal
])

const anyWin = (board, x = tictactoeWin ('X', board)) =>
  x || tictactoeWin ('O', board)


const board = [
  {textContent: 'X'}, {textContent: ''}, {textContent: 'O'},
  {textContent: 'O'}, {textContent: 'O'}, {textContent: ''},
  {textContent: 'X'}, {textContent: 'X'}, {textContent: 'X'}
]

console .log (tictactoeWin ('O', board)) //=> false
console .log (tictactoeWin ('X', board)) //=> true
console .log (anyWin (board))            //=> true
.as-console-wrapper {max-height: 100% !important; top: 0}

I have one further suggestion. You seem to be be using the DOM to store your state. I would suggest that it would make more sense to keep a logical model of this in memory, and on a change, update the model, and redraw the UI. That makes it much more robust.

Upvotes: 0

Blake
Blake

Reputation: 1

if the board is a flat array of strings, then you can do this

function checkWinner(board) {
    let combos =  [[0,1,2], [3,4,5], [6,7,8], [0,3,6], [1,4,7], [2,5,8], [0,4,8], [6,4,2]];

    let winner = '';
    // iterate though the combos and de-structure the set of 3 indices
    for(let [a, b, c] of combos) {
        if(board[a] && board[a] === board[b] && board[a] === board[c]) {
            return board[a]; // 'X' | 'O'
        }
    }
    return ''; // no winner
}

// ----------------- //
var board = ['X', 'X', '', 'O', 'X', 'O', '', '', 'X'];
let winner = checkWinner(board);
if(winner) {
    alert(winner + ' wins!');
}

Upvotes: 0

Slai
Slai

Reputation: 22876

In the lazy version, you can join them into one string and test them with RegExp:

function checkWin()
{
    var str = ""
    for (var i = 0; i < 9; i++)
        str += board[i].textContent

    if (/(X...?)\1X|^(...)*XXX|^..X.X.X/.test(str))
        alert("Win")
}

Upvotes: 0

Robert I
Robert I

Reputation: 1527

With ES6

function checkWin(player){
  // player = 'X' or 'O'

  const horizontal = [0,3,6].map(i=>{return[i,i+1,i+2]});
  const vertical = [0,1,2].map(i=>{return[i,i+3,i+6]});
  const diagonal = [[0,4,8],[2,4,6]];

  var allwins = [].concat(horizontal).concat(vertical).concat(diagonal);
  
  let res = allwins.some(indices => { 
  return board[indices[0]] == player && board[indices[1]] == player && board[indices[2]] == player})
  return res;
}
// O O O
// X X O
// X X O
var board=["X","X","O","X","X","O","O","O","O"];
console.log(checkWin("X"),"Expect: false"); // false
console.log(checkWin("O"),"Expect: true"); // true

// negative test case
var board = [];
console.log(checkWin("X"),"Expect: false"); // false

// wierd case of only 1 player
// X X X
var board = ["X","X","X"]
console.log(checkWin("X"),"Expect: true"); //true

Upvotes: 2

Hefeust
Hefeust

Reputation: 621

Doing loops and sums. of course, this can be optimized :

var winConditions = [ /* what you defined */ ];

function winTest(board, side) {
  for(var i = 0; i < winConditions.length; i++) { 
    var sum = 0;
    var w = winConditions[i];

    for(var b = 0; b < w.length; b++) {
      if(board[w[b]].textcontent === side) {
        sum++
      }
    }

    if(sum === 3) {
      return true;
    }
  }
  return false;
}

// then, call it :
var result = winTest(board, 'X');

Upvotes: 0

Related Questions