Let's break down what we need to build for our single-player Battleship game. The game requires creating an interactive board where players can search for hidden ships. Think of it like a digital version of the classic board game, but designed for solo play.
Our game needs several key components:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Battleship</title>
<link rel="stylesheet" href="./assets/css/index.css">
<script type="module" src="./assets/js/index.js"></script>
</head>
<body>
<h1>Battleship</h1>
<div id="game-over-message"></div>
<div id="board-container">
<div id="game-board"></div>
</div>
<button id="reset-button">Reset Game</button>
</body>
</html>
/* Main container layout */
#board-container {
display: flex;
justify-content: center;
margin: 20px;
}
#game-board {
display: grid;
grid-template-columns: repeat(9, 50px);
grid-template-rows: repeat(9, 50px);
gap: 2px;
background-color: #235789;
padding: 10px;
border-radius: 5px;
}
/* Square styling */
.square {
background-color: #c1c8e4;
border: 1px solid #8860d0;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
cursor: pointer;
transition: background-color 0.3s ease;
}
.square:hover {
background-color: #84ceeb;
}
/* Hit and miss indicators */
.hit {
background-color: #5ab9ea !important;
color: white;
}
.miss {
background-color: #c1121f !important;
}
/* Game over message */
#game-over-message {
text-align: center;
font-size: 24px;
color: #4CAF50;
margin: 20px;
height: 30px;
}
/* Reset button */
#reset-button {
display: block;
margin: 20px auto;
padding: 10px 20px;
font-size: 16px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s;
}
#reset-button:hover {
background-color: #45a049;
}
import Board from "./board.js";
class BattleshipGame {
constructor() {
// Initialize game board and state
this.board = new Board();
this.setupBoard();
this.setupResetButton();
}
setupBoard() {
const gameBoard = document.getElementById('game-board');
gameBoard.innerHTML = ''; // Clear existing board
// Create squares for each cell in the grid
for (let row = 0; row < this.board.numRows; row++) {
for (let col = 0; col < this.board.numCols; col++) {
const square = document.createElement('div');
square.className = 'square';
// Store position data on the element
square.dataset.row = row;
square.dataset.col = col;
// Add click handler
square.addEventListener('click', (e) => this.handleSquareClick(e));
gameBoard.appendChild(square);
}
}
}
handleSquareClick(e) {
// Ignore if game is over
if (this.board.isGameOver()) return;
const square = e.target;
const row = parseInt(square.dataset.row);
const col = parseInt(square.dataset.col);
// Make hit and get result
const result = this.board.makeHit(row, col);
// Update square appearance based on hit result
if (result === null) {
square.classList.add('miss');
} else {
square.classList.add('hit');
square.textContent = result;
}
// Check for game over
if (this.board.isGameOver()) {
this.handleGameOver();
}
// Disable clicked square
square.style.pointerEvents = 'none';
}
handleGameOver() {
// Show victory message
const message = document.getElementById('game-over-message');
message.textContent = 'Congratulations! You found all the ships!';
// Disable all squares
const squares = document.querySelectorAll('.square');
squares.forEach(square => {
square.style.pointerEvents = 'none';
});
}
setupResetButton() {
const resetButton = document.getElementById('reset-button');
resetButton.addEventListener('click', () => {
// Create new board
this.board = new Board();
// Clear game over message
document.getElementById('game-over-message').textContent = '';
// Reset the board
this.setupBoard();
});
}
}
// Initialize game when DOM content is loaded
document.addEventListener('DOMContentLoaded', () => {
new BattleshipGame();
});
Our implementation uses a BattleshipGame class to encapsulate all game logic and state. This organization helps keep our code clean and maintainable. Think of it like having a game manager who keeps track of all the rules and state changes.
The game board is created using CSS Grid, which is perfect for our 9x9 layout. Each cell is a div element that stores its position using data attributes. This is similar to how a physical board game has coordinates for each position.
Our click handlers work like this:
The game provides clear visual feedback using CSS classes:
The Board class (provided in board.js) handles the core game logic. It maintains:
The reset button creates a fresh game state by:
To ensure your implementation works correctly, test these scenarios:
Consider these improvements to the basic implementation:
Each square needs to be clickable only once and should be disabled after being revealed. We handle this by:
Keeping track of hits and game completion requires careful state management. Our solution: