Reputation: 19
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
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
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
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
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
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