Code viewer for World: X and O

// Some code was implemented from 
//https://editor.p5js.org/cs4all/sketches/Bk3TOJzlE
// https://github.com/processing/p5.js/wiki/Positioning-your-canvas
// refereces
// https://www.geeksforgeeks.org/p5-js-position-function/#:~:text=js%20%7C%20position()%20Function,-Improve%20Article&text=The%20position()%20function%20is,y%20position%20of%20the%20element.
// https://learn.microsoft.com/et-ee/dotnet/desktop/wpf/controls/how-to-use-the-attached-properties-of-canvas-to-position-child-elements?view=netframeworkdesktop-4.8


// P5 code

AB.world.newRun = function() {
    AB.socketStart(); // AB starts the socket
    AB.runReady = false; // set run ready to false so it the start button can work
};

function InfoScreen() {
    let text = "Welcome to X and O. To win the game you have to get 3 rows with either X or O. This is a 2 player Game. You can play On 2 different browsers or you can play on the same browser";        
    return (text); // display welcome message on splash screen 
}


//
AB.newSplash( InfoScreen() ); // displays the info screen function

$("#splash").click (function () { // when start button is clicked splash screen goes away 
   AB.removeSplash();
   AB.backgroundMusic ( music ); // Start the background music when the user interacts with the page
   AB.runReady = true; // run ready is true now because the game can run
     }
);

AB.world.nextStep = function() { 
}
//some constants used
let table; 
let p1;
let p2;
let turn;
let divbox;
let TText = "";
var ready;

var info = { //dictionary to store information to be sent via websocket
};

const soundaffect = '/uploads/destinyjames/mixkit-game-click-1114.wav'; //constant to store sound
const music = '/uploads/destinyjames/the-incident-soundroll-main-version-16775-01-32.mp3';
let height;
var canvas;

function setup() { // function called by p5 when game stats up
    height = windowHeight - 300;
    p1 = new Player("X"); //defined player 1
    p2 = new Player("O"); //defined player 2
    var canvas = createCanvas(height, height); //squares that reps board 
    var x = (windowWidth - width) / 2; // canvas positioning
    var y = (windowHeight - height) / 2; // canvas positioning
    canvas.position(x, y); //canvas position
    divbox = createDiv('').size(100, 100); //for which players turn
    table = new Board(p1, p2); //element on bord class
    ready = p1;
}

function draw() { // this function will be called automatically by p5 after set up 
    background(color("brown")); // background colour 
    AB.hideRunHeader(); // hides ancient brain controls 
    table.display(); // dispays board
    // style for player box
    divbox.style('font-size', '50px');
    divbox.style('font-family', 'sans-serif');
    divbox.style('padding-top', '25px');
    divbox.style('width', '100%');
    divbox.style('height', '100%');
    divbox.style('text-align', 'center');
    divbox.style('margin', 'auto');

    divbox.style('color', 'black');
    //  background image
    divbox.style('background-repeat', 'no-repeat');
    divbox.style('background-image', 'url(/uploads/destinyjames/1669759381.png)');
    divbox.style('background-position', 'center');
    divbox.style('background-attachment', 'fixed');
    divbox.style('background-size', 'cover');

}

function Sound(instance) { // function to play sound
    if (instance == "click") { //when ckick is passed to Sound function
        var sound = new Audio(soundaffect); // gets sound from aound file stored variable 
        sound.play(); // sound plays
    }
}

function mousePressed() { //automatically called by p5 when mouse is pressed
 // console.log ( mouseX + " " + mouseY );  // MH edit 
    if (mouseX > 0 && mouseX < 500 && mouseY > 0 && mouseY < 500 && AB.runReady === true) {
    // console.log ( "processing click" );  // MH edit 

        if (!table.winState) { // if the game has not been won 
            if (table.turn === "X") { // if it is player x's turn 
                Sound("click"); // play the sound 
                p1.select(table); // places player piece - calls select function on player one passes in bpard and updates the board
            } else if (table.turn === "O") { // if it is player o's turn
                Sound("click"); // same as above
                p2.select(table); // same as above
            }
            table.toggleTurn(); // switches players turn
        } else {
            // someone has won start a new game
            table.newGame();
            AB.socketOut("Start") // telling the socket to start again since the game has finished
        }
    }
}

// This class defines board attributes and methods
class Board {
    constructor(p1, p2) { // defines attributes of the board
        // empty array to hokd the board - gets filled in newGame function
        this.cells = []; // little blocks on the board
        this.cellSize = (width - 1) / 3; // size of little blocks

        this.p1 = p1; // player one
        this.p2 = p2; //player two
        this.turn = this.p1.s; //turn has started

        this.winState = false; // game hasn't been won
        this.resultText = ""; // text that will be updated and displayed depending on game result
        this.newGame(); // new game function
    }

    display() { //displays the board 
        let cellSize = this.cellSize; // sets little blocks size
        if (this.winState) {
            // someone has won, display the result
            textSize(20);
            textAlign(CENTER);
            text(this.resultText, width / 2, height / 2); // display result text 
            text("Click anywhere for a new game", width / 2, height / 2+30); //  new game
            var bc = color("black");
            fill(bc); // text colour teal
        } else {
            // nobody has won yet, display the board
            this.cells.forEach(function (element) {
                // draw the cell
                strokeWeight(20); //adds a border around the squares
                stroke('rgba(0, 5, 0, 0.8)'); // colour of stroke
                var c = color('brown'); // box colour
                fill(c);
                rect(element.r * cellSize, element.c * cellSize, cellSize, cellSize); //creates each cell

                c = color('black'); // piece colour
                fill(c);
                strokeWeight(10); // border is added to piece
                stroke('black)'); // colour of border
                textSize(100); // size of piece
                textAlign(CENTER);// center piece in little block
                // draw the X or O
                // centre the text in the cell
                text(element.s, element.r * cellSize + cellSize / 2, element.c * cellSize + cellSize / 2 + 35); // creates elements
                strokeWeight(0); // resets stroke to zero
            });
        }

    }

