Here is my Battleship entry:
https://jsfiddle.net/dgewaLj0
Looking at my code, you wont see textareas for all the cells that make up the play area grid, because I had the script create them for me. Saved me the pain of manually hardcoding 100 individual textareas.
HTML
StyleCode:<div class="game-wrapper"> <!-- Left: game board --> <div> <h1>Battleship</h1> <div id="game"></div> <div id="controls"> <input type="text" id="coordInput" placeholder="e.g. A5" maxlength="3"> <button onclick="fire()">Fire</button> </div> <p id="status">Chances left: 40 | Hits: 0 | Misses: 0 | Ships sunk: 0</p> <button id="restartBtn" style="display:none;" onclick="initGame()">Play Again</button> </div> <!-- Right: side column with textarea --> <div class="side-column"> <textarea> Objective: Sink all of the hidden ships on the 10×10 grid before you run out of chances. Fleet: There are 5 ships of different lengths. They are placed randomly on the grid, either horizontally or vertically. Taking a Shot: Type the coordinate of the cell you want to target into the input box, for example: A5 ? Column A, Row 5. Press Enter on your keyboard or click the Fire button. The cell will update, X ? you hit part of a ship, 0 ? you missed. Below the input box you’ll see: • Chances left • Hits • Misses • Ships sunk </textarea> </div> </div>
JavascriptCode:body { font-family: monospace; text-align: center; background-color: #f0f8ff; } table { border-collapse: collapse; margin: auto; } td { padding: 0; margin: 0; text-align: center; } .row-header { padding-right: 8px; min-width: 25px; text-align: right; } textarea { resize: none; width: 30px; height: 30px; text-align: center; font-size: 18px; background-color: #add8e6; border: none; /* no visible borders */ margin-right: 5px; margin-bottom: 3px; } .miss { background-color: #add8e6; color: white; .hit { background-color: #add8e6; color: red; } #controls { margin-top: 20px; } .game-wrapper { display: flex; justify-content: center; gap: 10px; margin-top: 20px; height: 600px; } .side-column { background-color: #E0E0E0; border-radius: 8px; padding: 10px; margin-top: 81px; } .side-column textarea { background-color: #E0E0E0; width: 350px; height: 100%; resize: none; font-family: monospace; font-size: 14px; box-sizing: border-box; text-align: left; }
Code:const SIZE = 10; // Size of the board (10x10 grid) const CHANCES = 40; // Number of shots the player has let grid = []; // 2D array representing the board cells let ships = []; // Array holding all ships and their coordinates let chancesLeft, hits, misses, sunk; // Counters for game progress // Initialize a new game function initGame() { chancesLeft = CHANCES; hits = 0; misses = 0; sunk = 0; grid = Array.from({length: SIZE}, () => Array(SIZE).fill("~")); ships = []; placeShips(); drawBoard(); updateStatus(); document.getElementById("restartBtn").style.display = "none"; } // Draw the board as an HTML table function drawBoard() { let html = "<table><tr><td></td>"; for (let c=0; c<SIZE; c++) { html += `<td>${String.fromCharCode(65+c)}</td>`; } html += "</tr>"; for (let r=0; r<SIZE; r++) { html += `<tr><td class="row-header">${r+1}</td>`; for (let c=0; c<SIZE; c++) { html += `<td><textarea id="cell-${r}-${c}" readonly>${grid[r][c]}</textarea></td>`; } html += "</tr>"; } html += "</table>"; document.getElementById("game").innerHTML = html; } // Update status line document.getElementById("status").innerText = `Chances left: ${chancesLeft} | Hits: ${hits} | Misses: ${misses} | Ships sunk: ${sunk}`; } // Handle firing at a coordinate function fire() { let input = document.getElementById("coordInput").value.trim().toUpperCase(); document.getElementById("coordInput").value = ""; if (!/^[A-J](10|[1-9])$/.test(input)) { alert("Invalid coordinate! Use format like A5."); return; } let col = input.charCodeAt(0) - 65; let row = parseInt(input.slice(1)) - 1; let cell = document.getElementById(`cell-${row}-${col}`); if (cell.classList.contains("hit") || cell.classList.contains("miss")) { alert("Already targeted!"); return; } let hitShip = ships.find(ship => ship.some(([r,c]) => r===row && c===col)); if (hitShip) { cell.value = "X"; cell.classList.add("hit"); hits++; hitShip.hitCount = (hitShip.hitCount || 0) + 1; if (hitShip.hitCount === hitShip.length) { sunk++; alert("You sunk a ship!"); } chancesLeft--; // <-- lose a chance even on a hit } else { cell.value = "0"; cell.classList.add("miss"); misses++; chancesLeft--; } updateStatus(); checkGameOver(); } // Check win/lose conditions function checkGameOver() { let totalShipCells = ships.reduce((sum, s) => sum + s.length, 0); if (hits === totalShipCells) { alert("You win!"); document.getElementById("restartBtn").style.display = "inline"; } else if (chancesLeft <= 0) { alert("Game over! You lose."); document.getElementById("restartBtn").style.display = "inline"; } } // Randomly place ships function placeShips() { const shipSizes = [2,3,3,4,5]; for (let size of shipSizes) { let placed = false; while (!placed) { let orientation = Math.random() < 0.5 ? "H" : "V"; let row = Math.floor(Math.random()*SIZE); let col = Math.floor(Math.random()*SIZE); let coords = []; for (let i=0; i<size; i++) { let r = row + (orientation==="V"?i:0); let c = col + (orientation==="H"?i:0); if (r>=SIZE || c>=SIZE) { coords=[]; break; } coords.push([r,c]); } if (coords.length===size && validPlacement(coords)) { ships.push(coords); placed = true; } } } } // Ensure ships don't overlap or touch function validPlacement(coords) { for (let [r,c] of coords) { for (let dr=-1; dr<=1; dr++) { for (let dc=-1; dc<=1; dc++) { let nr=r+dr, nc=c+dc; if (nr>=0 && nr<SIZE && nc>=0 && nc<SIZE) { if (ships.some(ship => ship.some(([sr,sc]) => sr===nr && sc===nc))) { return false; } } } } } return true; } // Allow Enter key to fire document.getElementById("coordInput").addEventListener("keydown", e => { if (e.key==="Enter") fire(); }); // Start game on load initGame();




Reply With Quote
