Skip to content

Latest commit

 

History

8 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 

Repository files navigation

Battleship in C++

A console Battleship game. The player places ships on a grid, the two grids are kept as separate models, and every placement and shooting rule is enforced by the grid classes rather than by the console code.

Written for the Advanced Programming Techniques course. The exercise was about splitting a small game into classes that each own one responsibility, and testing them.

Classes

Board
 ├── OwnGrid        ships, placement rules, incoming shots
 └── OpponentGrid   what the player knows about the enemy grid

OwnGrid       std::vector<Ship>, std::map<int,int> remaining per length,
              std::set<GridPosition> already shot at
Ship          length and the positions it covers
Shot          a target position and its Impact: NONE, HIT, SUNKEN
GridPosition  a column letter and a row number, comparable so it can go in a set
ConsoleView   draws both grids and reads user input

Board is only the data model. It holds the two grids and knows nothing about output. ConsoleView is the only class that uses std::cout and std::cin, so the game logic can be tested without a console.

Points worth explaining

Two grids, not one. OwnGrid knows where the ships really are. OpponentGrid only knows what the player has found out by shooting. Keeping them as separate types makes it impossible to accidentally read information the player should not have, which a single grid with a visibility flag would allow.

GridPosition is ordered on purpose. It defines a comparison so it can be a key in std::set and std::map. OwnGrid keeps a std::set<GridPosition> m_shotAt of every position already fired at, which is what stops the same hit from being counted twice and what makes it possible to decide whether a ship is completely destroyed.

Placement rules live in OwnGrid::placeShip. It checks the grid limits, the overlap with ships already placed, and how many ships of that length are still allowed, using std::map<int,int> m_remainingShips. The caller only gets a bool back, so no rule can be bypassed from outside.

One return value carries the whole result of a shot. takeBlow returns Shot::Impact, which is NONE, HIT or SUNKEN. The caller does not have to ask any follow up question to know what happened.

Tests

src/test.cpp holds a hand written test suite with an assertTrue helper, grouped per class: GridPosition, Ship, OwnGrid, Board, ConsoleView, then the placement and shooting exercises. The groups are switched on with codeSnippet1 to codeSnippet3, and main.cpp switches between testMode and playMode with a #define, so the same project either runs the tests or plays the game.

Files

File Contents
src/GridPosition.* Column letter and row number, parsing, comparison
src/Ship.*, src/Shot.* A ship and its positions, a shot and its impact
src/OwnGrid.* Ship placement rules and incoming shots
src/OpponentGrid.* What is known about the enemy grid
src/Board.* Holds both grids
src/ConsoleView.* Drawing and user input
src/test.cpp Test suite
src/main.cpp Entry point, test mode or play mode

Build

Needs a C++14 compiler.

g++ -std=c++14 src/*.cpp -o battleship

Developed in Eclipse CDT, so the workspace files are not in this repository. The test functions live in test.cpp and are called from main.cpp, so all sources compile together. Switch between the two modes with the testMode and playMode defines at the top of main.cpp.

Context

Advanced Programming Techniques, M.Sc. Electrical Engineering and Information Technology, Hochschule Darmstadt, winter semester 2025/26.

About

Console Battleship in C++: separate own and opponent grid models, placement and shooting rules inside the grid classes, with a per-class test suite

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages