Why are my functions executing out of order at the end of this Connect Four game? It works in some browsers

42 Views Asked by At

I'm having two timing issues here, both involving the process in this game once the winning move has been made: https://codepen.io/acchang/pen/XWePpWB

Ideally, I should (1) pick the winning space (2) see the winning space filled (3) have the alert proclaim the winner.

What I see and do not like is:

*checkForWinners() runs

  • winDeclared() runs and the alert "winner" pop up first

  • Then after the alert is cleared, drawboard() runs, adding the winning piece to the gameboard.

This does not happen as badly in Firefox. The piece is added at the same time the alert pops up.

Then, in winDeclared(), I also change the display in the top right to also indicate the winner. But swapTurns() seems to execute before winDeclared().

Is that because winDeclared() is two functions deep into checkForWinners()? Is there a way to delay it?

Thanks!



let gameboard = [
                 [1,2,3,4,5,6,7],
                 [8,9,10,11,12,13,14],
                 [15,16,17,18,19,20,21],
                 [22,23,24,25,26,27,28],
                 [29,30,31,32,33,34,35],
                 [36,37,38,39,40,41,42]
                ];

let playerOne
let playerTwo
let indexPick
let availableSpots
let gameType
let playerOneTurn = true
document.getElementsByName("announcements")[0].innerHTML = "Current Player: " + whosPlaying() + " "

let itsAOnePlayerGame = true
let isThereAWinner = false

let mainDiv = document.createElement("div");
mainDiv.setAttribute('class', 'mainDiv')
document.body.append(mainDiv);

let selectorHolder = document.createElement("div") 
selectorHolder.setAttribute('class', 'selectorHolder')
selectorHolder.setAttribute('id', 'selectorHolder')
mainDiv.append(selectorHolder)

let selectorTable = document.createElement("table") 
selectorTable.setAttribute('class', 'selectorTable')
selectorTable.setAttribute('id', 'selectorTable')
selectorHolder.append(selectorTable)

function drawSelector() {  
    let selectorRow = document.createElement("tr") 
    selectorRow.setAttribute('class', 'selectorRow')
    selectorTable.append(selectorRow)

    for (i=0; i<7; i++){
        let selectorCell = document.createElement("td") 
        selectorCell.setAttribute('class', 'selectorCell')

        let innerSelectorCell = document.createElement("div") 
        innerSelectorCell.setAttribute('class', 'innerSelectorCell')
        innerSelectorCell.setAttribute('id', [i])
        selectorCell.append(innerSelectorCell)
        
        innerSelectorCell.addEventListener("mouseover", function(event) {
            if (playerOneTurn == true) {
            innerSelectorCell.classList.add('yellowBG')}
            else {innerSelectorCell.classList.add('redBG')
            }
        })

        innerSelectorCell.addEventListener("mouseout", function(event) {
            if (playerOneTurn == true) {
            innerSelectorCell.classList.remove('yellowBG')}
            else {innerSelectorCell.classList.remove('redBG')
            }
        })

        innerSelectorCell.onclick = function(){
                if (isThereAWinner == true){return}
                else {
                    indexPick = parseInt(this.id)
                    console.log(indexPick)
                    claimSpot()
                    }
        }

        selectorRow.append(selectorCell)
    }        
};


drawSelector()

// Draw Main Gameboard

let mainTable = document.createElement("table");
mainTable.setAttribute('class', 'mainTable')
mainDiv.append(mainTable)

function drawBoard() {
    for (i=0; i<gameboard.length; i++){
            let row = document.createElement("tr")
            mainTable.append(row)

                for (j=0; j<gameboard[i].length; j++){
                    let outerCell = document.createElement('td')
                    outerCell.setAttribute('class', 'outerCell')
                    row.append(outerCell)
                    let innerCell = document.createElement('div')
                    innerCell.setAttribute('class', 'innerCell')
                    innerCell.classList.add(gameboard[i][j])
                    innerCell.setAttribute('innerHTML', gameboard[i][j])
                    outerCell.append(innerCell)
                }   
            }
};

drawBoard()

function validateRadio() {
    let ele = document.getElementsByName('gameType');    
            for(i = 0; i < ele.length; i++) {
                if(ele[i].checked){
                gameType = (ele[i].value)
                beginGame()
                }
            }
};

function beginGame() {
    if (gameType == "1PEasy"){
        itsAOnePlayerGame = true
        resetBoard()
        onePlayerPickSides()
        play1PGame()
        }
    else if (gameType == "1PHard"){
        itsAOnePlayerGame = true
        resetBoard()
        onePlayerPickSides()
        play1PGame()
        }
    else if (gameType == "2P"){
        itsAOnePlayerGame = false
        resetBoard()
        twoPlayerPickSides()
        play2PGame()
        }
};

function resetBoard() {
    playerOneTurn = true
    isThereAWinner = false
    gameboard = [
        [1,2,3,4,5,6,7],
        [8,9,10,11,12,13,14],
        [15,16,17,18,19,20,21],
        [22,23,24,25,26,27,28],
        [29,30,31,32,33,34,35],
        [36,37,38,39,40,41,42]
       ];
}


function swapTurns() {
    selectorTable.innerHTML = ""
    drawSelector()
    playerOneTurn = !playerOneTurn
    document.getElementsByName("announcements")[0].innerHTML = "Current Player: " + whosPlaying() + "&nbsp;"
};

// GAMEPLAY

function playerSelects2P() {
    findAvailableSpots()

    // put an eventListener here?
    columnPick = prompt(whosPlaying() +  ', choose which column 1-7')


    if (availableSpots.includes(parseInt(columnPick))) 
        {console.log(columnPick)}
    else {
        alert("not available")
        playerSelects2P()}
};



function playerSelects1P() {
    if (whosPlaying() == playerTwo) {
        findAvailableSpots()
        columnPick = availableSpots[Math.floor(Math.random() * availableSpots.length)]
        return
    }
    else {playerSelects2P()}
};    

function whosPlaying() {
    if (playerOneTurn) {
    return "Yellow"
    } else {
    return "Red"
    }
};

// starts from the bottom row and claims spot when there it is a number (unoccupied)
function claimSpot(){
    findAvailableSpots()
    if (availableSpots.includes(indexPick+1)) {

    let i;
    for (i = 5; i > -1; i--) 
        {if (Number.isInteger(gameboard[i][indexPick])) {
            gameboard[i].splice((indexPick), 1, whosPlaying())
            mainTable.innerHTML = ""
            drawBoard()
            checkForWinners() 

            // do I need to put some sort of delay here for it not to go to swap turns right away?
            swapTurns()
            return
            }
        }
    }
    else {
        console.log(availableSpots)
        alert("Forbidden")
    }

};

// if there is a string in row[0], that column is no longer available.
// the cells are numbered from 1 to 7, not per index so you need to add one to indexPick to identify
function findAvailableSpots() {
    availableSpots = gameboard[0].filter(x => Number.isInteger(x) == true)
};


function checkForWinners() {
    horizontalCheck()
    verticalCheck()
    downrightCheck()
    uprightCheck()
}

// WIN CHECKERS
// a forloop evaluates a section of the matrix, moving through it and seeing if the 3 ahead match.
// it stops before going out of bounds

function findFour(w,x,y,z) {
    // Checks first cell against current player and all cells match that player
    return ((w == whosPlaying()) && (w === x) && (w === y) && (w === z));
};

function winDeclared() {
    isThereAWinner = true
    alert("winner")

    document.getElementsByName("announcements")[0].innerHTML = whosPlaying() + " wins!&nbsp;"
    // this does not show, it snaps to swap places
};

function uprightCheck() {
    for (r=5; r>2; r--) {
        for (c=0; c<4; c++){
            if (findFour(gameboard[r][c], gameboard[r-1][c+1], gameboard[r-2][c+2], gameboard[r-3][c+3])) {
                winDeclared()
                return
            }
        }
    }
};

function downrightCheck() {
    for (r=0; r<3; r++) {
        for (c=0; c<4; c++){
            if (findFour(gameboard[r][c], gameboard[r+1][c+1], gameboard[r+2][c+2], gameboard[r+3][c+3])) {
                winDeclared()
                return
            }
        }
    }
};


function verticalCheck() {
    for (r=5; r>2; r--) {
        for (c=0; c<7; c++){
            if (findFour(gameboard[r][c], gameboard[r-1][c], gameboard[r-2][c], gameboard[r-3][c])) {
                winDeclared()
                return
            }
        }
    }
};

function horizontalCheck() {
    for (r=0; r<6; r++) {
        for (c=0; c<4; c++){
            if (findFour(gameboard[r][c], gameboard[r][c+1], gameboard[r][c+2], gameboard[r][c+3])) {
                winDeclared()
                return
            }
        }
    }
};

1

There are 1 best solutions below

1
krasi On BEST ANSWER

When you manipulate the DOM, the operation itself is syncrhonous but the browser decides when the user will actually see the changes. Sometimes, the broswer will not have time to redraw before the prompt appears. To get around this, you can wrap the alert in a setTimeout() to delay the alert.

setTimeout(
function() {
  alert("winner")
}, 10)