    // update the board after a turn takes place
    update(r, c, s) {
        let turn = this.turn;

        this.cells.forEach(function (element) {
            // element.c is the column and element.r is the row
            // element is the object in the game
            // if the cell's row and column match the row and column of the move
            if (element.r === r && element.c === c && element.val === 0) {
                // update the cell's text and value
                element.s = s;
                if (turn === "X") {
                    // if it's X's turn, the value is 1
                    element.val = 1;
                    // info.totalOnes++;
                    info["element"] = element;
                } else {
                    // if it's O's turn, the value is -1
                    element.val = -1;
                    // info.totalMinusOnes++;
                    info["element"] = element;
                }
            }
        });
        // check if the game has been won and who has won
        // updating the result 
        let result = this.checkResult();
        if (result === "X") {
            this.winState = true;
            this.resultText = "X wins!";
        } else if (result === "O") {
            this.winState = true;
            this.resultText = "O wins!";
        } else if (result === "tie") {
            this.winState = true;
            this.resultText = "Its a draw!";
        }
    }

    toggleTurn() {
        // if it's X's turn, make it O's turn
        if (this.turn == p1.s) {
            info["Whosturn"] = p2.s;
            this.turn = p2.s;
            ready = p2;
            TText = "Turn: " + p2.s;
        } else {
            // if it's O's turn, make it X's turn
            info["Whosturn"] = p1.s;
            this.turn = p1.s;
            ready = p1;
            TText = "Turn: " + p1.s;

        }
        info["ready"] = ready
        // console.log(turnText);
        divbox.html(TText);
        info["TText"] = TText;
        AB.socketOut(info);
    }


    // check if someone has won
    checkResult() {
        let winner;
        let p1 = this.p1;
        let p2 = this.p2;
        
        let rowSums = new Array(3);
        let colSums = new Array(3);
        let diagSums = new Array(3);
        // numOpen is the number of open cells
        let numOpen = 9;

        // initialize the rowSums, colSums, and diagSums arrays
        for (let i = 0; i < 3; i++) {
            rowSums[i] = 0
            colSums[i] = 0
            diagSums[i] = 0
        }

        // loop through the cells
        this.cells.forEach(function (element) {
            // if the cell is open, decrement numOpen
            rowSums[element.r] += element.val;
            colSums[element.col] += element.val;
            numOpen -= abs(element.val);

        
            if (abs(element.r - element.c) === 0) {
                // add the cell's value to the main diagonal sum
                diagSums[0] += element.val;
            }
            if (abs(element.r - element.c) === 2 || (element.r == 1 && element.c == 1)) {
                // add the cell's value to the secondary diagonal sum
                diagSums[1] += element.val;
            }
        });

        // check if any of the sums are 3 or -3
        rowSums.forEach(function (element) {
            if (element === 3) {
                winner = p1.s;
                // if the sum is 3, X won
                p1.win();
            }
            if (element === -3) {
                winner = p2.s;
                // if the sum is -3, O won
                p2.win();
            }
        });

        colSums.forEach(function (element) {
            if (element === 3) {
                winner = p1.s;
                p1.win();
            }
            if (element === -1 * 3) {
                winner = p2.s;
                p2.win();
            }
        });

        diagSums.forEach(function (element) {
            if (element === 3) {
                winner = p1.s;
                p1.win();
            }
            if (element === -1 * 3) {
                winner = p2.s;
                p2.win();
            }
        });

        // when all the squares are filled with no winner then its a tie
        if (numOpen === 0) {
            winner = "tie";
        }

        return winner;
    }

    // configure the board's state at the beginning of a game
    newGame() {
        console.log("p1 score" + p1.score);
        console.log("p2 score" + p2.score);
        this.winState = false;  // the game is not over
        this.turn = this.p1.s; // X goes first
        divbox.html("Turn: " + this.p1.s);

        // reset the cells
        this.cells = [];

        // create the cells
        for (let i = 0; i < 3; i++) {
            for (let j = 0; j < 3; j++) {
                this.cells.push({
                    // the row
                    "r": i,

                    // the column
                    "c": j,

                    // the player's s is X or O.
                    "s": "",

                    // the value
                    "val": 0
                });
            }
        }
    }
}

class Player {
    constructor(s) {
        // the player's s, X or O
        this.s = s;


        this.score = 0;
    }

    select(table) {
        // if it's X's turn, the value is 1
        if (table.turn == this.s) {
            console.log(table.turn, this.s)
            // cx and cy are the coordinates of the mouse
            let cx = int(Math.floor(mouseX / table.cellSize));
            let cy = int(Math.floor(mouseY / table.cellSize));

            // squaree on board gets updated
            table.update(cx, cy, this.s);

            // sending live board state to other player
            info["r"] = cx;
            info["c"] = cy;
            info["s"] = this.s;
        }
    }

    win() {
        console.log("yes");
        this.score++
    }
}

// Our socket funtion
AB.socketIn = function (info) {
    console.log(info.TText);
    // the game gets updated on both browsers
    divbox.html(info.TText);
    table.update(info.r, info.c, info.s);
    console.log(info.Whosturn);
    table.turn = info.Whosturn;
    ready = info.ready;
    if (info == "start") {
        table.newGame();
    }
};