From 8ffe3d2d6ebd7c76a2d6df02fd92cb065e8e1f2d Mon Sep 17 00:00:00 2001 From: wreeshab Date: Tue, 28 May 2024 22:39:04 +0530 Subject: [PATCH 1/4] implement bot --- README.md | 9 +++- hackerplus.js | 121 +++++++++++++++++++++++++++++++++++++++++++++-- pieces/pieces.js | 11 ++--- 3 files changed, 130 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index cd1a82a..dd9f726 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,12 @@
3)Animate the game movements (For e.g., smoother bullets)
-4)Create a single-player mode where the opponent is a bot. -
5)Add spells (For eg: spell which makes a piece passthrough for a move)
+4)Create a single-player mode where the opponent is a bot. +
+ +##List of Bugs Found in Bot So Far +1) say the bot is rotating one of its ricos/semiricos it'll rotate, no issues.... but but but if the same piece is moved by the bot in the next move, it'll rotate again to its initial position. The Dom structure is such that if a rico is rotated it parent node (ie .square) is rotated and not the svg element or the rico div. Upon moving the rico, that new parent will have style.transform = rotate(0deg) and old parent will have style.transform = rotate(90deg). +Note: other pieces going to the old parent will have 90 deg rotation whichh is absurdd. +2) if a semiRico is broken it is not visually reflected immediately (its being updated in the next move only) though the master array is updated simulatneously. diff --git a/hackerplus.js b/hackerplus.js index 52efbb2..01d858e 100644 --- a/hackerplus.js +++ b/hackerplus.js @@ -152,7 +152,7 @@ function resetGame() { } function gameWin(element, currentLocation) { - saveGameState() + saveGameState(); currentLocation.removeChild(bulletDiv); // console.log("game over"); @@ -177,7 +177,7 @@ function playAgain() { localStorage.removeItem("gameStateHistory"); } function handlePlayerLossByTime(player) { - saveGameState() + saveGameState(); gameOver = true; isBulletMoving = false; @@ -201,6 +201,7 @@ function changePlayer() { if (currentPlayer === "green") { currentPlayer = "blue"; playerDisplay.innerText = "blue's"; + botMove(); } else { currentPlayer = "green"; playerDisplay.innerText = "green's"; @@ -319,7 +320,7 @@ function replayGame() { }).then(() => { setTimeout(() => { handleCannonShoot(currentPlayer === "green" ? "blue" : "green"); - },100); + }, 100); }); index++; } else { @@ -344,6 +345,120 @@ function logMove(description) { movesBoardChild.textContent = description; gameHistoryBoard.append(movesBoardChild); } +//here im assuming that the blue player is bot +function botMove() { + if (gameOver || gamePaused || currentPlayer !== "blue") { + return; + } + const validMoves = getAllValidMovesForBot("blue"); + if (validMoves.length === 0) { + handlePlayerLossByTime("blue"); + return; + } + const randomIndex = Math.floor(Math.random() * validMoves.length); + const randomMove = validMoves[randomIndex]; + executeBotMove(randomMove); +} + +function getAllValidMovesForBot(player) { + const validMoves = []; + const parser = new DOMParser(); + + for (let i = 0; i < width * width; i++) { + const piece = startPieces[i]; + const row = Math.floor(i / width); + const column = i % width; + + // Parse the piece string into a DOM element + const doc = parser.parseFromString(piece, "text/html"); + const pieceDOM = doc.querySelector("div"); + // console.log(pieceDOM.parentNode); + if (pieceDOM && pieceDOM.getAttribute("class").includes(player)) { + const moves = getValidMovesForPieceForBot(row, column, pieceDOM); + validMoves.push(...moves); + // The below is for ricochets + if (pieceDOM.id === "semiRicochet" || pieceDOM.id === "ricochet") { + validMoves.push({ piece, row, column, type: "rotate" }); + } + } + } + return validMoves; +} + +function getValidMovesForPieceForBot(row, column, piece) { + const moves = []; + const directions = []; + + if (piece.id === "topCannon" || piece.id === "bottomCanon") { + directions.push({ row: 0, column: -1 }); + directions.push({ row: 0, column: 1 }); + } else { + directions.push( + { row: -1, column: 0 }, // Up + { row: 1, column: 0 }, // Down + { row: 0, column: -1 }, // Left + { row: 0, column: 1 }, // Right + { row: -1, column: -1 }, // Up-Left + { row: -1, column: 1 }, // Up-Right + { row: 1, column: -1 }, // Down-Left + { row: 1, column: 1 } // Down-Right + ); + } + + directions.forEach((direction) => { + const newRow = row + direction.row; + const newColumn = column + direction.column; + + // Check if the new position is valid + if ( + isValidPosition(newRow, newColumn) && + startPieces[newRow * width + newColumn] === "" + ) { + moves.push({ + piece, + oldRow: row, + oldColumn: column, + row: newRow, + column: newColumn, + type: "move", + }); + } + }); + return moves; +} +function executeBotMove(move) { + console.log("move", move); + if (move.type === "move") { + movePiece(move.oldRow, move.oldColumn, move.row, move.column); + } else if (move.type === "rotate") { + rotatePieceForBot(move.row, move.column); + } + updateBoard(); +} +function rotatePieceForBot(row, column) { + const pieceIndex = row * width + column; + const pieceHTML = startPieces[pieceIndex]; + + // Parse the piece HTML into a DOM element + const parser = new DOMParser(); + const doc = parser.parseFromString(pieceHTML, "text/html"); + const pieceDOM = doc.querySelector("div"); + + if (pieceDOM.id === "semiRicochet" || pieceDOM.id === "ricochet") { + const currentRotation = ricochetRotation[pieceIndex] || 0; + const newRotation = (currentRotation + 90) % 360; + ricochetRotation[pieceIndex] = newRotation; + + // Apply rotation to the actual DOM element in the game board + const actualPieceDOM = document.querySelector(`#${pieceDOM.id}`); + if (actualPieceDOM) { + actualPieceDOM.style.transform = `rotate(${newRotation}deg)`; + } + } + handleCannonShoot(currentPlayer); + updateBoard(); + changePlayer(); +} resetButton.addEventListener("click", resetGame); pauseButton.addEventListener("click", pauseGame); diff --git a/pieces/pieces.js b/pieces/pieces.js index c55bb38..b7f4ef4 100644 --- a/pieces/pieces.js +++ b/pieces/pieces.js @@ -1,17 +1,16 @@ const topTitan = ` -
+
`; - const topCannon = - '
'; + '
'; const topSemiRicochet = ` -
+
@@ -20,10 +19,10 @@ const topSemiRicochet = `
`; const topRicochet = - '
'; + '
'; const topTank = ` -
+
From 7237b995ae6ba39583b36e360432294258709a62 Mon Sep 17 00:00:00 2001 From: wreeshab Date: Wed, 29 May 2024 00:35:30 +0530 Subject: [PATCH 2/4] add complete responsiveness to all pages except main page --- README.md | 1 + hacker-mode.css | 56 +++++++++++++++++++++++++++++++++++++++++----- hacker-mode.html | 2 +- hackerplus.css | 58 ++++++++++++++++++++++++++++++++++++++++++------ hackerplus.html | 2 +- normal-mode.css | 21 ++++++++++++++---- normal-mode.html | 2 +- 7 files changed, 122 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index dd9f726..8371903 100644 --- a/README.md +++ b/README.md @@ -46,3 +46,4 @@ 1) say the bot is rotating one of its ricos/semiricos it'll rotate, no issues.... but but but if the same piece is moved by the bot in the next move, it'll rotate again to its initial position. The Dom structure is such that if a rico is rotated it parent node (ie .square) is rotated and not the svg element or the rico div. Upon moving the rico, that new parent will have style.transform = rotate(0deg) and old parent will have style.transform = rotate(90deg). Note: other pieces going to the old parent will have 90 deg rotation whichh is absurdd. 2) if a semiRico is broken it is not visually reflected immediately (its being updated in the next move only) though the master array is updated simulatneously. +3) rico/semirico turning is not considered as a move => even after they rotate, one more move is getting executed => 2 bullets are being shot!!!!! \ No newline at end of file diff --git a/hacker-mode.css b/hacker-mode.css index 913ea2d..ae9453b 100644 --- a/hacker-mode.css +++ b/hacker-mode.css @@ -24,8 +24,8 @@ body { justify-content: center; flex-direction: column; align-items: center; - min-height: 100vh; - min-width: 700px; + height: 100vh; + width: 700px; width: 33.3333%; } @@ -402,10 +402,20 @@ button:disabled { align-items: center; width: 100%; height: 100vh; + overflow-x: hidden; + background: url(assets/background3.jpg); + background-size: cover; + } + + #left-div { + height: 0; + width: 0; } h1 { text-align: center; + font-size: 30px; + margin-top: 70px; } #gameboard { @@ -457,11 +467,45 @@ button:disabled { width: 10px; } -} + #game-state-history { + height: 225px; + width: 70%; + font-size: 11px; + + + } + + #right-div { + width: 100%; + height: 100%; + } -@media screen and (max-width: 420px) { #control-center { - /* width: 300px; */ + scale: .85; + } + + #center-div { + height: 700px; + width: 100%; + } + + #winner-notice { scale: .7; } -} \ No newline at end of file + + #directionalBullet { + height: 23px; + width: 23px; + } + #control-panel-parent{ + width: 100%; + display: flex; + justify-content: center; + align-items: center; + + } + #timertext{ + font-size: .7em; + } + +} diff --git a/hacker-mode.html b/hacker-mode.html index de50f85..6415f6b 100644 --- a/hacker-mode.html +++ b/hacker-mode.html @@ -17,7 +17,7 @@

