Battleship++
Battleship in C++

Weiwei Hu and I worked on this project as our final project for CIS 190 (C++ Programming). The goal of the final project is to build something in C++ using some of the features covered in class (templates, smart pointers, I/O, etc.).

Preview

The game is played by interacting with stdin/stdout. The player can select an opponent (random AI, thoughtful AI, or another user). The random AI's strategy is to randomly attack a cell, while the thoughtful AI remembers successful attacks and makes better-than-random choices. Playing against another user is achieved using sockets.

Playing Against CPU

Playing Against Another User

Implementation Highlights

While working on this project, we realized that C++ has some nice features that helped us implement the rules of the game. Below is a highlight of those features and how they're applied.


Inheritance and Pure Virtual Functions

There are 5 types of ships in the game, each with a different length. We built an abstract class representing an arbitrary ship, and a few classes that extend ship class and implement virtual methods. This allowed us to abstract over different types of ships, while maintaining the ability to display each one differently.

// ship.hpp
class ship {
  public:
    ship(int, string);  // length, name
    
    // pure virtual to require implementation by derived classes
    virtual void draw() const = 0;  
}

// submarine.cpp
submarine::submarine() : ship(3, "submarine") {}
void submarine::draw() const {
  cout << "s ";
}

// carrier.cpp
carrier::carrier() : ship(5, "carrier") {}
void carrier::draw() const {
  cout << "s ";
}

Shared Pointers

We represent the board as a 2D array of shared pointers to ships. For example, a ship of length 3 placed horizontally at (0, 0) will be represented by 3 shared pointers in board[0][0], board[0][1], and board[0][2]. This makes it easy to check if an attack position contains a ship, since empty positions have a null pointer. When an attack position is a hit, we change the pointer in that position to point to a dead ship, which is displayed differently. The benefit of representing the board as a 2D array of shared pointers is that we don't have to worry about freeing up memory, since memory is deallocated when the last shared pointer gets assigned a pointer to a dead ship.

Sockets

We enable players to play against one another using sockets. One player acts as the host, creating/binding a socket to an address and listening for a connection. The other player acts as the client, creating a socket and connecting to the server's address. When players place their ships on their board, the positions are sent to eachother and cached in memory. The player who gets to play first is the one with the higher randomly generated number. Players generate a random number and send it to each other. If the numbers are the same, they repeat the process until one is higher than the other. The players then take turn making moves. When a move is made, the game updates the state locally and sends the other player the position of the attack. The other player then updates their own state locally.