diff --git a/Ludo/charishma2k06/README.md b/Ludo/charishma2k06/README.md new file mode 100644 index 000000000..bbb73fa5f --- /dev/null +++ b/Ludo/charishma2k06/README.md @@ -0,0 +1,19 @@ +# Ludo App - JavaScript Mini Project + +A turn-based digital adaptation of the classic board game Ludo, developed using vanilla HTML5, CSS3, and JavaScript. + +## Features +- **Turn-based gameplay:** Supports 4 players (Red, Green, Yellow, Blue). +- **Dice Roll Mechanics:** Simulates 1–6 rolls with bonus turns awarded on rolling a 6. +- **Piece Movement & Base Exits:** Tokens exit the starting base when a 6 is rolled and travel clockwise around the track. +- **Capture Logic:** Landing on an opponent's token sends it back to their base. +- **Win Condition:** First player to bring their pieces home wins. + +## Technologies Used +- HTML5 +- CSS3 (CSS Grid & Flexbox) +- JavaScript (Vanilla ES6) + +## How to Run Locally +1. Clone this repository or download the source files. +2. Open `index.html` directly in any web browser. diff --git a/Ludo/charishma2k06/index.html b/Ludo/charishma2k06/index.html new file mode 100644 index 000000000..4fd8dcd48 --- /dev/null +++ b/Ludo/charishma2k06/index.html @@ -0,0 +1,31 @@ + + + + + + Ludo Mini Project + + + +
+

Ludo Game

+ +
+
+ Current Turn: + Red +
+
+ +
🎲
+
+

Click "Roll Dice" to start!

+
+ + +
+
+ + + + \ No newline at end of file diff --git a/Ludo/charishma2k06/script.js b/Ludo/charishma2k06/script.js new file mode 100644 index 000000000..a068434b8 --- /dev/null +++ b/Ludo/charishma2k06/script.js @@ -0,0 +1,186 @@ +const BOARD_SIZE = 15; +const boardElement = document.getElementById("board"); +const rollBtn = document.getElementById("roll-btn"); +const diceView = document.getElementById("dice-view"); +const turnIndicator = document.getElementById("turn-indicator"); +const msgElement = document.getElementById("msg"); + +const PLAYERS = ["red", "green", "yellow", "blue"]; +let currentTurnIdx = 0; +let lastRoll = 0; +let hasRolled = false; + +// Common outer track coordinates (row, col) +const TRACK = [ + [6, 1], [6, 2], [6, 3], [6, 4], [6, 5], + [5, 6], [4, 6], [3, 6], [2, 6], [1, 6], [0, 6], + [0, 7], [0, 8], + [1, 8], [2, 8], [3, 8], [4, 8], [5, 8], + [6, 9], [6, 10], [6, 11], [6, 12], [6, 13], [6, 14], + [7, 14], [8, 14], + [8, 13], [8, 12], [8, 11], [8, 10], [8, 9], + [9, 8], [10, 8], [11, 8], [12, 8], [13, 8], [14, 8], + [14, 7], [14, 6], + [13, 6], [12, 6], [11, 6], [10, 6], [9, 6], + [8, 5], [8, 4], [8, 3], [8, 2], [8, 1], [8, 0], + [7, 0], [6, 0] +]; + +// Token states: -1 means in base home, 0..51 are track positions, 52 is home goal +const gameState = { + red: { basePos: [2, 2], tokens: [-1, -1] }, + green: { basePos: [2, 12], tokens: [-1, -1] }, + yellow: { basePos: [12, 12], tokens: [-1, -1] }, + blue: { basePos: [12, 2], tokens: [-1, -1] } +}; + +const START_OFFSETS = { red: 0, green: 13, yellow: 26, blue: 39 }; + +function initBoard() { + boardElement.innerHTML = ""; + for (let r = 0; r < BOARD_SIZE; r++) { + for (let c = 0; c < BOARD_SIZE; c++) { + const cell = document.createElement("div"); + cell.className = "cell"; + cell.dataset.row = r; + cell.dataset.col = c; + + if (r < 6 && c < 6) cell.classList.add("red-base"); + else if (r < 6 && c > 8) cell.classList.add("green-base"); + else if (r > 8 && c > 8) cell.classList.add("yellow-base"); + else if (r > 8 && c < 6) cell.classList.add("blue-base"); + else if (r >= 6 && r <= 8 && c >= 6 && c <= 8) cell.classList.add("center-cell"); + else if (r === 7 && c > 0 && c < 6) cell.classList.add("red-path"); + else if (c === 7 && r > 0 && r < 6) cell.classList.add("green-path"); + else if (r === 7 && c > 8 && c < 14) cell.classList.add("yellow-path"); + else if (c === 7 && r > 8 && r < 14) cell.classList.add("blue-path"); + + boardElement.appendChild(cell); + } + } + renderTokens(); +} + +function getCell(r, c) { + return document.querySelector(`[data-row='${r}'][data-col='${c}']`); +} + +function renderTokens() { + document.querySelectorAll(".token").forEach((t) => t.remove()); + + PLAYERS.forEach((player) => { + gameState[player].tokens.forEach((stepPos, tIdx) => { + if (stepPos === 52) return; // Reached goal + + const tokenEl = document.createElement("div"); + tokenEl.className = `token ${player}`; + tokenEl.id = `${player}-token-${tIdx}`; + + if (hasRolled && player === PLAYERS[currentTurnIdx]) { + if (canMoveToken(player, stepPos, lastRoll)) { + tokenEl.classList.add("clickable"); + tokenEl.onclick = () => moveToken(player, tIdx); + } + } + + let targetCell = null; + if (stepPos === -1) { + const [br, bc] = gameState[player].basePos; + targetCell = getCell(br, bc + tIdx); + } else { + const actualIdx = (stepPos + START_OFFSETS[player]) % TRACK.length; + const [tr, tc] = TRACK[actualIdx]; + targetCell = getCell(tr, tc); + } + + if (targetCell) targetCell.appendChild(tokenEl); + }); + }); +} + +function canMoveToken(player, stepPos, roll) { + if (stepPos === -1) return roll === 6; + return stepPos + roll <= 52; +} + +rollBtn.addEventListener("click", () => { + if (hasRolled) return; + lastRoll = Math.floor(Math.random() * 6) + 1; + diceView.textContent = lastRoll; + hasRolled = true; + rollBtn.disabled = true; + + const currentPlayer = PLAYERS[currentTurnIdx]; + const canAnyMove = gameState[currentPlayer].tokens.some((pos) => + canMoveToken(currentPlayer, pos, lastRoll) + ); + + if (!canAnyMove) { + msgElement.textContent = `No moves possible for ${currentPlayer}. Turn switches.`; + setTimeout(switchTurn, 1000); + } else { + msgElement.textContent = `${currentPlayer.toUpperCase()} rolled a ${lastRoll}! Select a piece.`; + renderTokens(); + } +}); + +function moveToken(player, tokenIdx) { + const currentPos = gameState[player].tokens[tokenIdx]; + if (currentPos === -1) { + gameState[player].tokens[tokenIdx] = 0; + } else { + gameState[player].tokens[tokenIdx] = Math.min(52, currentPos + lastRoll); + } + + // Check captures + checkCapture(player, gameState[player].tokens[tokenIdx]); + + // Check win condition + if (gameState[player].tokens.every((pos) => pos === 52)) { + msgElement.textContent = `🎉 Player ${player.toUpperCase()} wins the game!`; + rollBtn.disabled = true; + renderTokens(); + return; + } + + if (lastRoll === 6) { + msgElement.textContent = `Rolled a 6! Roll again.`; + hasRolled = false; + rollBtn.disabled = false; + renderTokens(); + } else { + switchTurn(); + } +} + +function checkCapture(activePlayer, pos) { + if (pos <= 0 || pos >= 52) return; + const activeActual = (pos + START_OFFSETS[activePlayer]) % TRACK.length; + + PLAYERS.forEach((other) => { + if (other === activePlayer) return; + gameState[other].tokens.forEach((oPos, idx) => { + if (oPos > 0 && oPos < 52) { + const otherActual = (oPos + START_OFFSETS[other]) % TRACK.length; + if (activeActual === otherActual) { + gameState[other].tokens[idx] = -1; + msgElement.textContent = `${activePlayer.toUpperCase()} captured ${other.toUpperCase()}!`; + } + } + }); + }); +} + +function switchTurn() { + currentTurnIdx = (currentTurnIdx + 1) % PLAYERS.length; + hasRolled = false; + rollBtn.disabled = false; + + const nextPlayer = PLAYERS[currentTurnIdx]; + turnIndicator.textContent = nextPlayer.charAt(0).toUpperCase() + nextPlayer.slice(1); + turnIndicator.className = `turn-${nextPlayer}`; + msgElement.textContent = `Turn switched to ${nextPlayer}. Roll the dice!`; + renderTokens(); +} + +initBoard(); \ No newline at end of file diff --git a/Ludo/charishma2k06/style.css b/Ludo/charishma2k06/style.css new file mode 100644 index 000000000..1c26be2ce --- /dev/null +++ b/Ludo/charishma2k06/style.css @@ -0,0 +1,137 @@ +* { + box-sizing: border-box; + margin: 0; + padding: 0; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; +} + +body { + background: #f0f2f5; + display: flex; + justify-content: center; + padding: 20px; +} + +.container { + display: flex; + flex-direction: column; + align-items: center; + gap: 16px; +} + +h1 { + color: #333; +} + +.control-panel { + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; + background: #fff; + padding: 14px 24px; + border-radius: 10px; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.08); +} + +.status-box { + font-size: 1.1rem; +} + +.turn-red { color: #e53935; font-weight: bold; } +.turn-green { color: #43a047; font-weight: bold; } +.turn-yellow { color: #fbc02d; font-weight: bold; } +.turn-blue { color: #1e88e5; font-weight: bold; } + +.dice-section { + display: flex; + align-items: center; + gap: 12px; +} + +#roll-btn { + padding: 8px 18px; + font-size: 1rem; + font-weight: 600; + color: #fff; + background-color: #2e7d32; + border: none; + border-radius: 6px; + cursor: pointer; + transition: 0.2s ease; +} + +#roll-btn:disabled { + background-color: #9e9e9e; + cursor: not-allowed; +} + +.dice-display { + font-size: 2rem; + width: 44px; + height: 44px; + display: flex; + align-items: center; + justify-content: center; + border: 2px solid #ccc; + border-radius: 8px; + background: #fafafa; +} + +#msg { + font-size: 0.95rem; + color: #555; +} + +/* Board styling: 15x15 CSS Grid */ +.ludo-board { + display: grid; + grid-template-columns: repeat(15, 32px); + grid-template-rows: repeat(15, 32px); + border: 4px solid #333; + background-color: #fff; +} + +.cell { + border: 1px solid #ddd; + display: flex; + align-items: center; + justify-content: center; + position: relative; +} + +.cell.red-base { background-color: #ffebee; } +.cell.green-base { background-color: #e8f5e9; } +.cell.yellow-base { background-color: #fffde7; } +.cell.blue-base { background-color: #e3f2fd; } + +.cell.red-path { background-color: #ef5350; } +.cell.green-path { background-color: #66bb6a; } +.cell.yellow-path { background-color: #ffee58; } +.cell.blue-path { background-color: #42a5f5; } + +.cell.center-cell { background-color: #37474f; } + +/* Token styles */ +.token { + width: 22px; + height: 22px; + border-radius: 50%; + border: 2px solid #fff; + box-shadow: 0 2px 4px rgba(0,0,0,0.3); + cursor: pointer; +} + +.token.clickable { + animation: pulse 1s infinite alternate; +} + +@keyframes pulse { + from { transform: scale(1); } + to { transform: scale(1.25); } +} + +.token.red { background-color: #d32f2f; } +.token.green { background-color: #2e7d32; } +.token.yellow { background-color: #fbc02d; } +.token.blue { background-color: #1976d2; } \ No newline at end of file