Ricochet Rumble hacker

-
+
diff --git a/hackerplus.css b/hackerplus.css index dc4e4b4..9dc62c0 100644 --- a/hackerplus.css +++ b/hackerplus.css @@ -12,7 +12,7 @@ body { justify-content: center; flex-direction: row; align-items: center; - min-height: 100vh; + height: 100vh; width: 100%; background: url(assets/background4.jpg); background-size: cover; @@ -24,8 +24,8 @@ body { justify-content: center; flex-direction: column; align-items: center; - min-height: 100vh; - min-width: 700px; + height: 100vh; + width: 700px; width: 33.3333%; } @@ -403,10 +403,21 @@ button:disabled { align-items: center; width: 100%; height: 100vh; + overflow-x: hidden; + background: url(assets/background3.jpg); + background-size: cover; + } + + #left-div { + height: 0; + width: 0; } h1 { text-align: center; + font-size: 27px; + margin-top: 70px; + color: aqua; } #gameboard { @@ -458,11 +469,44 @@ button:disabled { width: 10px; } -} + #game-state-history { + height: 225px; + width: 70%; + font-size: 11px; + + + } + + #right-div { + width: 100%; + height: 100%; + } -@media screen and (max-width: 420px) { #control-center { - /* width: 300px; */ + scale: .8; + } + + #center-div { + height: 700px; + width: 100%; + } + + #winner-notice { scale: .7; } -} \ No newline at end of file + + #directionalBullet { + height: 23px; + width: 23px; + } + #control-panel-parent{ + width: 100%; + display: flex; + justify-content: center; + align-items: center; + + } + #timertext{ + font-size: .7em; + } +} diff --git a/hackerplus.html b/hackerplus.html index 656a5a4..d87eaaf 100644 --- a/hackerplus.html +++ b/hackerplus.html @@ -17,7 +17,7 @@

Ricochet Rumble hacker++

-
+
diff --git a/normal-mode.css b/normal-mode.css index 287255a..be501e9 100644 --- a/normal-mode.css +++ b/normal-mode.css @@ -316,6 +316,7 @@ button:disabled { h1 { text-align: center; + font-size: 25px; } #gameboard { @@ -367,11 +368,23 @@ button:disabled { width: 10px; } -} + #control-panel-parent { + width: 100%; + display: flex; + justify-content: center; + align-items: center; -@media screen and (max-width: 420px) { - #control-center { - /* width: 300px; */ + } + + #timertext { + font-size: .7em; + } + + #winner-notice { scale: .7; } + + #control-center { + scale: .8; + } } \ No newline at end of file diff --git a/normal-mode.html b/normal-mode.html index 6ca257e..f180627 100644 --- a/normal-mode.html +++ b/normal-mode.html @@ -16,7 +16,7 @@

Ricochet Rumble normal

-
+

It's green's turn

+
00:21
diff --git a/pieces/pieces.js b/pieces/pieces.js index b7f4ef4..cbe571c 100644 --- a/pieces/pieces.js +++ b/pieces/pieces.js @@ -9,26 +9,13 @@ const topTitan = ` const topCannon = '
'; -const topSemiRicochet = ` -
- - - - - -
`; +const topSemiRicochet = '
'; const topRicochet = '
'; -const topTank = ` -
- - - - - -
`; +const topTank = '
' + const bottomSemiRicochet = '
'; diff --git a/script-for-hacker-mode.js b/script-for-hacker-mode.js index b84720a..ad7c581 100644 --- a/script-for-hacker-mode.js +++ b/script-for-hacker-mode.js @@ -12,6 +12,7 @@ const winnerNotice = document.querySelector("#winner-notice"); const playAgainBtn = document.querySelector("#play-again"); const undoButton = document.querySelector("#undo"); const redoButton = document.querySelector("#redo"); +const swapButton = document.querySelector("#swap"); const gameHistoryBoard = document.querySelector("#game-state-history"); const timer = document.querySelector("#timertext"); @@ -33,6 +34,7 @@ let moveCount = 1; let gameOver = false; let gamePaused = false; let isBulletMoving = false; +let ricoSwap = false; rightBtn.disabled = true; leftBtn.disabled = true; @@ -251,7 +253,7 @@ function redoLastMove() { currentPlayer: currentPlayer, remainingSeconds: remainingSeconds, }); - console.log("Redo: Current state pushed to gameStateHistory:", gameStateHistory); + startPieces = redoGameState.pieces; ricochetRotation = redoGameState.rotation; currentPlayer = redoGameState.currentPlayer; @@ -261,8 +263,6 @@ function redoLastMove() { updateBoard(); } } - - function saveGameStateHistoryToLocal() { localStorage.setItem("gameStateHistory", JSON.stringify(gameStateHistory)); } @@ -280,6 +280,31 @@ resumeButton.addEventListener("click", resumeGame); playAgainBtn.addEventListener("click", playAgain); undoButton.addEventListener("click", undoLastMove); redoButton.addEventListener("click", redoLastMove); +swapButton.addEventListener("click", swapRicochet); + +function swapRicochet() { + ricoSwap = true; + if (selectedPiece.pieceName === "Ricochet") { + clearHighlightedSquares(); + const squares = document.querySelectorAll(".square"); + squares.forEach((square) => { + if (square.firstChild) { + if ( + (square.firstChild.id === "semiRicochet" || + square.firstChild.id === "ricochet" || + square.firstChild.id === "tank") && + (selectedPiece.row !== + parseInt(square.getAttribute("square-id")[0]) || + selectedPiece.column !== + parseInt(square.getAttribute("square-id")[1])) + ) + // console.log(square.firstChild.id) + square.style.border = "5px ridge rgb(156,229,248)"; + square.addEventListener("click", handleRicoSwap); + } + }); + } +} function createBoard() { startPieces.forEach((startPiece, i) => { @@ -325,11 +350,42 @@ function handleRicochetMove(event) { moveRicochet(selectedPiece.row, selectedPiece.column, row, column); } } +function handleRicoSwap(e) { + if (selectedPiece) { + const row = parseInt(e.target.parentNode.getAttribute("square-id")[0]); + const column = parseInt(e.target.parentNode.getAttribute("square-id")[1]); + moveForSwap(selectedPiece.row, selectedPiece.column, row, column); + } +} +function moveForSwap(row, column, newRow, newColumn) { + let temp = startPieces[row * width + column]; + startPieces[row * width + column] = startPieces[newRow * width + newColumn]; + startPieces[newRow * width + newColumn] = temp; + const toBeSwappedPiece = document.querySelector( + `[square-id="${newRow}${newColumn}"]` + ); + if ( + toBeSwappedPiece.firstChild.id === "ricochet" || + toBeSwappedPiece.firstChild.id === "semiRicochet" + ) { + let tempRotation = ricochetRotation[row * width + column]; + ricochetRotation[row * width + column] = ricochetRotation[newRow * width + newColumn]; + ricochetRotation[newRow * width + newColumn] = tempRotation; + } + logMove(`${currentPlayer.toUpperCase()} swapped their ${selectedPiece.pieceName} for ${(currentPlayer === "green"?"blue":"green").toUpperCase()}'s ${toBeSwappedPiece.firstChild.id}`); + + setTimeout(()=>{ + updateBoard(); + changePlayer() + },7) // really dont know how setTimeout solves the bug, but it does so yeh! :) + // changePlayer(); + ricoSwap = false; +} // ---------------------------------------- PRIMARY EVENT LISTENER----------------------------------------------- gameBoard.addEventListener("click", (e) => { if (!gameOver) { - if (!gamePaused) { + if (!gamePaused && !ricoSwap) { if (e.target.classList.contains(currentPlayer)) { if (!isBulletMoving) { const parentNode = e.target.parentNode;