Help With Win/Lose Logic for Tic Tac Toe Game

Hey guys, I’m trying to figure out how to write the win/lose logic for my tic tac toe game. I thought maybe storing each player move in an array and then compare those arrays to an array that has all the winning moves but I’m not sure how to go about it or if there’s a better way. I don’t want the answer but I wouldn’t mind a hint. Right now the it’s a single player game vs the computer. Player is always X and the computer is always 0. Thanks.

<body>
    <div class='container'>
        <button id='boxOne'>
        </button>
        <button id='boxTwo'>
        </button>
        <button id='boxThree'>
        </button>
        <button id='boxFour'>
        </button>
        <button id='boxFive'>
        </button>
        <button id='boxSix'>
        </button>
        <button id='boxSeven'>
        </button>
        <button id='boxEight'>
        </button>
        <button id='boxNine'>
        </button>
    </div>
    <script src="scripts.js"></script>
</body>

let buttons = document.querySelectorAll('button');
var btnsArr = Array.from(buttons);
let playerArr = [];
let compArr = [];
let winConditions = [
    ['boxOne', 'boxTwo', 'boxThree'],
    ['boxFour', 'boxFive', 'boxSix'],
    ['boxSeven', 'boxEight', 'boxNine'],
    ['boxOne', 'boxFour', 'boxSeven'],
    ['boxTwo', 'boxFive', 'boxEight'],
    ['boxThree', 'boxSix', 'boxNine'],
    ['boxOne', 'boxFive', 'boxNine'],
    ['boxTwo', 'boxFive', 'boxSeven']
];

buttons.forEach(function (btn) {

    btn.addEventListener('click', function play() {

        if (btn.innerText === "" && btn.innerText != 'O' && btn.innerText != 'X' && btnsArr.length > 1) {
            btn.innerHTML = 'X';
            btn.setAttribute('class', 'playerChose');
            var spot = btnsArr.indexOf(btn);
            btnsArr.splice(spot, 1);
            playerArr.push(btn.id);


            let compMove = btnsArr[Math.floor(Math.random() * btnsArr.length)];
            compMove.innerHTML = 'O'
            compMove.setAttribute('class', 'compChose');
            let compChose = btnsArr.indexOf(compMove);
            btnsArr.splice(compChose, 1);
            compArr.push(compMove.id);


        } else if (btn.innerText === "" && btn.innerText != 'O' && btn.innerText != 'X' && btnsArr.length == 1) {
            btn.innerHTML = 'X';
            btn.setAttribute('class', 'playerChose');
            var spot = btnsArr.indexOf(btn);
            btnsArr.splice(spot, 1);
            console.log("new game");
        }
        else {
            console.log("new move");
        }
    });
});

There are a number of ways to approach this. For me, i think you’re right: an array for each player storing their plays. The question is, what would you actually store?

Next question to consider, how might you define winConditions, in terms of the data? There are eight possible ways to win, so how might they be represented?

Finally, it becomes a question of how those two data sets intersect. How do you check if the current player matches any winCondition?

1 Like

Thanks for the reply. I have updated my code which I think answers the first and second question you provided. Am I headed in the right direction? I’m not really sure how to compare an array (playerArr, compArr) to a multidimensional array(winConditions).

1 Like

That is GREAT! You’ve answered questions one and two perfectly. You have a mechanism of storing each player’s moves, and you have a clear set of win conditions. That’s really the hardest part!

Now, it’s a matter of finding the intersection. I’ll show you some pseudocode, some language I might use in my head, to express the challenge. How you implement that is up to you. :wink:

function checkForWin(whichPlayer){
  - iterate over the winConditions array, each of which 
    represents a single winCondition.
    - For each winCondition, check if each value is present 
      anywhere in the whichPlayer. 
      - If it is not, drop out of the loop, this not a win
      - If all the values of this winCondition are present in 
        whichPlayer, then we can return a win!
  - If we loop through all the winConditions and fall out the bottom, no win.
}

So that function would be run after the player has taken their turn, *and again after the computer has taken theirs! In each case, you need to pass in the appropriate player’s array (so on the player’s turn, you’d call checkForWin(playerArr), and on the computer’s, checkForWin(compArr) ).

As I said, there are a number of ways to implement this, but the logic is pretty consistent either way - check each possible win combination and see if the player’s got it. Think of this as a nine-square bingo card - can you call BINGO!!!? lol

let buttons = document.querySelectorAll('button');
var btnsArr = Array.from(buttons);
let playerArr = [];
let compArr = [];
let winConditions = [
    ['boxOne', 'boxTwo', 'boxThree'],
    ['boxFour', 'boxFive', 'boxSix'],
    ['boxSeven', 'boxEight', 'boxNine'],
    ['boxOne', 'boxFour', 'boxSeven'],
    ['boxTwo', 'boxFive', 'boxEight'],
    ['boxThree', 'boxSix', 'boxNine'],
    ['boxOne', 'boxFive', 'boxNine'],
    ['boxThree', 'boxFive', 'boxSeven']
];

buttons.forEach(function (btn) {

    btn.addEventListener('click', function play() {

        if (btn.innerText === "" && btn.innerText != 'O' && btn.innerText != 'X' && btnsArr.length > 1) {
            btn.innerHTML = 'X';
            btn.setAttribute('class', 'playerChose');
            var spot = btnsArr.indexOf(btn);
            btnsArr.splice(spot, 1);
            playerArr.push(btn.id);
            checkForWin(playerArr);
            if (checkForWin(playerArr)) {
                console.log("winner player x");
            };

            let compMove = btnsArr[Math.floor(Math.random() * btnsArr.length)];
            compMove.innerHTML = 'O'
            compMove.setAttribute('class', 'compChose');
            let compChose = btnsArr.indexOf(compMove);
            btnsArr.splice(compChose, 1);
            compArr.push(compMove.id);
            checkForWin(compArr);
            if (checkForWin(compArr)) {
                console.log("winner player O");
            };

        } else if (btn.innerText === "" && btn.innerText != 'O' && btn.innerText != 'X' && btnsArr.length == 1) {
            btn.innerHTML = 'X';
            btn.setAttribute('class', 'playerChose');
            var spot = btnsArr.indexOf(btn);
            btnsArr.splice(spot, 1);
            playerArr.push(btn.id);
           checkForWin(playerArr);
            checkForWin(compArr);
            if (checkForWin(playerArr)) {
                console.log(" 2.winner player X");
            } else if (checkForWin(compArr)) {
                console.log("2.winner player O");
            } else {
                console.log("new game");
            }
        } else {
            console.log("try again");

        }

    });
});

function checkForWin(whichPlayer) {
    //iterate over the winConditions array, each of which 
    //represents a single winCondition.
    for (let i = 0; i < winConditions.length; i++) {
        for (var j = 0; j < winConditions[i].length; j++) {
            //console.log(winConditions[i][j]);
            //console.log(whichPlayer);
            if (whichPlayer.includes(winConditions[i][0]) && whichPlayer.includes(winConditions[i][1]) && whichPlayer.includes(winConditions[i][2]) && whichPlayer.length >= 3) {
                return true;
            } else if (!whichPlayer.includes(winConditions[i][0]) && whichPlayer.includes(winConditions[i][1]) && whichPlayer.includes(winConditions[i][2]) && whichPlayer.length >= 3) {
                break;
            } else {
                false;
            }
        }
    }
    // For each winCondition, check if each value is present 
    //anywhere in the whichPlayer. 
    // If it is not, drop out of the loop, this not a win
    // If all the values of this winCondition are present in 
    //whichPlayer, then we can return a win!
    // If we loop through all the winConditions and fall out the bottom, no win.
}

This is what I have so far…I posted earlier how I was having a tough time but I think I’ve made some progress. Now I have to continue testing it. I think I’m finally close! :slight_smile: :sweat_smile:

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.