From 74c2e0b86d28c6c50626d824f97fd455aa9c8885 Mon Sep 17 00:00:00 2001 From: Harshil Gupta Date: Fri, 19 Jul 2024 17:03:43 +0530 Subject: [PATCH] Draw situation fixed --- js/board.js | 173 ++++++++++++++++++++++++++++------------------------ js/box.js | 56 ++++++++--------- js/game.js | 62 +++++++++++-------- 3 files changed, 158 insertions(+), 133 deletions(-) diff --git a/js/board.js b/js/board.js index 01669d9..fa3d2d7 100644 --- a/js/board.js +++ b/js/board.js @@ -1,64 +1,70 @@ class Board { - static ROWS //number of rows - static COLUMNS //number of columns - static BOXES_COUNT //number of boxes + static ROWS; //number of rows + static COLUMNS; //number of columns + static BOXES_COUNT; //number of boxes constructor(rows, columns) { - Board.ROWS = rows - Board.COLUMNS = columns - Board.BOXES_COUNT = rows * columns + Board.ROWS = rows; + Board.COLUMNS = columns; + Board.BOXES_COUNT = rows * columns; //uiRoot is the root element of the board - this.uiRoot = document.querySelector(".board") - this.setEdgeThickness() + this.uiRoot = document.querySelector(".board"); + this.setEdgeThickness(); //Below is the code for generation of the board - this.boxes = new Array(rows).fill(null).map(() => new Array(columns).fill(null)) + this.boxes = new Array(rows) + .fill(null) + .map(() => new Array(columns).fill(null)); - this.adjacentBoxesToFill = [] - this.isFillingAdjacentBoxes = false - this.filledBoxes = 0 + this.adjacentBoxesToFill = []; + this.isFillingAdjacentBoxes = false; + this.filledBoxes = 0; - this.generate() + this.generate(); - this.addEdgeClickEventListener() + this.addEdgeClickEventListener(); - Game.instance.addEventListener("edgeFill", (edge) => - this.onEdgeFill(edge) - ) - Game.instance.addEventListener("boxFill", (box) => this.onBoxFill(box)) + Game.instance.addEventListener("edgeFill", (edge) => this.onEdgeFill(edge)); + Game.instance.addEventListener("boxFill", (box) => this.onBoxFill(box)); } - //Setting up the edge thickness based on the number of boxes. + //Setting up the edge thickness based on the number of boxes. //We need to set it for Root UI. setEdgeThickness() { //The thickness of the edge depends on the number of boxes let thickness = - (Board.BOXES_COUNT <= 25) ? 10 : - (Board.BOXES_COUNT <= 100) ? 8 : - (Board.BOXES_COUNT <= 200) ? 5 : 3 - - document.querySelector(":root").style.setProperty("--edge-thikness", `${thickness}px`) + Board.BOXES_COUNT <= 25 + ? 10 + : Board.BOXES_COUNT <= 100 + ? 8 + : Board.BOXES_COUNT <= 200 + ? 5 + : 3; + + document + .querySelector(":root") + .style.setProperty("--edge-thikness", `${thickness}px`); } // addEdgeClickEventListener() { this.uiRoot.addEventListener("click", (e) => { - let click = new Audio('../assets/sounds/click.mp3'); + let click = new Audio("../assets/sounds/click.mp3"); click.play(); if (!this.isFillingAdjacentBoxes) { if (e.target.classList.contains("edge")) { - let edgePosition = e.target.getAttribute("data-position") + let edgePosition = e.target.getAttribute("data-position"); //Getting the row and column of the box so we can write logic to fill the box later - let r = e.target.parentElement.getAttribute("data-row") - let c = e.target.parentElement.getAttribute("data-column") - let box = this.boxes[r][c] + let r = e.target.parentElement.getAttribute("data-row"); + let c = e.target.parentElement.getAttribute("data-column"); + let box = this.boxes[r][c]; //Getting the edge object from the box - let edge = box.getEdge(edgePosition) - this.onEdgeClick(box, edge) + let edge = box.getEdge(edgePosition); + this.onEdgeClick(box, edge); } } - }) + }); } //On click of the edge, we need to fill the edge and check if the adjacent boxes can be filled // onEdgeClick(box, edge) { @@ -78,71 +84,75 @@ class Board { onEdgeClick(box, edge) { const currentPlayerColor = Game.instance.currentPlayer.color; // Get the current player's color box.fillEdge(edge, currentPlayerColor); - + // Get the adjacent filled boxes. const adjacentBox = box.getAdjacentBox(edge.position); - - if (adjacentBox != null) + + if (adjacentBox != null) { adjacentBox.fillEdge(edge.inverseEdge, currentPlayerColor); - + } + setTimeout(() => { - if (this.adjacentBoxesToFill.length == 0) Game.instance.switchPlayer(); + if (this.adjacentBoxesToFill.length == 0) { + Game.instance.switchPlayer(); + } }, 100); } - //Check for remaining edges in boxes onEdgeFill(edge) { - const box = edge.box - box.remainingEdges-- + const box = edge.box; + box.remainingEdges--; if (box.remainingEdges == 0) { - this.fillBox(box) + this.fillBox(box); } } //Check for adjacent boxes to fill fillBox(box) { - this.adjacentBoxesToFill.push(box) + this.adjacentBoxesToFill.push(box); - setTimeout(() => this.checkAdjacentadjacentBoxesToFill(box), 100) + setTimeout(() => this.checkAdjacentadjacentBoxesToFill(box), 100); if (!this.isFillingAdjacentBoxes) { - this.isFillingAdjacentBoxes = true - this.fillBoxes() + this.isFillingAdjacentBoxes = true; + this.fillBoxes(); } } onBoxFill(box) { - this.filledBoxes++ + this.filledBoxes++; //If all the boxes are filled, then the player wins - if (this.filledBoxes == Board.BOXES_COUNT) - Game.instance.invokeEvent("playerWin") + if (this.filledBoxes == Board.BOXES_COUNT) { + Game.instance.invokeEvent("boxFill"); + Game.instance.invokeEvent("playerWin"); + } } //Board boxes generateBoardBoxes() { for (let r = 0; r < Board.ROWS; r++) for (let c = 0; c < Board.COLUMNS; c++) { - const box = new Box(r, c) - this.boxes[r][c] = box - this.uiRoot.appendChild(box.ui) + const box = new Box(r, c); + this.boxes[r][c] = box; + this.uiRoot.appendChild(box.ui); } //set each box adjacents and inverses for (let r = 0; r < Board.ROWS; r++) for (let c = 0; c < Board.COLUMNS; c++) { - let box = this.boxes[r][c] - box.adjacentBoxes = this.getBoxAdjacents(box) - box.inverseEdges = this.getBoxInverseEdges(box) - this.boxes[r][c] = box + let box = this.boxes[r][c]; + box.adjacentBoxes = this.getBoxAdjacents(box); + box.inverseEdges = this.getBoxInverseEdges(box); + this.boxes[r][c] = box; } } generate() { - this.uiRoot.style.gridTemplate = `repeat(${Board.ROWS}, 1fr) / repeat(${Board.COLUMNS}, 1fr)` - this.generateBoardBoxes() + this.uiRoot.style.gridTemplate = `repeat(${Board.ROWS}, 1fr) / repeat(${Board.COLUMNS}, 1fr)`; + this.generateBoardBoxes(); } getBoxAdjacents(box) { @@ -152,14 +162,17 @@ class Board { //{0,1} means the box is to the right of the current box //{1,0} means the box is below the current box //{0,-1} means the box is to the left of the current box - top: (box.row > 0) ? this.boxes[box.row - 1][box.column] : null, - right: (box.column < Board.COLUMNS - 1) ? this.boxes[box.row][box.column + 1] : null, - bottom: (box.row < Board.ROWS - 1) ? this.boxes[box.row + 1][box.column] : null, - left: (box.column > 0) ? this.boxes[box.row][box.column - 1] : null, - } + top: box.row > 0 ? this.boxes[box.row - 1][box.column] : null, + right: + box.column < Board.COLUMNS - 1 + ? this.boxes[box.row][box.column + 1] + : null, + bottom: + box.row < Board.ROWS - 1 ? this.boxes[box.row + 1][box.column] : null, + left: box.column > 0 ? this.boxes[box.row][box.column - 1] : null, + }; } - //Inverse edges are the edges of the adjacent boxes getBoxInverseEdges(box) { const inverseEdgePositions = { @@ -167,23 +180,25 @@ class Board { right: "left", bottom: "top", left: "right", - } + }; for (const [position, edge] of Object.entries(box.edges)) { - const inversePosition = inverseEdgePositions[position] - const adjacentBox = box.getAdjacentBox(position) - if (adjacentBox != null) //If the adjacent box is not null, then we need to set the inverse edges - box.inverseEdges[position] = adjacentBox.edges[inversePosition] //Setting the inverse edges + const inversePosition = inverseEdgePositions[position]; + const adjacentBox = box.getAdjacentBox(position); + if (adjacentBox != null) { + //If the adjacent box is not null, then we need to set the inverse edges + box.inverseEdges[position] = adjacentBox.edges[inversePosition]; //Setting the inverse edges + } } - return box.inverseEdges + return box.inverseEdges; } checkAdjacentadjacentBoxesToFill(box) { for (const [position, adjacentBox] of Object.entries(box.adjacentBoxes)) { if (adjacentBox != null) { if (!adjacentBox.filled && adjacentBox.remainingEdges == 1) { - const edge = adjacentBox.getLastRemainingEdge() + const edge = adjacentBox.getLastRemainingEdge(); if (edge != null) { - this.onEdgeClick(adjacentBox, edge) + this.onEdgeClick(adjacentBox, edge); } } } @@ -191,20 +206,20 @@ class Board { } fillBoxes() { - let fill = new Audio('../assets/sounds/fill.mp3'); + let fill = new Audio("../assets/sounds/fill.mp3"); fill.play(); if (this.adjacentBoxesToFill.length != 0) { setTimeout(() => { - const box = this.adjacentBoxesToFill.shift() - box.fill(Game.instance.currentPlayer.color) - this.fillBoxes() - }, 150) + const box = this.adjacentBoxesToFill.shift(); + box.fill(Game.instance.currentPlayer.color); + this.fillBoxes(); + }, 150); } else { setTimeout(() => { - this.isFillingAdjacentBoxes = false - this.adjacentBoxesToFill = [] + this.isFillingAdjacentBoxes = false; + this.adjacentBoxesToFill = []; //Switch the player - }, 600) + }, 600); } } } diff --git a/js/box.js b/js/box.js index cae0e7f..8efd33b 100644 --- a/js/box.js +++ b/js/box.js @@ -1,71 +1,69 @@ class Box { - constructor(row, column) { - this.row = row - this.column = column + this.row = row; + this.column = column; - this.remainingEdges = 4 + this.remainingEdges = 4; this.edges = { top: new Edge(this, "top"), right: new Edge(this, "right"), bottom: new Edge(this, "bottom"), left: new Edge(this, "left"), - } + }; this.inverseEdges = { top: null, right: null, bottom: null, left: null, - } - this.adjacentBoxes = {} - this.filled = false + }; + this.adjacentBoxes = {}; + this.filled = false; - this.ui = this.createUI() + this.ui = this.createUI(); } getEdge(edgePosition) { - return this.edges[edgePosition] + return this.edges[edgePosition]; } getAdjacentBox(edgePosition) { - return this.adjacentBoxes[edgePosition] + return this.adjacentBoxes[edgePosition]; } getLastRemainingEdge() { for (const [position, edge] of Object.entries(this.edges)) - if (!edge.filled) return this.edges[position] + if (!edge.filled) return this.edges[position]; - return null + return null; } fillEdge(edge, color) { - edge.fill(color) + edge.fill(color); } fill(color) { if (!this.filled) { - this.filled = true - this.remainingEdges = 0 - this.ui.style.background = color - this.ui.classList.add("filled") + this.filled = true; + this.remainingEdges = 0; + this.ui.style.background = color; + this.ui.classList.add("filled"); - Game.instance.invokeEvent("boxFill", this) + Game.instance.invokeEvent("boxFill", this); } } createUI() { - const ui = document.createElement("div") + const ui = document.createElement("div"); - ui.setAttribute("class", "box") - ui.setAttribute("data-row", this.row) - ui.setAttribute("data-column", this.column) + ui.setAttribute("class", "box"); + ui.setAttribute("data-row", this.row); + ui.setAttribute("data-column", this.column); - ui.appendChild(this.edges.top.ui) - ui.appendChild(this.edges.right.ui) - ui.appendChild(this.edges.bottom.ui) - ui.appendChild(this.edges.left.ui) + ui.appendChild(this.edges.top.ui); + ui.appendChild(this.edges.right.ui); + ui.appendChild(this.edges.bottom.ui); + ui.appendChild(this.edges.left.ui); - return ui + return ui; } - } diff --git a/js/game.js b/js/game.js index 56cb135..c01aaa7 100644 --- a/js/game.js +++ b/js/game.js @@ -1,5 +1,5 @@ class Game { - static instance; //Singleton instance of Game class + static instance; // Singleton instance of Game class constructor(rows, columns, playersCount) { if (Game.instance == null) Game.instance = this; @@ -37,81 +37,93 @@ class Game { this.addPlayersUI(); this.updatePlayerNameUI(); - //Adding event listeners for filling box, switching player and winning + // Adding event listeners for filling box, switching player, and winning this.addEventListener("boxFill", () => this.onBoxFill()); this.addEventListener("playerSwitch", () => this.onPlayerSwitch()); this.addEventListener("playerWin", () => this.onPlayerWin()); } - //End Game + // End Game onPlayerWin() { this.isGameover = true; + this.removeEventListener("boxFill"); let winSound = new Audio("../assets/sounds/win.mp3"); winSound.play(); - const player = this.players.reduce((prev, current) => { - return prev.filledBoxes > current.filledBoxes ? prev : current; - }); - - let play = this.players[0].filledBoxes; + // Determine winner or draw + const winner = this.determineWinner(this.players); - //Check for winner - if (this.players.every((p) => p.filledBoxes == play)) { - this.playerNameUI.parentElement.textContent = "Nobody wins"; - this.playerTurnBgUI.classList.add("no-win"); + // Display the result + if (winner === "DRAW") { + this.playerNameUI.parentElement.textContent = "DRAW"; + this.playerTurnBgUI.classList.add("win"); this.playerTurnBgUI.style.background = "#eaeaea"; } else { - this.playerNameUI.parentElement.textContent = `${player.name} wins`; + this.playerNameUI.parentElement.textContent = `${winner.name} wins`; this.playerTurnBgUI.classList.add("win"); - this.playerTurnBgUI.style.background = player.color; + this.playerTurnBgUI.style.background = winner.color; } // Open the win overlay document.getElementById("win-overlay").style.height = "100%"; } + determineWinner(players) { + // Find the maximum number of filled boxes + const maxBoxes = Math.max(...players.map((player) => player.filledBoxes)); + + // Find how many players have the maximum number of filled boxes + const result = players.filter((player) => player.filledBoxes === maxBoxes); + + if (result.length > 1) { + return "DRAW"; + } else { + return result[0]; + } + } + onPlayerSwitch() { this.updatePlayerNameUI(); } - //If a box if filled, increament players score with number of boxes filled by him/her and update UI + // If a box is filled, increment players' score with the number of boxes filled by him/her and update UI onBoxFill() { this.currentPlayer.filledBoxes++; this.updatePlayerScoreUI(); } - //Add players to UI + // Add players to UI addPlayersUI() { this.players.forEach((player, index) => { const div = document.createElement("div"); div.classList.add("player"); - //Maintain filled boxes. + // Maintain filled boxes. const b = document.createElement("b"); b.classList.add("filled-boxes"); b.textContent = player.filledBoxes; b.style.background = player.color; this.players[index]["filledBoxesUI"] = b; - //Maintain player name. + // Maintain player name. const span = document.createElement("span"); span.textContent = player.name; div.appendChild(b); div.appendChild(span); - //Adding score and name to the element + // Adding score and name to the element this.playersUI.appendChild(div); }); } - //Update player score UI used while switching player + // Update player score UI used while switching player updatePlayerScoreUI() { this.currentPlayer.filledBoxesUI.innerText = this.currentPlayer.filledBoxes; } - //Update player name UI used while switching player + // Update player name UI used while switching player updatePlayerNameUI() { this.playerNameUI.innerText = this.currentPlayer.name; this.playerTurnBgUI.style.background = this.currentPlayer.color; @@ -121,7 +133,7 @@ class Game { return this.events.hasOwnProperty(event); } - //Add event listeners + // Add event listeners addEventListener(event, callback) { if (!this.eventExist(event)) { console.error(`${event} event is not defined`); @@ -131,7 +143,7 @@ class Game { this.events[event].push(callback); } - //Remove event listeners + // Remove event listeners removeEventListener(event, callback) { if (!this.eventExist(event)) { console.error(`${event} event is not defined`); @@ -140,7 +152,7 @@ class Game { this.events[event].splice(this.events[event].indexOf(callback), 1); } - //Invoke event listeners + // Invoke event listeners invokeEvent(event, args) { if (!this.eventExist(event)) { console.error(`${event} event is not defined`); @@ -149,7 +161,7 @@ class Game { this.events[event].forEach((callback) => callback(args)); } - //Switch player + // Switch player switchPlayer() { if (!this.isGameover) { this.currentPlayerIndex = ++this.currentPlayerIndex % this.players.length;