Building a Battleship Game: Complete Implementation Guide

Understanding the Problem

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:

Complete Implementation

HTML Structure (index.html)


<!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>
    

CSS Styling (index.css)


/* 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;
}
    

Main Game Logic (index.js)


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();
});
    

Understanding the Implementation

Class-Based Structure

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.

Grid Creation

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.

Event Handling

Our click handlers work like this:

  1. When a square is clicked, we first check if the game is still active
  2. We retrieve the position data stored on the clicked square
  3. We attempt to make a hit on the board at those coordinates
  4. Based on the result, we update the visual state of the square
  5. We check if this move ended the game

Visual Feedback

The game provides clear visual feedback using CSS classes:

Key Features Explained

Game State Management

The Board class (provided in board.js) handles the core game logic. It maintains:

Reset Functionality

The reset button creates a fresh game state by:

  1. Creating a new Board instance with randomly placed ships
  2. Clearing the game over message
  3. Rebuilding the visual board with new squares

Testing Strategies

To ensure your implementation works correctly, test these scenarios:

Potential Enhancements

Consider these improvements to the basic implementation:

Common Challenges and Solutions

Challenge: Square Click Management

Each square needs to be clickable only once and should be disabled after being revealed. We handle this by:

Challenge: Game State Tracking

Keeping track of hits and game completion requires careful state management. Our solution: