// Cloned by Niall Kelly on 29 Nov 2022 from World "Tic-Tac-Toe" by Scott Brady
// Please leave this clone trail here.
// Adapted from https://editor.p5js.org/cs4all/sketches/Bk3TOJzlE
// positioning information from https://github.com/processing/p5.js/wiki/Positioning-your-canvas
//---- normal P5 code -------------------------------------------------------
AB.world.newRun = function() {
AB.socketStart(); // starts socket at game start
AB.runReady = false; // sets run ready to false to allow for splash screen and to press start
};
function splashScreen() {
let text = "Welcome to Tic Tac Toe. The aim of the game is to fill a row, diagonal or column with your symbol, X or O. First to do so is the winner.";
return (text); // display welcome message on splash screen
}
function errorSplashScreen() {
let text = "The game is full. Please try again later.";
return (text); // display welcome message on splash screen
}
AB.newSplash( splashScreen() ); // calls splash screen function (makes it display)
$("#splashbutton").click (function () { // when start button is clicked splash screen goes away
AB.removeSplash();
AB.backgroundMusic ( MUSICFILE );
AB.runReady = true; // game is now ready to run (place symbols)
if (AB.socket) { // redundant
if (AB.socket.connected) {
console.log("SOCKET CONNECTED");
// AB.socketOut("TESTING SOCKETS!!!");
}
}
});
AB.world.nextStep = function() { //redundant
}
//constants used in game
let board;
let p1;
let p2;
let turn;
let currentTurnDiv;
let turnText = "";
var cnv;
let height;
var activePlayer;
var data = { //dictionary to store information to be sent via websocket
};
const POP_SOUND = '/uploads/mrprice/mixkit-game-ball-tap-2073.wav'; //constant to store sound
const MUSICFILE = '/uploads/mrprice/elevator_music.mp3';
function setup() { // function called by p5 when game stats up
height = windowHeight - 300;
p1 = new Player("X"); //define player 1
p2 = new Player("O"); //define player 2
var cnv = createCanvas(height, height); //square that reps board
var x = (windowWidth - width) / 2; //x canvas position
var y = (windowHeight - height) / 2; //y canvas position
cnv.position(x, y); //canvas position
currentTurnDiv = createDiv('').size(180, 100); //defines player turn aka who goes also background
board = new Board(p1, p2); //instance on board class
activePlayer = p1;
}
// function centerCanvas() {
// var x = (windowWidth - width) / 2;
// var y = (windowHeight - height) / 2;
// cnv.position(x, y);
// }
// function windowResized() {
// height = windowHeight - 300
// resizeCanvas(height, height)
// }
function draw() { // called automatically by p5 after setup
background(color("yellow")); // background colour of the win screen
AB.hideRunHeader(); // hides ancient brain controls
board.display(); // dispays board
// style player turn text
currentTurnDiv.style('font-size', '50px');
currentTurnDiv.style('font-family', 'sans-serif');
currentTurnDiv.style('padding-top', '25px');
currentTurnDiv.style('margin', 'auto');
currentTurnDiv.style('width', '100%');
currentTurnDiv.style('height', '100%');
currentTurnDiv.style('color', 'white');
currentTurnDiv.style('text-align', 'center');
// set the currentTurnDiv background image
currentTurnDiv.style('background-image', 'url(/uploads/mrprice/bg.jpg)');
currentTurnDiv.style('background-size', 'cover');
currentTurnDiv.style('background-position', 'center');
currentTurnDiv.style('background-repeat', 'no-repeat');
currentTurnDiv.style('background-attachment', 'fixed');
}
function playSound(instance) { // function to play sound
if (instance == "pop") { //if pop is passed to playSound function
var sound = new Audio(POP_SOUND); // gets sound from aound file stored variable
sound.play(); // sound plays
}
}
function mousePressed() { //automatically called by p5 when mouse is pressed
// check if mouse is in the board and game is ready to run
if (mouseX > 0 && mouseX < height && mouseY > 0 && mouseY < height && AB.runReady === true) {
if (!board.winState) { // if the game has not been won
if (board.turn === "X") { // if it is player x's turn
playSound("pop"); // play the sound
p1.select(board); // places player piece - calls select function on player one passes in bpard and updates the board
} else if (board.turn === "O") { // if it is player o's turn
playSound("pop"); // same as above
p2.select(board); // same as above
}
board.toggleTurn(); // switches player turn ie x to o
} else {
// someone has won start a new game
board.newGame();
}
}
}
// board attributes & 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.symbol; //initialises starting turn
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(40);
textAlign(CENTER);
text(this.resultText, width / 2, height / 2); // display result text
text("Click anywhere for a new game", width / 2, height - 200); // promts user for new game
var bc = color("teal");
fill(bc); // text colour teal
} else {
// nobody has won yet, display the board
this.cells.forEach(function (element) {
// draw the cell
strokeWeight(25); //adds border for 3d appearance
stroke('rgba(0, 80, 80, 0.8)'); // colour of stroke
var c = color('teal'); // cell colour
fill(c);
rect(element.row * cellSize, element.col * cellSize, cellSize, cellSize); //creates each cell
c = color('yellow'); // set piece colour
fill(c);
strokeWeight(10); // adds border to piece
stroke('rgba(173, 167, 0, 0.8)'); // colour of border
textSize(168); // size of piece
textAlign(CENTER);// center piece in little block
// draw the X or O
// centre the text in the cell
text(element.symbol, element.row * cellSize + cellSize / 2, element.col * cellSize + cellSize / 2 + 60); // creates elements
// text(element.symbol, element.row*cellSize + cellSize/2, element.col*cellSize + cellSize/2 + 20);
// text(element.symbol, element.row*cellSize + cellSize/2, element.col*cellSize+cellSize/1.5)
strokeWeight(0); // resets stroke to zero to avoid unnecessary strokes
});
}
}
// update the board after a turn takes place
update(row, col, symbol) {
let turn = this.turn;
this.cells.forEach(function (element) {
// element.col is the column and element.row is the row
// element.symbol is the X or O and element.val is the value
// element is the cell object
// if the cell's row and column match the row and column of the move
if (element.row === row && element.col === col && element.val === 0) {
// update the cell's text and value
element.symbol = symbol;
if (turn === "X") {
// if it's X's turn, the value is 1
element.val = 1;
// data.totalOnes++;
data["element"] = element;
} else {
// if it's O's turn, the value is -1
element.val = -1;
// data.totalMinusOnes++;
data["element"] = element;
}
}
});
// check if the game has been won and who has won
// update the result label accordingly
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 = "It's a tie!";
}
}
toggleTurn() {
// if it's X's turn, make it O's turn
if (this.turn == p1.symbol) {
data["currentTurn"] = p2.symbol;
this.turn = p2.symbol;
activePlayer = p2;
turnText = "Turn: " + p2.symbol;
// AB.socketOut(turnText);
} else {
// if it's O's turn, make it X's turn
data["currentTurn"] = p1.symbol;
this.turn = p1.symbol;
activePlayer = p1;
turnText = "Turn: " + p1.symbol;
// AB.socketOut(turnText);
}
data["activePlayer"] = activePlayer;
// console.log(turnText);
currentTurnDiv.html(turnText);
data["turnText"] = turnText;
AB.socketOut(data);
}
// 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.row] += element.val;
colSums[element.col] += element.val;
numOpen -= abs(element.val);
if (abs(element.row - element.col) === 0) {
// add the cell's value to the main diagonal sum
diagSums[0] += element.val;
}
if (abs(element.row - element.col) === 2 || (element.row == 1 && element.col == 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.symbol;
// if the sum is 3, X won
// p1.win();
}
if (element === -3) {
winner = p2.symbol;
// if the sum is -3, O won
// p2.win();
}
});
colSums.forEach(function (element) {
if (element === 3) {
winner = p1.symbol;
// p1.win();
}
if (element === -1 * 3) {
winner = p2.symbol;
// p2.win();
}
});
diagSums.forEach(function (element) {
if (element === 3) {
winner = p1.symbol;
// p1.win();
}
if (element === -1 * 3) {
winner = p2.symbol;
// p2.win();
}
});
// if all the squares are filled and there is no winner, i.e. none of the above confitions have been met, it's a tie
if (numOpen === 0) {
winner = "tie";
}
return winner;
}
// configure the board's state at the beginning of a game
newGame() {
this.winState = false; // the game is not over
this.turn = this.p1.symbol; // X goes first
currentTurnDiv.html("Turn: " + this.p1.symbol);
// 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
"row": i,
// the column
"col": j,
// the player's symbol. X or O
"symbol": "",
// the value
"val": 0
});
}
}
}
}
class Player {
constructor(symbol) {
// the player's symbol, X or O
this.symbol = symbol;
// this.score = 0;
}
select(board) {
// if it's X's turn, the value is 1
if (board.turn == this.symbol) {
console.log(board.turn, this.symbol)
// cx and cy are the coordinates of the mouse
let cx = int(Math.floor(mouseX / board.cellSize));
let cy = int(Math.floor(mouseY / board.cellSize));
// update the square on the board that the mouse is within
board.update(cx, cy, this.symbol);
// send the board's state to the other player
data["row"] = cx;
data["col"] = cy;
data["symbol"] = this.symbol;
}
}
// win() {
// this.score++
// }
}
// Sockets
AB.socketIn = function (data) {
console.log(data.turnText);
// update the board with the new play
currentTurnDiv.html(data.turnText);
board.update(data.row, data.col, data.symbol);
console.log(data.currentTurn);
board.turn = data.currentTurn;
activePlayer = data.activePlayer
};
// AB.socketUserlist = function ( array ) {
// console.log(array.length);
// if (array.length == 2) {
// AB.newSplash (errorSplashScreen());
// }
// };