// 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();
}
};